pdfrb 0.7.11 → 0.7.12

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: 110dd46487b657b7cd703d56a21cd92d6a39a5592a899298e8e0e9c18b292b17
4
- data.tar.gz: bd739eb6fc4238edc56aad66db164b12c66c421a13d8165fe93c140c8e335b0d
3
+ metadata.gz: 26e34e7b8d5b74f8a2f8a89b36b3ea7ae028b0cf8944707aeed0a3ddde8270fc
4
+ data.tar.gz: 9bae4c50eff54052d0cc173559625fb0de3ff782bb567b9ac0bf7f85be951aea
5
5
  SHA512:
6
- metadata.gz: 1b5b55bdb8bf2cd3b4874a7c4d4f85905e7a99e27b683de07043a3dc3f3f9d3b676fc14493ee7209f2f646b593c9604f79fef4dbadbf9ae6dd08d8aa07153c39
7
- data.tar.gz: d13c343ed3efc47b9dc29ad3f828dc101b093926d2516c34cb4ab0b9736312974076772e2fe1c367f1784de842d7ea220c4ab4efcc738db42542d9ef474ac7c9
6
+ metadata.gz: 3c12a6ae1950e81e8bb5fc181dc35aa63fcb6c0671bf0ab9e3becc92b3ebbb28e2210eb0be966ca4cefb6baf97688b1e7ba8115629d08647493680f5f42c6588
7
+ data.tar.gz: 87eb412507356b781a33f96f3d58aa6a14acb5284664091879c23c699e70bd3a2ee4e5f0dbd64c7958361544e1ecc3a23f71b00e2d95b3fe9f2805ab9ad441ed
@@ -57,6 +57,8 @@ module Pdfrb
57
57
  def new_page(style_name = nil)
58
58
  @current_style_name = style_name || next_style_name
59
59
  style_obj = @page_styles[@current_style_name]
60
+ # Reset the frame cursor for the new page.
61
+ reset_frame_cursor(style_obj)
60
62
  page = @document.pages.add
61
63
  page.value[:MediaBox] = [0, 0, style_obj.width, style_obj.height]
62
64
  @current_page = page
@@ -64,6 +66,11 @@ module Pdfrb
64
66
  page
65
67
  end
66
68
 
69
+ def reset_frame_cursor(style_obj)
70
+ style_obj&.frame&.reset!
71
+ end
72
+ private :reset_frame_cursor
73
+
67
74
  def x
68
75
  @cursor_x || 50
69
76
  end
@@ -143,17 +150,29 @@ module Pdfrb
143
150
 
144
151
  style_obj = @page_styles[@current_style_name]
145
152
  frame = style_obj.frame
146
- cursor_y = frame.top
147
153
 
148
154
  @pending_boxes.each do |box|
149
- unless box.fit?(frame.width, cursor_y - frame.bottom)
155
+ available_h = frame.available_height.positive? ? frame.available_height : (frame.top - frame.bottom)
156
+ unless box.fit?(frame.width, available_h)
150
157
  new_page
151
- cursor_y = @page_styles[@current_style_name].frame.top
152
- box.fit?(frame.width, cursor_y - frame.bottom)
158
+ style_obj = @page_styles[@current_style_name]
159
+ frame = style_obj.frame
160
+ available_h = frame.top - frame.bottom
161
+ box.fit?(frame.width, available_h)
153
162
  end
163
+
164
+ position = frame.find_available_area(box.width || frame.width, box.height || available_h)
165
+ if position
166
+ draw_x, draw_y, = position
167
+ else
168
+ draw_x = frame.left
169
+ draw_y = frame.cursor_y - (box.height || 0)
170
+ end
171
+
154
172
  canvas = @current_page.canvas
155
- box.draw(canvas, frame.left, cursor_y)
156
- cursor_y -= box.height
173
+ box.draw(canvas, draw_x, draw_y)
174
+ box_h = box.height || available_h
175
+ frame.remove_area(draw_x, draw_y, box.width || frame.width, box_h)
157
176
  end
158
177
  @pending_boxes = []
159
178
  end
@@ -9,8 +9,14 @@ module Pdfrb
9
9
  # Simple case: the Frame is a plain rectangle. Advanced: the
10
10
  # +shape+ polygon allows non-rectangular regions (L-shaped, with
