hexapdf 0.43.0 → 0.44.0
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 +4 -4
- data/CHANGELOG.md +17 -0
- data/examples/030-pdfa.rb +1 -0
- data/lib/hexapdf/composer.rb +1 -0
- data/lib/hexapdf/document/files.rb +7 -2
- data/lib/hexapdf/document/metadata.rb +12 -1
- data/lib/hexapdf/layout/box.rb +160 -61
- data/lib/hexapdf/layout/box_fitter.rb +1 -0
- data/lib/hexapdf/layout/column_box.rb +22 -24
- data/lib/hexapdf/layout/container_box.rb +2 -2
- data/lib/hexapdf/layout/frame.rb +13 -95
- data/lib/hexapdf/layout/image_box.rb +4 -4
- data/lib/hexapdf/layout/list_box.rb +11 -19
- data/lib/hexapdf/layout/style.rb +5 -1
- data/lib/hexapdf/layout/table_box.rb +48 -55
- data/lib/hexapdf/layout/text_box.rb +27 -42
- data/lib/hexapdf/parser.rb +5 -2
- data/lib/hexapdf/type/file_specification.rb +9 -5
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_files.rb +5 -0
- data/test/hexapdf/document/test_metadata.rb +21 -0
- data/test/hexapdf/layout/test_box.rb +82 -37
- data/test/hexapdf/layout/test_box_fitter.rb +7 -0
- data/test/hexapdf/layout/test_column_box.rb +7 -13
- data/test/hexapdf/layout/test_container_box.rb +1 -1
- data/test/hexapdf/layout/test_frame.rb +0 -48
- data/test/hexapdf/layout/test_image_box.rb +14 -6
- data/test/hexapdf/layout/test_list_box.rb +25 -26
- data/test/hexapdf/layout/test_table_box.rb +39 -53
- data/test/hexapdf/layout/test_text_box.rb +38 -66
- data/test/hexapdf/test_composer.rb +6 -0
- data/test/hexapdf/test_parser.rb +8 -0
- data/test/hexapdf/type/test_file_specification.rb +2 -1
- 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: 3b30b54b90222fbcc5469b23153efc4b15083797f527af6c5b34b3f6e161832d
|
4
|
+
data.tar.gz: 969c67ab85591e3b7ccad17023bd3bc820ea5effa4c01e53d254915ab1626d71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b60934dd6534ccd019953b278fa188b77996634b3e3878332302b9a2acccfd13b493b57432cff2113b7cc7727f028f24301c2d050f9b908c1b43cf66fbdeebfd
|
7
|
+
data.tar.gz: fdd792109306bc7cf0e30bb2da770e58e81e333843e3c785a9a31873a296b7d027121b467b54a80405332735e99bdaf6b0d167c9eaf08dbc04a26922b890bb51
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.44.0 - 2024-06-05
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for specifying the MIME type when embedding files
|
6
|
+
* Support for adding custom XMP metadata
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
|
10
|
+
* **Breaking change**: Refactored the box implementation of the document layout
|
11
|
+
system
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
|
15
|
+
* Parsing of invalid files with garbage bytes at the end
|
16
|
+
|
17
|
+
|
1
18
|
## 0.43.0 - 2024-05-26
|
2
19
|
|
3
20
|
### Added
|
data/examples/030-pdfa.rb
CHANGED
data/lib/hexapdf/composer.rb
CHANGED
@@ -70,6 +70,9 @@ module HexaPDF
|
|
70
70
|
# description::
|
71
71
|
# A description of the file.
|
72
72
|
#
|
73
|
+
# mime_type::
|
74
|
+
# The MIME type that should be set for embedded files (so only used if +embed+ is +true+).
|
75
|
+
#
|
73
76
|
# embed::
|
74
77
|
# When an IO object is given, it is always embedded and this option is ignored.
|
75
78
|
#
|
@@ -77,7 +80,7 @@ module HexaPDF
|
|
77
80
|
# only a reference to it is stored.
|
78
81
|
#
|
79
82
|
# See: HexaPDF::Type::FileSpecification
|
80
|
-
def add(file_or_io, name: nil, description: nil, embed: true)
|
83
|
+
def add(file_or_io, name: nil, description: nil, mime_type: nil, embed: true)
|
81
84
|
name ||= File.basename(file_or_io) if file_or_io.kind_of?(String)
|
82
85
|
if name.nil?
|
83
86
|
raise ArgumentError, "The name argument is mandatory when given an IO object"
|
@@ -86,7 +89,9 @@ module HexaPDF
|
|
86
89
|
spec = @document.add({Type: :Filespec})
|
87
90
|
spec.path = name
|
88
91
|
spec[:Desc] = description if description
|
89
|
-
|
92
|
+
if embed || !file_or_io.kind_of?(String)
|
93
|
+
spec.embed(file_or_io, name: name, mime_type: mime_type, register: true)
|
94
|
+
end
|
90
95
|
spec
|
91
96
|
end
|
92
97
|
|
@@ -161,6 +161,7 @@ module HexaPDF
|
|
161
161
|
@properties = PREDEFINED_PROPERTIES.transform_values(&:dup)
|
162
162
|
@default_language = document.catalog[:Lang] || 'x-default'
|
163
163
|
@metadata = Hash.new {|h, k| h[k] = {} }
|
164
|
+
@custom_metadata = []
|
164
165
|
write_info_dict(true)
|
165
166
|
write_metadata_stream(true)
|
166
167
|
@document.register_listener(:complete_objects, &method(:write_metadata))
|
@@ -248,6 +249,16 @@ module HexaPDF
|
|
248
249
|
end
|
249
250
|
end
|
250
251
|
|
252
|
+
# Adds the given +data+ string as custom metadata to the XMP document.
|
253
|
+
#
|
254
|
+
# The +data+ string must contain a fully valid 'rdf:Description' element.
|
255
|
+
#
|
256
|
+
# Using this method allows adding metadata like PDF/A schema definitions for which there is no
|
257
|
+
# direct support by HexaPDF.
|
258
|
+
def custom_metadata(data)
|
259
|
+
@custom_metadata << data
|
260
|
+
end
|
261
|
+
|
251
262
|
# :call-seq:
|
252
263
|
# metadata.delete
|
253
264
|
# metadata.delete(ns_prefix)
|
@@ -469,7 +480,7 @@ module HexaPDF
|
|
469
480
|
<?xpacket begin="\u{FEFF}" id="#{SecureRandom.uuid.tr('-', '')}"?>
|
470
481
|
<x:xmpmeta xmlns:x="adobe:ns:meta/">
|
471
482
|
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
472
|
-
#{data}
|
483
|
+
#{data}#{@custom_metadata.empty? ? '' : "\n#{@custom_metadata.join("\n")}"}
|
473
484
|
</rdf:RDF>
|
474
485
|
</x:xmpmeta>
|
475
486
|
<?xpacket end="r"?>
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -61,9 +61,9 @@ module HexaPDF
|
|
61
61
|
# instantiated from the common convenience method HexaPDF::Document::Layout#box. To use this
|
62
62
|
# facility subclasses need to be registered with the configuration option 'layout.boxes.map'.
|
63
63
|
#
|
64
|
-
# The methods #supports_position_flow?, #empty?, #
|
65
|
-
#
|
66
|
-
#
|
64
|
+
# The methods #supports_position_flow?, #empty?, #fit_content, #split_content, and #draw_content
|
65
|
+
# need to be customized according to the subclass's use case (also see the documentation of the
|
66
|
+
# methods besides the information below):
|
67
67
|
#
|
68
68
|
# #supports_position_flow?::
|
69
69
|
# If the subclass supports the value :flow of the 'position' style property, this method
|
@@ -72,25 +72,22 @@ module HexaPDF
|
|
72
72
|
# #empty?::
|
73
73
|
# This method should return +true+ if the subclass won't draw anything when #draw is called.
|
74
74
|
#
|
75
|
-
# #
|
76
|
-
# This method
|
77
|
-
#
|
78
|
-
# #split.
|
75
|
+
# #fit_content::
|
76
|
+
# This method determines whether the box fits into the available region and should set the
|
77
|
+
# status of #fit_result appropriately.
|
79
78
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
79
|
+
# It is called from the #fit method which should not be overridden in most cases. The
|
80
|
+
# default implementations of both methods provide code common to all use-cases and delegates
|
81
|
+
# the specifics to the subclass-specific #fit_content method.
|
83
82
|
#
|
84
|
-
# #
|
85
|
-
# This method
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# box.
|
83
|
+
# #split_content::
|
84
|
+
# This method is called from #split which handles the common cases based on the status of
|
85
|
+
# the #fit_result. It needs to handle the case when only some part of the box fits. The
|
86
|
+
# method #create_split_box should be used for getting a basic cloned box.
|
89
87
|
#
|
90
|
-
# #
|
91
|
-
# This method draws the content and
|
92
|
-
# drawing the border and background. So
|
93
|
-
# drawing commands should be implemented in the #draw_content method.
|
88
|
+
# #draw_content::
|
89
|
+
# This method draws the box specific content and is called from #draw which already handles
|
90
|
+
# things like drawing the border and background. So #draw should usually not be overridden.
|
94
91
|
#
|
95
92
|
# This base class provides various private helper methods for use in the above methods:
|
96
93
|
#
|
@@ -118,6 +115,104 @@ module HexaPDF
|
|
118
115
|
|
119
116
|
include HexaPDF::Utils
|
120
117
|
|
118
|
+
# Stores the result of fitting a box in a frame.
|
119
|
+
class FitResult
|
120
|
+
|
121
|
+
# The box that was fitted into the frame.
|
122
|
+
attr_accessor :box
|
123
|
+
|
124
|
+
# The frame into which the box was fitted.
|
125
|
+
attr_accessor :frame
|
126
|
+
|
127
|
+
# The horizontal position where the box will be drawn.
|
128
|
+
attr_accessor :x
|
129
|
+
|
130
|
+
# The vertical position where the box will be drawn.
|
131
|
+
attr_accessor :y
|
132
|
+
|
133
|
+
# The rectangle (a Geom2D::Rectangle object) that will be removed from the frame when
|
134
|
+
# drawing the box.
|
135
|
+
attr_accessor :mask
|
136
|
+
|
137
|
+
# The status result of fitting the box in the frame.
|
138
|
+
#
|
139
|
+
# Allowed values are:
|
140
|
+
#
|
141
|
+
# +:failure+:: (default) Indicates fitting the box has failed.
|
142
|
+
# +:success+:: Indicates that the box was completely fitted.
|
143
|
+
# +:overflow+:: Indicates that only a part of the box was fitted.
|
144
|
+
attr_reader :status
|
145
|
+
|
146
|
+
# Initializes the result object for the given box and, optionally, frame.
|
147
|
+
def initialize(box, frame: nil)
|
148
|
+
@box = box
|
149
|
+
reset(frame)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Resets the result object.
|
153
|
+
def reset(frame)
|
154
|
+
@frame = frame
|
155
|
+
@x = @y = @mask = nil
|
156
|
+
@status = :failure
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
# Sets the result status to success.
|
161
|
+
def success!
|
162
|
+
@status = :success
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns +true+ if fitting was successful.
|
166
|
+
def success?
|
167
|
+
@status == :success
|
168
|
+
end
|
169
|
+
|
170
|
+
# Sets the result status to overflow.
|
171
|
+
def overflow!
|
172
|
+
@status = :overflow
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns +true+ if only parts of the box were fitted.
|
176
|
+
def overflow?
|
177
|
+
@status == :overflow
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns +true+ if fitting was a failure.
|
181
|
+
def failure?
|
182
|
+
@status == :failure
|
183
|
+
end
|
184
|
+
|
185
|
+
# Draws the #box onto the canvas at (#x + *dx*, #y + *dy*).
|
186
|
+
#
|
187
|
+
# The relative offset (dx, dy) is useful when rendering results that were accumulated and
|
188
|
+
# then need to be moved because the container holding them changes its position.
|
189
|
+
#
|
190
|
+
# The configuration option "debug" can be used to add visual debug output with respect to
|
191
|
+
# box placement.
|
192
|
+
def draw(canvas, dx: 0, dy: 0)
|
193
|
+
return if box.height == 0 || box.width == 0
|
194
|
+
doc = canvas.context.document
|
195
|
+
if doc.config['debug']
|
196
|
+
name = (frame.parent_boxes + [box]).map do |box|
|
197
|
+
box.class.to_s.sub(/.*::/, '')
|
198
|
+
end.join('-') << "##{box.object_id}"
|
199
|
+
name = "#{name} (#{(x + dx).to_i},#{(y + dy).to_i}-#{mask.width.to_i}x#{mask.height.to_i})"
|
200
|
+
ocg = doc.optional_content.ocg(name)
|
201
|
+
canvas.optional_content(ocg) do
|
202
|
+
canvas.translate(dx, dy) do
|
203
|
+
canvas.fill_color("green").stroke_color("darkgreen").
|
204
|
+
opacity(fill_alpha: 0.1, stroke_alpha: 0.2).
|
205
|
+
draw(:geom2d, object: mask, path_only: true).fill_stroke
|
206
|
+
end
|
207
|
+
end
|
208
|
+
page = "Page #{canvas.context.index + 1}" rescue "XObject"
|
209
|
+
doc.optional_content.default_configuration.add_ocg_to_ui(ocg, path: ['Debug', page])
|
210
|
+
end
|
211
|
+
box.draw(canvas, x + dx, y + dy)
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
121
216
|
# Creates a new Box object, using the provided block as drawing block (see ::new).
|
122
217
|
#
|
123
218
|
# If +content_box+ is +true+, the width and height are taken to mean the content width and
|
@@ -143,10 +238,15 @@ module HexaPDF
|
|
143
238
|
# The height of the box, including padding and/or borders.
|
144
239
|
attr_reader :height
|
145
240
|
|
241
|
+
# The FitResult instance holding the result after a call to #fit.
|
242
|
+
attr_reader :fit_result
|
243
|
+
|
146
244
|
# The style to be applied.
|
147
245
|
#
|
148
246
|
# Only the following properties are used:
|
149
247
|
#
|
248
|
+
# * Style#position
|
249
|
+
# * Style#overflow
|
150
250
|
# * Style#background_color
|
151
251
|
# * Style#background_alpha
|
152
252
|
# * Style#padding
|
@@ -190,7 +290,7 @@ module HexaPDF
|
|
190
290
|
@style = Style.create(style)
|
191
291
|
@properties = properties || {}
|
192
292
|
@draw_block = block
|
193
|
-
@
|
293
|
+
@fit_result = FitResult.new(self)
|
194
294
|
@split_box = false
|
195
295
|
end
|
196
296
|
|
@@ -217,7 +317,7 @@ module HexaPDF
|
|
217
317
|
height < 0 ? 0 : height
|
218
318
|
end
|
219
319
|
|
220
|
-
# Fits the box into the *frame* and returns
|
320
|
+
# Fits the box into the *frame* and returns the #fit_result.
|
221
321
|
#
|
222
322
|
# The arguments +available_width+ and +available_height+ are the width and height of the
|
223
323
|
# current region of the frame, adjusted for this box. The frame itself is provided as third
|
@@ -225,70 +325,68 @@ module HexaPDF
|
|
225
325
|
#
|
226
326
|
# The default implementation uses the given available width and height for the box width and
|
227
327
|
# height if they were initially set to 0. Otherwise the intially specified dimensions are
|
228
|
-
# used.
|
328
|
+
# used. The method returns early if the thus configured box already doesn't fit. Otherwise,
|
329
|
+
# the #fit_content method is called which allows sub-classes to fit their content.
|
229
330
|
#
|
230
331
|
# The following variables are set that may later be used during splitting or drawing:
|
231
332
|
#
|
232
333
|
# * (@fit_x, @fit_y): The lower-left corner of the content box where fitting was done. Can be
|
233
|
-
# used to adjust the drawing position in #
|
234
|
-
# * @fit_successful: +true+ if fitting was successful.
|
334
|
+
# used to adjust the drawing position in #draw_content if necessary.
|
235
335
|
def fit(available_width, available_height, frame)
|
336
|
+
@fit_result.reset(frame)
|
236
337
|
@width = (@initial_width > 0 ? @initial_width : available_width)
|
237
338
|
@height = (@initial_height > 0 ? @initial_height : available_height)
|
238
|
-
@
|
239
|
-
|
240
|
-
return unless @fit_successful
|
339
|
+
return @fit_result if style.position != :flow && (float_compare(@width, available_width) > 0 ||
|
340
|
+
float_compare(@height, available_height) > 0)
|
241
341
|
|
242
|
-
|
342
|
+
fit_content(available_width, available_height, frame)
|
243
343
|
|
244
344
|
@fit_x = frame.x + reserved_width_left
|
245
345
|
@fit_y = frame.y - @height + reserved_height_bottom
|
246
346
|
|
247
|
-
@
|
347
|
+
@fit_result
|
248
348
|
end
|
249
349
|
|
250
350
|
# Tries to split the box into two, the first of which needs to fit into the current region of
|
251
|
-
# the frame, and returns the parts as array.
|
351
|
+
# the frame, and returns the parts as array. The method #fit needs to be called before this
|
352
|
+
# method to correctly set-up the #fit_result.
|
252
353
|
#
|
253
354
|
# If the first item in the result array is not +nil+, it needs to be this box and it means
|
254
355
|
# that even when #fit fails, a part of the box may still fit. Note that #fit should not be
|
255
|
-
# called before #draw on the first box since it is already fitted. If not even a part of
|
256
|
-
# box fits into the current region, +nil+ should be returned as the first array element.
|
356
|
+
# called again before #draw on the first box since it is already fitted. If not even a part of
|
357
|
+
# this box fits into the current region, +nil+ should be returned as the first array element.
|
257
358
|
#
|
258
359
|
# Possible return values:
|
259
360
|
#
|
260
|
-
# [self]:: The box fully fits into the current region.
|
361
|
+
# [self, nil]:: The box fully fits into the current region.
|
261
362
|
# [nil, self]:: The box can't be split or no part of the box fits into the current region.
|
262
363
|
# [self, new_box]:: A part of the box fits and a new box is returned for the rest.
|
263
364
|
#
|
264
|
-
# This default implementation provides the basic functionality based on the
|
265
|
-
# should be sufficient for most subclasses; only #split_content needs to be
|
266
|
-
# necessary.
|
267
|
-
def split
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
float_compare(@height, available_height) > 0)) ||
|
273
|
-
content_height == 0 || content_width == 0
|
274
|
-
[nil, self]
|
275
|
-
else
|
276
|
-
split_content(available_width, available_height, frame)
|
365
|
+
# This default implementation provides the basic functionality based on the status of the
|
366
|
+
# #fit_result that should be sufficient for most subclasses; only #split_content needs to be
|
367
|
+
# implemented if necessary.
|
368
|
+
def split
|
369
|
+
case @fit_result.status
|
370
|
+
when :overflow then (@initial_height > 0 ? [self, nil] : split_content)
|
371
|
+
when :failure then [nil, self]
|
372
|
+
when :success then [self, nil]
|
277
373
|
end
|
278
374
|
end
|
279
375
|
|
280
376
|
# Draws the content of the box onto the canvas at the position (x, y).
|
281
377
|
#
|
282
|
-
#
|
283
|
-
#
|
378
|
+
# When +@draw_block+ is used (the block specified when creating the box), the coordinate
|
379
|
+
# system is translated so that the origin is at the bottom left corner of the **content box**.
|
284
380
|
#
|
285
|
-
#
|
286
|
-
#
|
287
|
-
#
|
288
|
-
# operations when the block does nothing.
|
289
|
-
#
|
290
|
-
# Alternatively, if a #draw_content method is defined, this method is called.
|
381
|
+
# Subclasses should not rely on the +@draw_block+ but implement the #draw_content method. The
|
382
|
+
# coordinates passed to it are also modified to represent the bottom left corner of the
|
383
|
+
# content box but the coordinate system is not translated.
|
291
384
|
def draw(canvas, x, y)
|
385
|
+
if @fit_result.overflow? && @initial_height > 0 && style.overflow == :error
|
386
|
+
raise HexaPDF::Error, "Box with limited height doesn't completely fit and " \
|
387
|
+
"style property overflow is set to :error"
|
388
|
+
end
|
389
|
+
|
292
390
|
if (oc = properties['optional_content'])
|
293
391
|
canvas.optional_content(oc)
|
294
392
|
end
|
@@ -381,12 +479,13 @@ module HexaPDF
|
|
381
479
|
|
382
480
|
# Fits the content of the box and returns whether fitting was successful.
|
383
481
|
#
|
384
|
-
# This is just a stub implementation that
|
385
|
-
# provide the box
|
482
|
+
# This is just a stub implementation that sets the #fit_result status to success if the
|
483
|
+
# content rectangle is not degenerate. Subclasses should override it to provide the box
|
484
|
+
# specific behaviour.
|
386
485
|
#
|
387
486
|
# See #fit for details.
|
388
487
|
def fit_content(_available_width, _available_height, _frame)
|
389
|
-
|
488
|
+
fit_result.success! if content_width > 0 && content_height > 0
|
390
489
|
end
|
391
490
|
|
392
491
|
# Splits the content of the box.
|
@@ -395,12 +494,12 @@ module HexaPDF
|
|
395
494
|
# the content when it didn't fit.
|
396
495
|
#
|
397
496
|
# Subclasses that support splitting content need to provide an appropriate implementation and
|
398
|
-
# use #create_split_box to create a cloned box to supply as the second argument.
|
399
|
-
def split_content
|
497
|
+
# use #create_split_box to create a cloned box to supply as the second return argument.
|
498
|
+
def split_content
|
400
499
|
[nil, self]
|
401
500
|
end
|
402
501
|
|
403
|
-
# Draws the content of the box at position [x, y] which is the bottom
|
502
|
+
# Draws the content of the box at position [x, y] which is the bottom left corner of the
|
404
503
|
# content box.
|
405
504
|
#
|
406
505
|
# This implementation uses the drawing block provided on initialization, if set, to draw the
|
@@ -422,7 +521,7 @@ module HexaPDF
|
|
422
521
|
box = clone
|
423
522
|
box.instance_variable_set(:@width, @initial_width)
|
424
523
|
box.instance_variable_set(:@height, @initial_height)
|
425
|
-
box.instance_variable_set(:@
|
524
|
+
box.instance_variable_set(:@fit_result, FitResult.new(box))
|
426
525
|
box.instance_variable_set(:@split_box, split_box_value)
|
427
526
|
box
|
428
527
|
end
|
@@ -111,6 +111,7 @@ module HexaPDF
|
|
111
111
|
@content_heights[@frame_index] = [@content_heights[@frame_index],
|
112
112
|
@initial_frame_y[@frame_index] - result.mask.y].max
|
113
113
|
@fit_results << result
|
114
|
+
break unless box
|
114
115
|
elsif !current_frame.find_next_region
|
115
116
|
@frame_index += 1
|
116
117
|
end
|
@@ -138,31 +138,29 @@ module HexaPDF
|
|
138
138
|
super && (!@box_fitter || @box_fitter.fit_results.empty?)
|
139
139
|
end
|
140
140
|
|
141
|
+
private
|
142
|
+
|
141
143
|
# Fits the column box into the current region of the frame.
|
142
144
|
#
|
143
|
-
|
144
|
-
# arbitrary (sets of) polygons since the +frame+s shape is taken into account.
|
145
|
-
def fit(available_width, available_height, frame)
|
146
|
-
return false if @initial_height > available_height || @initial_width > available_width
|
147
|
-
|
145
|
+
def fit_content(available_width, available_height, frame)
|
148
146
|
initial_fit_successful = (@equal_height && @columns.size > 1 ? nil : false)
|
149
147
|
tries = 0
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
148
|
+
width = if style.position == :flow
|
149
|
+
(@initial_width > 0 ? @initial_width : frame.width) - reserved_width
|
150
|
+
else
|
151
|
+
(@initial_width > 0 ? @initial_width : available_width) - reserved_width
|
152
|
+
end
|
155
153
|
height = if style.position == :flow
|
156
|
-
(@initial_height > 0 ? @initial_height : frame.
|
154
|
+
(@initial_height > 0 ? @initial_height : frame.y - frame.bottom) - reserved_height
|
157
155
|
else
|
158
156
|
(@initial_height > 0 ? @initial_height : available_height) - reserved_height
|
159
157
|
end
|
160
158
|
|
161
|
-
columns = calculate_columns(
|
162
|
-
return
|
159
|
+
columns = calculate_columns(width)
|
160
|
+
return if columns.empty?
|
163
161
|
|
164
162
|
left = (style.position == :flow ? frame.left : frame.x) + reserved_width_left
|
165
|
-
top =
|
163
|
+
top = frame.y - reserved_height_top
|
166
164
|
successful_height = height
|
167
165
|
unsuccessful_height = 0
|
168
166
|
|
@@ -206,16 +204,16 @@ module HexaPDF
|
|
206
204
|
tries += 1
|
207
205
|
end
|
208
206
|
|
209
|
-
|
210
|
-
|
211
|
-
@draw_pos_x = frame.x + reserved_width_left
|
212
|
-
@draw_pos_y = frame.y - @height + reserved_height_bottom
|
207
|
+
update_content_width { columns[-1].sum }
|
208
|
+
update_content_height { @box_fitter.content_heights.max }
|
213
209
|
|
214
|
-
@box_fitter.success?
|
210
|
+
if @box_fitter.success?
|
211
|
+
fit_result.success!
|
212
|
+
elsif !@box_fitter.fit_results.empty?
|
213
|
+
fit_result.overflow!
|
214
|
+
end
|
215
215
|
end
|
216
216
|
|
217
|
-
private
|
218
|
-
|
219
217
|
# Calculates the x-coordinates and widths of all columns based on the given total available
|
220
218
|
# width.
|
221
219
|
#
|
@@ -241,7 +239,7 @@ module HexaPDF
|
|
241
239
|
end
|
242
240
|
|
243
241
|
# Splits the content of the column box. This method is called from Box#split.
|
244
|
-
def split_content
|
242
|
+
def split_content
|
245
243
|
box = create_split_box
|
246
244
|
box.instance_variable_set(:@children, @box_fitter.remaining_boxes)
|
247
245
|
[self, box]
|
@@ -249,8 +247,8 @@ module HexaPDF
|
|
249
247
|
|
250
248
|
# Draws the child boxes onto the canvas at position [x, y].
|
251
249
|
def draw_content(canvas, x, y)
|
252
|
-
if style.position != :flow && (x != @
|
253
|
-
canvas.translate(x - @
|
250
|
+
if style.position != :flow && (x != @fit_x || y != @fit_y)
|
251
|
+
canvas.translate(x - @fit_x, y - @fit_y) do
|
254
252
|
@box_fitter.fit_results.each {|result| result.draw(canvas) }
|
255
253
|
end
|
256
254
|
else
|
@@ -143,11 +143,11 @@ module HexaPDF
|
|
143
143
|
children.empty? ? 0 : result.mask.x + result.mask.width - my_frame.left
|
144
144
|
end
|
145
145
|
update_content_height { @box_fitter.content_heights.max }
|
146
|
-
|
146
|
+
fit_result.success!
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
-
# Draws the
|
150
|
+
# Draws the children onto the canvas at position [x, y].
|
151
151
|
def draw_content(canvas, x, y)
|
152
152
|
dx = x - @fit_x
|
153
153
|
dy = y - @fit_y
|