hexapdf 0.38.0 → 0.39.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/examples/030-pdfa.rb +1 -1
- data/lib/hexapdf/layout/column_box.rb +2 -2
- data/lib/hexapdf/layout/container_box.rb +3 -2
- data/lib/hexapdf/layout/frame.rb +40 -6
- data/lib/hexapdf/layout/inline_box.rb +11 -4
- data/lib/hexapdf/layout/list_box.rb +3 -3
- data/lib/hexapdf/layout/style.rb +20 -0
- data/lib/hexapdf/layout/table_box.rb +5 -3
- data/lib/hexapdf/layout/text_box.rb +11 -2
- data/lib/hexapdf/layout/text_layouter.rb +12 -3
- data/lib/hexapdf/type/acro_form/field.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/layout/test_frame.rb +31 -2
- data/test/hexapdf/layout/test_inline_box.rb +8 -1
- data/test/hexapdf/layout/test_style.rb +1 -0
- data/test/hexapdf/layout/test_table_box.rb +26 -2
- data/test/hexapdf/layout/test_text_box.rb +22 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07f7fd9468ec9c36993f4b1f2189704d65c1c73f7fa1ea30d44f6cb0a67fa92c
|
4
|
+
data.tar.gz: 1cad88a48910fd65a9dae7a90dcfe6ac34ef5ba9f69172a18bd52052fb10b99c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eeee46c22616abbca812ef441100e33534384db831aadf04df9b9382920aa58105be04046a76a9da76951c1d0bc39126e4ef33c43fa3401077c34604ff414acc
|
7
|
+
data.tar.gz: 1cd42546ff96980f6fbfeaae450a7f8657350ce3b23692b508b87718028e68fe50acd9ca8b744bc755f9b79029ecd41f5cb1cedc046209336594c3fba5a1807b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
## 0.39.1 - 2024-03-20
|
2
|
+
|
3
|
+
### Fixed
|
4
|
+
|
5
|
+
* [HexaPDF::Layout::TableBox] to correctly split tables when a row span with a
|
6
|
+
too high cell is involved
|
7
|
+
|
8
|
+
|
9
|
+
## 0.39.0 - 2024-03-18
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
* Hierarchical box information to the document layout engine
|
14
|
+
* Style property 'text_overflow' for controlling how overflowing text should be
|
15
|
+
handled
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
* [HexaPDF::Layout::Frame::FitResult#draw] to provide better optional content
|
20
|
+
group names
|
21
|
+
|
22
|
+
### Fixed
|
23
|
+
|
24
|
+
* [HexaPDF::Layout::TextBox] to correctly respect a set height
|
25
|
+
|
26
|
+
|
1
27
|
## 0.38.0 - 2024-03-10
|
2
28
|
|
3
29
|
### Added
|
data/examples/030-pdfa.rb
CHANGED
@@ -179,8 +179,8 @@ module HexaPDF
|
|
179
179
|
[column_left, column_bottom + height])
|
180
180
|
shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, rect, :intersection)
|
181
181
|
end
|
182
|
-
column_frame =
|
183
|
-
|
182
|
+
column_frame = frame.child_frame(column_left, column_bottom, column_width, height,
|
183
|
+
shape: shape, box: self)
|
184
184
|
@box_fitter << column_frame
|
185
185
|
end
|
186
186
|
|
@@ -131,8 +131,9 @@ module HexaPDF
|
|
131
131
|
|
132
132
|
# Fits the children into the container.
|
133
133
|
def fit_content(_available_width, _available_height, frame)
|
134
|
-
my_frame =
|
135
|
-
|
134
|
+
my_frame = frame.child_frame(frame.x + reserved_width_left,
|
135
|
+
frame.y - @height + reserved_height_bottom,
|
136
|
+
content_width, content_height, box: self)
|
136
137
|
@box_fitter = BoxFitter.new([my_frame])
|
137
138
|
children.each {|box| @box_fitter.fit(box) }
|
138
139
|
|
data/lib/hexapdf/layout/frame.rb
CHANGED
@@ -91,6 +91,9 @@ module HexaPDF
|
|
91
91
|
# Stores the result of fitting a box in a Frame.
|
92
92
|
class FitResult
|
93
93
|
|
94
|
+
# The frame into which the box was fitted.
|
95
|
+
attr_accessor :frame
|
96
|
+
|
94
97
|
# The box that was fitted into the frame.
|
95
98
|
attr_accessor :box
|
96
99
|
|
@@ -110,8 +113,9 @@ module HexaPDF
|
|
110
113
|
# drawing the box.
|
111
114
|
attr_accessor :mask
|
112
115
|
|
113
|
-
# Initialize the result object for the given box.
|
114
|
-
def initialize(box)
|
116
|
+
# Initialize the result object for the given frame and box.
|
117
|
+
def initialize(frame, box)
|
118
|
+
@frame = frame
|
115
119
|
@box = box
|
116
120
|
@available_width = 0
|
117
121
|
@available_height = 0
|
@@ -138,7 +142,10 @@ module HexaPDF
|
|
138
142
|
def draw(canvas, dx: 0, dy: 0)
|
139
143
|
doc = canvas.context.document
|
140
144
|
if doc.config['debug']
|
141
|
-
name =
|
145
|
+
name = (frame.parent_boxes + [box]).map do |box|
|
146
|
+
box.class.to_s.sub(/.*::/, '')
|
147
|
+
end.join('-') << "##{box.object_id}"
|
148
|
+
name = "#{name} (#{(x + dx).to_i},#{(y + dy).to_i}-#{mask.width.to_i}x#{mask.height.to_i})"
|
142
149
|
ocg = doc.optional_content.ocg(name)
|
143
150
|
canvas.optional_content(ocg) do
|
144
151
|
canvas.translate(dx, dy) do
|
@@ -147,7 +154,8 @@ module HexaPDF
|
|
147
154
|
draw(:geom2d, object: mask, path_only: true).fill_stroke
|
148
155
|
end
|
149
156
|
end
|
150
|
-
|
157
|
+
page = "Page #{canvas.context.index + 1}" rescue "XObject"
|
158
|
+
doc.optional_content.default_configuration.add_ocg_to_ui(ocg, path: ['Debug', page])
|
151
159
|
end
|
152
160
|
box.draw(canvas, x + dx, y + dy)
|
153
161
|
end
|
@@ -195,14 +203,21 @@ module HexaPDF
|
|
195
203
|
# should be used.
|
196
204
|
attr_reader :context
|
197
205
|
|
206
|
+
# An array of box objects representing the parent boxes.
|
207
|
+
#
|
208
|
+
# The immediate parent is the last array entry, the top most parent the first one. All boxes
|
209
|
+
# that are fitted into this frame have to be child boxes of the immediate parent box.
|
210
|
+
attr_reader :parent_boxes
|
211
|
+
|
198
212
|
# Creates a new Frame object for the given rectangular area.
|
199
|
-
def initialize(left, bottom, width, height, shape: nil, context: nil)
|
213
|
+
def initialize(left, bottom, width, height, shape: nil, context: nil, parent_boxes: [])
|
200
214
|
@left = left
|
201
215
|
@bottom = bottom
|
202
216
|
@width = width
|
203
217
|
@height = height
|
204
218
|
@shape = shape || create_rectangle(left, bottom, left + width, bottom + height)
|
205
219
|
@context = context
|
220
|
+
@parent_boxes = parent_boxes.freeze
|
206
221
|
|
207
222
|
@x = left
|
208
223
|
@y = bottom + height
|
@@ -213,6 +228,25 @@ module HexaPDF
|
|
213
228
|
@region_selection = :max_height
|
214
229
|
end
|
215
230
|
|
231
|
+
# Creates a new Frame object based on this one.
|
232
|
+
#
|
233
|
+
# If the +init_args+ arguments are provided, a new Frame is created using the constructor. The
|
234
|
+
# optional +shape+ argument is then also passed to the constructor.
|
235
|
+
#
|
236
|
+
# Otherwise, this frame is duplicated. This kind of invocation is only useful if the +box+
|
237
|
+
# argument is provided (because otherwise there would be no difference to this frame).
|
238
|
+
#
|
239
|
+
# The +box+ argument can be used to add the appropriate parent box to the list of
|
240
|
+
# #parent_boxes for the newly created frame.
|
241
|
+
def child_frame(*init_args, shape: nil, box: nil)
|
242
|
+
parent_boxes = (box ? @parent_boxes.dup << box : @parent_boxes)
|
243
|
+
if init_args.empty?
|
244
|
+
dup.tap {|result| result.instance_variable_set(:@parent_boxes, parent_boxes) }
|
245
|
+
else
|
246
|
+
self.class.new(*init_args, shape: shape, context: @context, parent_boxes: parent_boxes)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
216
250
|
# Returns the HexaPDF::Document instance (through #context) that is associated with this Frame
|
217
251
|
# object or +nil+ if no context object has been set.
|
218
252
|
def document
|
@@ -227,7 +261,7 @@ module HexaPDF
|
|
227
261
|
#
|
228
262
|
# Use the FitResult#success? method to determine whether fitting was successful.
|
229
263
|
def fit(box)
|
230
|
-
fit_result = FitResult.new(box)
|
264
|
+
fit_result = FitResult.new(self, box)
|
231
265
|
return fit_result if full?
|
232
266
|
|
233
267
|
margin = box.style.margin if box.style.margin?
|
@@ -126,10 +126,17 @@ module HexaPDF
|
|
126
126
|
height
|
127
127
|
end
|
128
128
|
|
129
|
-
# Fits the wrapped box
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
# Fits the wrapped box.
|
130
|
+
#
|
131
|
+
# If the +frame+ argument is +nil+, a custom frame is created. Otherwise the given +frame+ is
|
132
|
+
# used for the fitting operation.
|
133
|
+
def fit_wrapped_box(frame)
|
134
|
+
frame = if frame
|
135
|
+
frame.child_frame(0, 0, box.width, box.height == 0 ? 100_000 : box.height)
|
136
|
+
else
|
137
|
+
Frame.new(0, 0, box.width, box.height == 0 ? 100_000 : box.height)
|
138
|
+
end
|
139
|
+
@fit_result = frame.fit(box)
|
133
140
|
if !@fit_result.success?
|
134
141
|
raise HexaPDF::Error, "Box for inline use could not be fit"
|
135
142
|
elsif box.height > 99_000
|
@@ -227,12 +227,12 @@ module HexaPDF
|
|
227
227
|
remove_indent_from_frame_shape(shape) unless shape.polygons.empty?
|
228
228
|
end
|
229
229
|
|
230
|
-
item_frame =
|
231
|
-
|
230
|
+
item_frame = frame.child_frame(item_frame_left, top - height, item_frame_width, height,
|
231
|
+
shape: shape, box: self)
|
232
232
|
|
233
233
|
if index != 0 || !split_box? || @split_box == :show_first_marker
|
234
234
|
box = item_marker_box(frame.document, index)
|
235
|
-
marker_frame =
|
235
|
+
marker_frame = frame.child_frame(0, 0, content_indentation, height, box: self)
|
236
236
|
break unless box.fit(content_indentation, height, marker_frame)
|
237
237
|
item_result.marker = box
|
238
238
|
item_result.marker_pos_x = item_frame.x - content_indentation
|
data/lib/hexapdf/layout/style.rb
CHANGED
@@ -1084,6 +1084,25 @@ module HexaPDF
|
|
1084
1084
|
# 'Centered',
|
1085
1085
|
# {text: "\u{00a0}", fill_horizontal: 1, overlays: overlays}])
|
1086
1086
|
|
1087
|
+
##
|
1088
|
+
# :method: text_overflow
|
1089
|
+
# :call-seq:
|
1090
|
+
# text_overflow(mode = nil)
|
1091
|
+
#
|
1092
|
+
# Specifies how text overflowing a box with a given initial height should be handled:
|
1093
|
+
#
|
1094
|
+
# Possible values:
|
1095
|
+
#
|
1096
|
+
# :error:: An error is raised (default).
|
1097
|
+
# :truncate:: Truncates the overflowing text.
|
1098
|
+
#
|
1099
|
+
# Examples:
|
1100
|
+
#
|
1101
|
+
# #>pdf-composer100
|
1102
|
+
# composer.text("This is some longer text that does appear in two lines.")
|
1103
|
+
# composer.text("This is some longer text that does not appear in two lines.",
|
1104
|
+
# height: 15, text_overflow: :truncate)
|
1105
|
+
|
1087
1106
|
##
|
1088
1107
|
# :method: background_color
|
1089
1108
|
# :call-seq:
|
@@ -1435,6 +1454,7 @@ module HexaPDF
|
|
1435
1454
|
extra_args: ", extra_arg = nil"}],
|
1436
1455
|
[:last_line_gap, false, {valid_values: [true, false]}],
|
1437
1456
|
[:fill_horizontal, nil],
|
1457
|
+
[:text_overflow, :error],
|
1438
1458
|
[:background_color, nil],
|
1439
1459
|
[:background_alpha, 1],
|
1440
1460
|
[:padding, "Quad.new(0)", {setter: "Quad.new(value)"}],
|
@@ -218,7 +218,7 @@ module HexaPDF
|
|
218
218
|
height = available_height - reserved_height
|
219
219
|
return false if width <= 0 || height <= 0
|
220
220
|
|
221
|
-
frame =
|
221
|
+
frame = frame.child_frame(0, 0, width, height, box: self)
|
222
222
|
case children
|
223
223
|
when Box
|
224
224
|
fit_result = frame.fit(children)
|
@@ -417,7 +417,8 @@ module HexaPDF
|
|
417
417
|
break
|
418
418
|
end
|
419
419
|
end
|
420
|
-
|
420
|
+
|
421
|
+
[height - available_height, last_fitted_row_index < start_row ? -1 : last_fitted_row_index]
|
421
422
|
end
|
422
423
|
|
423
424
|
# Draws the rows from +start_row+ to +end_row+ on the given +canvas+, with the top-left
|
@@ -607,6 +608,7 @@ module HexaPDF
|
|
607
608
|
columns = calculate_column_widths(width)
|
608
609
|
return false if columns.empty?
|
609
610
|
|
611
|
+
frame = frame.child_frame(box: self)
|
610
612
|
@special_cells_fit_not_successful = false
|
611
613
|
[@header_cells, @footer_cells].each do |special_cells|
|
612
614
|
next unless special_cells
|
@@ -646,7 +648,7 @@ module HexaPDF
|
|
646
648
|
end
|
647
649
|
end
|
648
650
|
|
649
|
-
# Splits the content of the
|
651
|
+
# Splits the content of the table box. This method is called from Box#split.
|
650
652
|
def split_content(_available_width, _available_height, _frame)
|
651
653
|
if @special_cells_fit_not_successful || @last_fitted_row_index < 0
|
652
654
|
[nil, self]
|
@@ -79,6 +79,7 @@ module HexaPDF
|
|
79
79
|
return false if (@initial_width > 0 && @initial_width > available_width) ||
|
80
80
|
(@initial_height > 0 && @initial_height > available_height)
|
81
81
|
|
82
|
+
frame = frame.child_frame(box: self)
|
82
83
|
@width = @height = 0
|
83
84
|
@result = if style.position == :flow
|
84
85
|
@tl.fit(@items, frame.width_specification, frame.shape.bbox.height,
|
@@ -104,7 +105,8 @@ module HexaPDF
|
|
104
105
|
@height += style.line_spacing.gap(@result.lines.last, @result.lines.last)
|
105
106
|
end
|
106
107
|
|
107
|
-
@result.status == :success
|
108
|
+
@result.status == :success ||
|
109
|
+
(@result.status == :height && @initial_height > 0 && style.text_overflow == :truncate)
|
108
110
|
end
|
109
111
|
|
110
112
|
# Splits the text box into two boxes if necessary and possible.
|
@@ -132,7 +134,14 @@ module HexaPDF
|
|
132
134
|
|
133
135
|
# Draws the text into the box.
|
134
136
|
def draw_content(canvas, x, y)
|
135
|
-
return unless @result
|
137
|
+
return unless @result
|
138
|
+
|
139
|
+
if @result.status == :height && @initial_height > 0 && style.text_overflow == :error
|
140
|
+
raise HexaPDF::Error, "Text doesn't fit into box with limited height and " \
|
141
|
+
"style property text_overflow is set to :error"
|
142
|
+
end
|
143
|
+
|
144
|
+
return if @result.lines.empty?
|
136
145
|
@result.draw(canvas, x, y + content_height)
|
137
146
|
end
|
138
147
|
|
@@ -307,10 +307,15 @@ module HexaPDF
|
|
307
307
|
class SimpleLineWrapping
|
308
308
|
|
309
309
|
# :call-seq:
|
310
|
-
# SimpleLineWrapping.call(items, width_block) {|line, item| block } -> rest
|
310
|
+
# SimpleLineWrapping.call(items, width_block, frame = nil) {|line, item| block } -> rest
|
311
311
|
#
|
312
312
|
# Arranges the items into lines.
|
313
313
|
#
|
314
|
+
# The optional +frame+ argument needs to be a Frame object that is used when fitting inline
|
315
|
+
# boxes. If not provided, a custom Frame object is used. However, if the items contain
|
316
|
+
# inline boxes that need to access a frame's context object, it is mandatory to provide an
|
317
|
+
# appropriate Frame object.
|
318
|
+
#
|
314
319
|
# The +width_block+ argument has to be a callable object that returns the width of the line:
|
315
320
|
#
|
316
321
|
# * If the line width doesn't depend on the height or the vertical position of the line
|
@@ -340,7 +345,7 @@ module HexaPDF
|
|
340
345
|
# current start of the line index should be stored for later use.
|
341
346
|
#
|
342
347
|
# After the algorithm is finished, it returns the unused items.
|
343
|
-
def self.call(items, width_block, frame, &block)
|
348
|
+
def self.call(items, width_block, frame = nil, &block)
|
344
349
|
obj = new(items, width_block, frame)
|
345
350
|
if width_block.arity == 1
|
346
351
|
obj.variable_width_wrapping(&block)
|
@@ -507,7 +512,7 @@ module HexaPDF
|
|
507
512
|
#
|
508
513
|
# Returns +true+ if the item could be added and +false+ otherwise.
|
509
514
|
def add_box_item(item)
|
510
|
-
item.fit_wrapped_box(@frame
|
515
|
+
item.fit_wrapped_box(@frame) if item.kind_of?(InlineBox)
|
511
516
|
return false unless @width + item.width <= @available_width
|
512
517
|
@line_items.concat(@glue_items).push(item)
|
513
518
|
@width += item.width
|
@@ -718,6 +723,10 @@ module HexaPDF
|
|
718
723
|
# Specifies whether style.text_indent should be applied to the first line. This should be
|
719
724
|
# set to +false+ if the items start with a continuation of a paragraph instead of starting
|
720
725
|
# a new paragraph (e.g. after a page break).
|
726
|
+
#
|
727
|
+
# +frame+::
|
728
|
+
# If used with the document layout functionality, this should be the frame into which the
|
729
|
+
# text is laid out.
|
721
730
|
def fit(items, width, height, apply_first_text_indent: true, frame: nil)
|
722
731
|
unless items.empty? || items[0].respond_to?(:type)
|
723
732
|
items = style.text_segmentation_algorithm.call(items)
|
@@ -284,7 +284,7 @@ module HexaPDF
|
|
284
284
|
end
|
285
285
|
|
286
286
|
# Creates a new widget annotation for this form field (must be a terminal field!) on the
|
287
|
-
# given +page+, adding the +values+ to the created widget annotation
|
287
|
+
# given +page+, adding the +values+ to the created widget annotation object.
|
288
288
|
#
|
289
289
|
# If +allow_embedded+ is +false+, embedding the first widget in the field itself is not
|
290
290
|
# allowed.
|
data/lib/hexapdf/version.rb
CHANGED
@@ -10,7 +10,8 @@ describe HexaPDF::Layout::Frame::FitResult do
|
|
10
10
|
doc = HexaPDF::Document.new(config: {'debug' => true})
|
11
11
|
canvas = doc.pages.add.canvas
|
12
12
|
box = HexaPDF::Layout::Box.create(width: 20, height: 20) {}
|
13
|
-
|
13
|
+
frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
|
14
|
+
result = HexaPDF::Layout::Frame::FitResult.new(frame, box)
|
14
15
|
result.mask = Geom2D::Rectangle(0, 0, 20, 20)
|
15
16
|
result.x = result.y = 0
|
16
17
|
result.draw(canvas, dx: 10, dy: 15)
|
@@ -30,7 +31,8 @@ describe HexaPDF::Layout::Frame::FitResult do
|
|
30
31
|
Q
|
31
32
|
CONTENTS
|
32
33
|
ocg = doc.optional_content.ocgs.first
|
33
|
-
assert_equal([['Debug', ocg]], doc.optional_content.default_configuration[:Order])
|
34
|
+
assert_equal([['Debug', ['Page 1', ocg]]], doc.optional_content.default_configuration[:Order])
|
35
|
+
assert_match(/10,15-20x20/, ocg.name)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
@@ -61,6 +63,11 @@ describe HexaPDF::Layout::Frame do
|
|
61
63
|
assert_equal(150, @frame.available_height)
|
62
64
|
end
|
63
65
|
|
66
|
+
it "allows access to the frame's parent boxes" do
|
67
|
+
frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150, parent_boxes: [:a])
|
68
|
+
assert_equal([:a], frame.parent_boxes)
|
69
|
+
end
|
70
|
+
|
64
71
|
it "allows setting the shape of the frame on initialization" do
|
65
72
|
shape = Geom2D::Polygon([50, 10], [55, 100], [105, 100], [105, 10])
|
66
73
|
frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150, shape: shape)
|
@@ -71,6 +78,27 @@ describe HexaPDF::Layout::Frame do
|
|
71
78
|
assert_equal(90, frame.available_height)
|
72
79
|
end
|
73
80
|
|
81
|
+
describe "child_frame" do
|
82
|
+
before do
|
83
|
+
@frame = HexaPDF::Layout::Frame.new(10, 10, 100, 100, parent_boxes: [:a])
|
84
|
+
end
|
85
|
+
|
86
|
+
it "duplicates the frame setting the parent boxes appropriately" do
|
87
|
+
assert_same(@frame.parent_boxes, @frame.child_frame.parent_boxes)
|
88
|
+
frame = @frame.child_frame(box: :b)
|
89
|
+
assert_equal([:a, :b], frame.parent_boxes)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "creates a new frame, optionally adding a parent box" do
|
93
|
+
shape = Geom2D::Rectangle(0, 0, 20, 20)
|
94
|
+
frame = @frame.child_frame(0, 0, 20, 20, shape: shape)
|
95
|
+
assert_same(@frame.parent_boxes, frame.parent_boxes)
|
96
|
+
assert_equal(shape, frame.shape)
|
97
|
+
frame = @frame.child_frame(0, 0, 20, 20, box: :b)
|
98
|
+
assert_equal([:a, :b], frame.parent_boxes)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
74
102
|
it "returns an appropriate width specification object" do
|
75
103
|
ws = @frame.width_specification(10)
|
76
104
|
assert_kind_of(HexaPDF::Layout::WidthFromPolygon, ws)
|
@@ -97,6 +125,7 @@ describe HexaPDF::Layout::Frame do
|
|
97
125
|
@canvas.expect(:translate, nil, pos)
|
98
126
|
fit_result = @frame.fit(@box)
|
99
127
|
refute_nil(fit_result)
|
128
|
+
assert_same(@frame, fit_result.frame)
|
100
129
|
@frame.draw(@canvas, fit_result)
|
101
130
|
assert_equal(mask, fit_result.mask.bbox.to_a)
|
102
131
|
if @frame.shape.respond_to?(:polygons)
|
@@ -31,7 +31,14 @@ describe HexaPDF::Layout::InlineBox do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
describe "fit_wrapped_box" do
|
34
|
-
it "automatically fits the provided box into
|
34
|
+
it "automatically fits the provided box into the given frame" do
|
35
|
+
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", width: 20))
|
36
|
+
ibox.fit_wrapped_box(HexaPDF::Layout::Frame.new(0, 0, 50, 50))
|
37
|
+
assert_equal(20, ibox.width)
|
38
|
+
assert_equal(45, ibox.height)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "automatically fits the provided box into a custom frame" do
|
35
42
|
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", width: 20))
|
36
43
|
ibox.fit_wrapped_box(nil)
|
37
44
|
assert_equal(20, ibox.width)
|
@@ -783,6 +783,7 @@ describe HexaPDF::Layout::Style do
|
|
783
783
|
refute(@style.superscript)
|
784
784
|
refute(@style.last_line_gap)
|
785
785
|
refute(@style.fill_horizontal)
|
786
|
+
assert_equal(:error, @style.text_overflow)
|
786
787
|
assert_kind_of(HexaPDF::Layout::Style::Layers, @style.underlays)
|
787
788
|
assert_kind_of(HexaPDF::Layout::Style::Layers, @style.overlays)
|
788
789
|
assert_equal(:default, @style.position)
|
@@ -362,8 +362,8 @@ describe HexaPDF::Layout::TableBox do
|
|
362
362
|
@doc = HexaPDF::Document.new
|
363
363
|
@page = @doc.pages.add
|
364
364
|
@frame = HexaPDF::Layout::Frame.new(0, 0, 160, 100, context: @page)
|
365
|
-
draw_block = lambda {|canvas, _box| canvas.move_to(0, 0).end_path }
|
366
|
-
@fixed_size_boxes = 15.times.map { HexaPDF::Layout::Box.new(width: 20, height: 10,
|
365
|
+
@draw_block = lambda {|canvas, _box| canvas.move_to(0, 0).end_path }
|
366
|
+
@fixed_size_boxes = 15.times.map { HexaPDF::Layout::Box.new(width: 20, height: 10, &@draw_block) }
|
367
367
|
end
|
368
368
|
|
369
369
|
def create_box(**kwargs)
|
@@ -561,6 +561,30 @@ describe HexaPDF::Layout::TableBox do
|
|
561
561
|
assert_equal(-1, box_b.last_fitted_row_index)
|
562
562
|
end
|
563
563
|
|
564
|
+
it "splits the table correctly when row spans and a too-high cell are involved" do
|
565
|
+
cells = [[@fixed_size_boxes[0], @fixed_size_boxes[1]],
|
566
|
+
[{row_span: 2, content: @fixed_size_boxes[2]}, @fixed_size_boxes[3]],
|
567
|
+
[HexaPDF::Layout::Box.new(width: 20, height: 150, &@draw_block)]]
|
568
|
+
box = create_box(cells: cells)
|
569
|
+
|
570
|
+
refute(box.fit(100, 100, @frame))
|
571
|
+
box_a, box_b = box.split(100, 100, @frame)
|
572
|
+
assert_same(box_a, box)
|
573
|
+
assert(box_b.split_box?)
|
574
|
+
assert_equal(0, box_a.start_row_index)
|
575
|
+
assert_equal(0, box_a.last_fitted_row_index)
|
576
|
+
assert_equal(1, box_b.start_row_index)
|
577
|
+
assert_equal(-1, box_b.last_fitted_row_index)
|
578
|
+
|
579
|
+
refute(box_b.fit(100, 100, @frame))
|
580
|
+
box_c, box_d = box_b.split(100, 100, @frame)
|
581
|
+
assert_nil(box_c)
|
582
|
+
assert_same(box_d, box_b)
|
583
|
+
assert(box_d.split_box?)
|
584
|
+
assert_equal(1, box_d.start_row_index)
|
585
|
+
assert_equal(-1, box_d.last_fitted_row_index)
|
586
|
+
end
|
587
|
+
|
564
588
|
it "splits the table if the header or footer rows don't fit" do
|
565
589
|
cells_creator = lambda {|_| [@fixed_size_boxes[10, 2]] }
|
566
590
|
[{header: cells_creator}, {footer: cells_creator}].each do |args|
|
@@ -50,8 +50,7 @@ describe HexaPDF::Layout::TextBox do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it "fits into the frame's outline" do
|
53
|
-
|
54
|
-
box = create_box([inline_box] * 20, style: {position: :flow})
|
53
|
+
box = create_box([@inline_box] * 20, style: {position: :flow})
|
55
54
|
assert(box.fit(100, 100, @frame))
|
56
55
|
assert_equal(100, box.width)
|
57
56
|
assert_equal(20, box.height)
|
@@ -80,6 +79,16 @@ describe HexaPDF::Layout::TextBox do
|
|
80
79
|
end
|
81
80
|
end
|
82
81
|
|
82
|
+
it "respects the style property text_overflow when fitting too much text" do
|
83
|
+
box = create_box([@inline_box] * 20, height: 15)
|
84
|
+
refute(box.fit(100, 100, @frame))
|
85
|
+
box.style.text_overflow = :truncate
|
86
|
+
assert(box.fit(100, 100, @frame))
|
87
|
+
|
88
|
+
box = create_box([@inline_box] * 20, style: {text_overflow: :truncate})
|
89
|
+
refute(box.fit(100, 15, @frame))
|
90
|
+
end
|
91
|
+
|
83
92
|
it "can't fit the text box if the set width is bigger than the available width" do
|
84
93
|
box = create_box([@inline_box], width: 101)
|
85
94
|
refute(box.fit(100, 100, @frame))
|
@@ -154,13 +163,16 @@ describe HexaPDF::Layout::TextBox do
|
|
154
163
|
end
|
155
164
|
|
156
165
|
describe "draw" do
|
166
|
+
before do
|
167
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
168
|
+
end
|
169
|
+
|
157
170
|
it "draws the layed out inline items onto the canvas" do
|
158
171
|
inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10,
|
159
172
|
border: {width: 1})
|
160
173
|
box = create_box([inline_box], width: 100, height: 30, style: {padding: [10, 5]})
|
161
|
-
box.fit(100, 100,
|
174
|
+
box.fit(100, 100, @frame)
|
162
175
|
|
163
|
-
@canvas = HexaPDF::Document.new.pages.add.canvas
|
164
176
|
box.draw(@canvas, 0, 0)
|
165
177
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
166
178
|
[:restore_graphics_state],
|
@@ -179,11 +191,16 @@ describe HexaPDF::Layout::TextBox do
|
|
179
191
|
end
|
180
192
|
|
181
193
|
it "draws nothing onto the canvas if the box is empty" do
|
182
|
-
@canvas = HexaPDF::Document.new.pages.add.canvas
|
183
194
|
box = create_box([])
|
184
195
|
box.draw(@canvas, 5, 5)
|
185
196
|
assert_operators(@canvas.contents, [])
|
186
197
|
end
|
198
|
+
|
199
|
+
it "raises an error if there is too much content for a set height with text_overlow=:error" do
|
200
|
+
box = create_box([@inline_box] * 20, height: 15)
|
201
|
+
box.fit(100, 100, @frame)
|
202
|
+
assert_raises(HexaPDF::Error) { box.draw(@canvas, 0, 0) }
|
203
|
+
end
|
187
204
|
end
|
188
205
|
|
189
206
|
it "is empty if there is a result without any text lines" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.39.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|