11
11
  # cutouts around images).
12
+ #
13
+ # Area tracking: +remove_area+ marks a rectangle as used. The
14
+ # Frame maintains a +cursor_y+ that tracks the lowest free
15
+ # vertical position. +find_available_area+ returns the next
16
+ # position at or above the cursor where the requested
17
+ # (width × height) fits without overlapping any removed area.
12
18
  class Frame
13
- attr_reader :left, :bottom, :width, :height, :shape
19
+ attr_reader :left, :bottom, :width, :height, :shape, :cursor_y
14
20
 
15
21
  def initialize(left:, bottom:, width:, height:, shape: nil)
16
22
  @left = left.to_f
@@ -18,6 +24,8 @@ module Pdfrb
18
24
  @width = width.to_f
19
25
  @height = height.to_f
20
26
  @shape = shape || default_shape
27
+ @cursor_y = top
28
+ @removed_areas = []
21
29
  end
22
30
 
23
31
  def right
@@ -28,37 +36,70 @@ module Pdfrb
28
36
  @bottom + @height
29
37
  end
30
38
 
31
- # Locate the next free area of (width × height) within this Frame.
32
- # Returns nil if none fits. After a successful find, callers
33
- # typically call +remove_area+ with the returned rectangle to
34
- # mark it as used.
39
+ # Locate the next free area of (width × height) within this
40
+ # Frame. Scans from the cursor_y downward, checking each
41
+ # candidate y against the removed-areas list. Returns
42
+ # [x, y, w, h] or nil if nothing fits.
35
43
  def find_available_area(width, height)
36
44
  w = width.to_f
37
45
  h = height.to_f
38
46
  return nil if w > @width || h > @height
47
+ return nil if h > available_height
48
+
49
+ y = @cursor_y - h
50
+ while y >= @bottom
51
+ if area_free?(@left, y, w, h)
52
+ @cursor_y = y
53
+ return [@left, y, w, h]
54
+ end
39
55
 
40
- [@left, @bottom + @height - h]
56
+ y -= 1
57
+ end
58
+ nil
41
59
  end
42
60
 
43
61
  # Mark the rectangle at (x, y) of (w × h) as filled. Subsequent
44
- # +find_available_area+ calls avoid this region.
62
+ # +find_available_area+ calls avoid this region. Moves the
63
+ # cursor down when the removed area is at the current cursor.
45
64
  def remove_area(x, y, w, h)
46
65
  return if x.nil? || y.nil?
47
66
 
48
- @removed ||= []
49
- @removed << [x.to_f, y.to_f, w.to_f, h.to_f]
67
+ @removed_areas << [x.to_f, y.to_f, w.to_f, h.to_f]
68
+ # If the removed area touches the cursor, advance it.
69
+ if (y + h) >= @cursor_y - 1
70
+ @cursor_y = [@cursor_y, y].min
71
+ end
50
72
  end
51
73
 
52
74
  def full?
53
- @removed&.any?
75
+ @cursor_y <= @bottom
54
76
  end
55
77
 
56
78
  def empty?
57
- !full?
79
+ @removed_areas.empty?
80
+ end
81
+
82
+ def available_height
83
+ @cursor_y - @bottom
84
+ end
85
+
86
+ # Reset the cursor and removed-areas list. Used by the
87
+ # Composer when starting a new page with the same Frame.
88
+ def reset!
89
+ @cursor_y = top
90
+ @removed_areas = []
58
91
  end
59
92
 
60
93
  private
61
94
 
95
+ def area_free?(x, y, w, h)
96
+ @removed_areas.none? { |rx, ry, rw, rh| rectangles_overlap?(x, y, w, h, rx, ry, rw, rh) }
97
+ end
98
+
99
+ def rectangles_overlap?(x1, y1, w1, h1, x2, y2, w2, h2)
100
+ x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2
101
+ end
102
+
62
103
  def default_shape
63
104
  [[@left, @bottom], [right, @bottom], [right, top], [@left, top]]
64
105
  end
data/lib/pdfrb/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pdfrb
4
- VERSION = "0.7.11"
4
+ VERSION = "0.7.12"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdfrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.11
4
+ version: 0.7.12
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-13 00:00:00.000000000 Z
11
+ date: 2026-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor