hexapdf 0.34.1 → 0.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -0
- data/examples/009-text_layouter_alignment.rb +7 -7
- data/examples/010-text_layouter_inline_boxes.rb +1 -1
- data/examples/011-text_layouter_line_wrapping.rb +2 -4
- data/examples/013-text_layouter_shapes.rb +9 -11
- data/examples/014-text_in_polygon.rb +2 -2
- data/examples/016-frame_automatic_box_placement.rb +6 -7
- data/examples/017-frame_text_flow.rb +2 -2
- data/examples/018-composer.rb +5 -6
- data/examples/020-column_box.rb +2 -2
- data/examples/021-list_box.rb +1 -1
- data/examples/027-composer_optional_content.rb +5 -5
- data/examples/028-frame_mask_mode.rb +23 -0
- data/examples/029-composer_fallback_fonts.rb +22 -0
- data/lib/hexapdf/cli/info.rb +1 -0
- data/lib/hexapdf/cli/inspect.rb +55 -2
- data/lib/hexapdf/composer.rb +2 -2
- data/lib/hexapdf/configuration.rb +61 -1
- data/lib/hexapdf/content/canvas.rb +63 -0
- data/lib/hexapdf/content/canvas_composer.rb +142 -0
- data/lib/hexapdf/content.rb +1 -0
- data/lib/hexapdf/dictionary.rb +14 -3
- data/lib/hexapdf/document/layout.rb +35 -13
- data/lib/hexapdf/encryption/standard_security_handler.rb +15 -0
- data/lib/hexapdf/error.rb +2 -1
- data/lib/hexapdf/font/invalid_glyph.rb +22 -6
- data/lib/hexapdf/font/true_type_wrapper.rb +48 -20
- data/lib/hexapdf/font/type1_wrapper.rb +48 -24
- data/lib/hexapdf/layout/box.rb +11 -8
- data/lib/hexapdf/layout/column_box.rb +5 -3
- data/lib/hexapdf/layout/frame.rb +77 -39
- data/lib/hexapdf/layout/image_box.rb +3 -3
- data/lib/hexapdf/layout/list_box.rb +20 -19
- data/lib/hexapdf/layout/style.rb +173 -68
- data/lib/hexapdf/layout/table_box.rb +3 -3
- data/lib/hexapdf/layout/text_box.rb +5 -5
- data/lib/hexapdf/layout/text_fragment.rb +50 -0
- data/lib/hexapdf/layout/text_layouter.rb +7 -6
- data/lib/hexapdf/object.rb +5 -2
- data/lib/hexapdf/pdf_array.rb +5 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +16 -11
- data/lib/hexapdf/utils/sorted_tree_node.rb +0 -10
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas.rb +37 -0
- data/test/hexapdf/content/test_canvas_composer.rb +112 -0
- data/test/hexapdf/document/test_layout.rb +40 -12
- data/test/hexapdf/encryption/test_standard_security_handler.rb +43 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +13 -1
- data/test/hexapdf/font/test_true_type_wrapper.rb +15 -2
- data/test/hexapdf/font/test_type1_wrapper.rb +21 -2
- data/test/hexapdf/layout/test_column_box.rb +14 -0
- data/test/hexapdf/layout/test_frame.rb +181 -95
- data/test/hexapdf/layout/test_list_box.rb +7 -7
- data/test/hexapdf/layout/test_style.rb +14 -10
- data/test/hexapdf/layout/test_table_box.rb +3 -3
- data/test/hexapdf/layout/test_text_box.rb +2 -2
- data/test/hexapdf/layout/test_text_fragment.rb +37 -0
- data/test/hexapdf/layout/test_text_layouter.rb +10 -10
- data/test/hexapdf/test_configuration.rb +49 -0
- data/test/hexapdf/test_dictionary.rb +1 -1
- data/test/hexapdf/test_object.rb +13 -12
- data/test/hexapdf/test_pdf_array.rb +9 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +41 -13
- data/test/hexapdf/utils/test_sorted_tree_node.rb +1 -1
- metadata +7 -3
@@ -48,8 +48,8 @@ module HexaPDF
|
|
48
48
|
# Represents a single glyph of the wrapped font.
|
49
49
|
class Glyph
|
50
50
|
|
51
|
-
# The associated
|
52
|
-
attr_reader :
|
51
|
+
# The associated Type1Wrapper object.
|
52
|
+
attr_reader :font_wrapper
|
53
53
|
|
54
54
|
# The name of the glyph.
|
55
55
|
attr_reader :name
|
@@ -59,35 +59,35 @@ module HexaPDF
|
|
59
59
|
attr_reader :str
|
60
60
|
|
61
61
|
# Creates a new Glyph object.
|
62
|
-
def initialize(
|
63
|
-
@
|
62
|
+
def initialize(font_wrapper, name, str)
|
63
|
+
@font_wrapper = font_wrapper
|
64
64
|
@name = name
|
65
65
|
@str = str
|
66
66
|
end
|
67
67
|
|
68
68
|
# Returns the glyph's minimum x coordinate.
|
69
69
|
def x_min
|
70
|
-
@
|
70
|
+
@font_wrapper.wrapped_font.metrics.character_metrics[name].bbox[0]
|
71
71
|
end
|
72
72
|
|
73
73
|
# Returns the glyph's maximum x coordinate.
|
74
74
|
def x_max
|
75
|
-
@
|
75
|
+
@font_wrapper.wrapped_font.metrics.character_metrics[name].bbox[2]
|
76
76
|
end
|
77
77
|
|
78
78
|
# Returns the glyph's minimum y coordinate.
|
79
79
|
def y_min
|
80
|
-
@
|
80
|
+
@font_wrapper.wrapped_font.metrics.character_metrics[name].bbox[1]
|
81
81
|
end
|
82
82
|
|
83
83
|
# Returns the glyph's maximum y coordinate.
|
84
84
|
def y_max
|
85
|
-
@
|
85
|
+
@font_wrapper.wrapped_font.metrics.character_metrics[name].bbox[3]
|
86
86
|
end
|
87
87
|
|
88
88
|
# Returns the width of the glyph.
|
89
89
|
def width
|
90
|
-
@width ||= @
|
90
|
+
@width ||= @font_wrapper.wrapped_font.width(name)
|
91
91
|
end
|
92
92
|
|
93
93
|
# Returns +true+ if the word spacing parameter needs to be applied for the glyph.
|
@@ -95,9 +95,15 @@ module HexaPDF
|
|
95
95
|
@name == :space
|
96
96
|
end
|
97
97
|
|
98
|
+
# Returns +true+ since this is a valid glyph.
|
99
|
+
def valid?
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
98
103
|
#:nodoc:
|
99
104
|
def inspect
|
100
|
-
"#<#{self.class.name} font=#{@
|
105
|
+
"#<#{self.class.name} font=#{@font_wrapper.wrapped_font.full_name.inspect} " \
|
106
|
+
"id=#{name.inspect} #{str.inspect}>"
|
101
107
|
end
|
102
108
|
|
103
109
|
end
|
@@ -154,13 +160,23 @@ module HexaPDF
|
|
154
160
|
1
|
155
161
|
end
|
156
162
|
|
163
|
+
# Returns +true+ if the font contains bold glyphs.
|
164
|
+
def bold?
|
165
|
+
@wrapped_font.weight_class > 500
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns +true+ if the font contains glyphs with an incline (italic or slant).
|
169
|
+
def italic?
|
170
|
+
@wrapped_font.italic_angle.to_i != 0
|
171
|
+
end
|
172
|
+
|
157
173
|
# Returns a Glyph object for the given glyph name.
|
158
174
|
def glyph(name)
|
159
175
|
@name_to_glyph[name] ||=
|
160
176
|
begin
|
161
177
|
str = Encoding::GlyphList.name_to_unicode(name, **@zapf_dingbats_opt)
|
162
178
|
if @wrapped_font.metrics.character_metrics.key?(name)
|
163
|
-
Glyph.new(
|
179
|
+
Glyph.new(self, name, str)
|
164
180
|
else
|
165
181
|
@pdf_object.document.config['font.on_missing_glyph'].call(str, self)
|
166
182
|
end
|
@@ -178,27 +194,35 @@ module HexaPDF
|
|
178
194
|
raise HexaPDF::Error, "Glyph named #{name.inspect} not found in " \
|
179
195
|
"font '#{@wrapped_font.full_name}'"
|
180
196
|
end
|
181
|
-
Glyph.new(
|
197
|
+
Glyph.new(self, name, string)
|
182
198
|
end
|
183
199
|
|
184
200
|
# Returns an array of glyph objects representing the characters in the UTF-8 encoded string.
|
185
201
|
#
|
202
|
+
# See #decode_codepoint for details.
|
203
|
+
def decode_utf8(str)
|
204
|
+
str.codepoints.map! {|c| @codepoint_to_glyph[c] || decode_codepoint(c) }
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns a glyph object for the given Unicode codepoint.
|
208
|
+
#
|
186
209
|
# If a Unicode codepoint is not available as glyph object, it is tried to map the codepoint
|
187
210
|
# using the font's internal encoding. This is useful, for example, for the ZapfDingbats font
|
188
211
|
# to use ASCII characters for accessing the glyphs.
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
name =
|
199
|
-
glyph(name)
|
212
|
+
#
|
213
|
+
# The configuration option 'font.on_missing_glyph' is invoked if no glyph for a given
|
214
|
+
# codepoint is available.
|
215
|
+
def decode_codepoint(codepoint)
|
216
|
+
@codepoint_to_glyph[codepoint] ||=
|
217
|
+
begin
|
218
|
+
name = Encoding::GlyphList.unicode_to_name(+'' << codepoint, **@zapf_dingbats_opt)
|
219
|
+
if @wrapped_font.metrics.character_set == 'Special' &&
|
220
|
+
(name == :'.notdef' || !@wrapped_font.metrics.character_metrics.key?(name))
|
221
|
+
name = @encoding.name(codepoint)
|
200
222
|
end
|
201
|
-
|
223
|
+
name = +"u" << codepoint.to_s(16).rjust(6, '0') if name == :'.notdef'
|
224
|
+
glyph(name)
|
225
|
+
end
|
202
226
|
end
|
203
227
|
|
204
228
|
# Encodes the glyph and returns the code string.
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -71,7 +71,7 @@ module HexaPDF
|
|
71
71
|
# If the subclass supports the value :flow of the 'position' style property, this method
|
72
72
|
# needs to be overridden to return +true+.
|
73
73
|
#
|
74
|
-
# #split:: This method splits the content so that the
|
74
|
+
# #split:: This method splits the content so that the current region is used as good as
|
75
75
|
# possible. The default implementation should be fine for most use-cases, so only
|
76
76
|
# #split_content needs to be implemented. The method #create_split_box should be used
|
77
77
|
# for getting a basic cloned box.
|
@@ -186,26 +186,29 @@ module HexaPDF
|
|
186
186
|
|
187
187
|
# Fits the box into the Frame and returns +true+ if fitting was successful.
|
188
188
|
#
|
189
|
-
# The
|
190
|
-
#
|
189
|
+
# The arguments +available_width+ and +available_height+ are the width and height of the
|
190
|
+
# current region of the frame. The frame itself is provided as third argument.
|
191
|
+
#
|
192
|
+
# The default implementation uses the available width and height for the box width and height
|
193
|
+
# if they were initially set to 0. Otherwise the specified dimensions are used.
|
191
194
|
def fit(available_width, available_height, _frame)
|
192
195
|
@width = (@initial_width > 0 ? @initial_width : available_width)
|
193
196
|
@height = (@initial_height > 0 ? @initial_height : available_height)
|
194
197
|
@fit_successful = (@width <= available_width && @height <= available_height)
|
195
198
|
end
|
196
199
|
|
197
|
-
# Tries to split the box into two, the first of which needs to fit into the
|
198
|
-
# and returns the parts as array.
|
200
|
+
# Tries to split the box into two, the first of which needs to fit into the current region of
|
201
|
+
# the frame, and returns the parts as array.
|
199
202
|
#
|
200
203
|
# If the first item in the result array is not +nil+, it needs to be this box and it means
|
201
204
|
# that even when #fit fails, a part of the box may still fit. Note that #fit should not be
|
202
205
|
# called before #draw on the first box since it is already fitted. If not even a part of this
|
203
|
-
# box fits into the
|
206
|
+
# box fits into the current region, +nil+ should be returned as the first array element.
|
204
207
|
#
|
205
208
|
# Possible return values:
|
206
209
|
#
|
207
|
-
# [self]:: The box fully fits into the
|
208
|
-
# [nil, self]:: The box can't be split or no part of the box fits into the
|
210
|
+
# [self]:: The box fully fits into the current region.
|
211
|
+
# [nil, self]:: The box can't be split or no part of the box fits into the current region.
|
209
212
|
# [self, new_box]:: A part of the box fits and a new box is returned for the rest.
|
210
213
|
#
|
211
214
|
# This default implementation provides the basic functionality based on the #fit result that
|
@@ -138,12 +138,14 @@ module HexaPDF
|
|
138
138
|
super && (!@box_fitter || @box_fitter.fit_results.empty?)
|
139
139
|
end
|
140
140
|
|
141
|
-
# Fits the column box into the
|
141
|
+
# Fits the column box into the current region of the frame.
|
142
142
|
#
|
143
143
|
# If the style property 'position' is set to :flow, the columns might not be rectangles but
|
144
144
|
# arbitrary (sets of) polygons since the +frame+s shape is taken into account.
|
145
145
|
def fit(available_width, available_height, frame)
|
146
|
-
|
146
|
+
return false if @initial_height > available_height || @initial_width > available_width
|
147
|
+
|
148
|
+
initial_fit_successful = (@equal_height && @columns.size > 1 ? nil : false)
|
147
149
|
tries = 0
|
148
150
|
@width = if style.position == :flow
|
149
151
|
(@initial_width > 0 ? @initial_width : frame.width) - reserved_width
|
@@ -205,7 +207,7 @@ module HexaPDF
|
|
205
207
|
end
|
206
208
|
|
207
209
|
@width = columns[-1].sum + reserved_width
|
208
|
-
@height = @box_fitter.content_heights.max + reserved_height
|
210
|
+
@height = (@initial_height > 0 ? @initial_height : @box_fitter.content_heights.max + reserved_height)
|
209
211
|
@draw_pos_x = frame.x + reserved_width_left
|
210
212
|
@draw_pos_y = frame.y - @height + reserved_height_bottom
|
211
213
|
|
data/lib/hexapdf/layout/frame.rb
CHANGED
@@ -53,8 +53,8 @@ module HexaPDF
|
|
53
53
|
# available space. If fitting is successful, the box can be drawn using #draw.
|
54
54
|
#
|
55
55
|
# The method #fit is also called for absolutely positioned boxes but since these boxes are not
|
56
|
-
# subject to the normal constraints, the available
|
57
|
-
# the frame to the right and top of the bottom-left corner of the box.
|
56
|
+
# subject to the normal constraints, the provided available width and height are the width and
|
57
|
+
# height inside the frame to the right and top of the bottom-left corner of the box.
|
58
58
|
#
|
59
59
|
# * If the box didn't fit, call #find_next_region to determine the next region for placing the
|
60
60
|
# box. If a new region was found, start over with #fit. Otherwise the frame has no more space
|
@@ -71,9 +71,9 @@ module HexaPDF
|
|
71
71
|
#
|
72
72
|
# == Used Box Properties
|
73
73
|
#
|
74
|
-
# The style properties
|
75
|
-
# fitting, splitting or drawing a box. Note that the margin is ignored if a box's
|
76
|
-
# with the frame's original boundary.
|
74
|
+
# The style properties 'position', 'align', 'valign', 'margin' and 'mask_mode' are taken into
|
75
|
+
# account when fitting, splitting or drawing a box. Note that the margin is ignored if a box's
|
76
|
+
# side coincides with the frame's original boundary.
|
77
77
|
#
|
78
78
|
# == Frame Shape
|
79
79
|
#
|
@@ -178,12 +178,12 @@ module HexaPDF
|
|
178
178
|
# Also see the note in the #x documentation for further information.
|
179
179
|
attr_reader :y
|
180
180
|
|
181
|
-
# The available width for placing a box.
|
181
|
+
# The available width of the current region for placing a box.
|
182
182
|
#
|
183
183
|
# Also see the note in the #x documentation for further information.
|
184
184
|
attr_reader :available_width
|
185
185
|
|
186
|
-
# The available height for placing a box.
|
186
|
+
# The available height of the current region for placing a box.
|
187
187
|
#
|
188
188
|
# Also see the note in the #x documentation for further information.
|
189
189
|
attr_reader :available_height
|
@@ -219,19 +219,24 @@ module HexaPDF
|
|
219
219
|
# Fits the given box into the current region of available space and returns a FitResult
|
220
220
|
# object.
|
221
221
|
#
|
222
|
+
# Fitting a box takes the style properties 'position', 'align', 'valign', 'margin', and
|
223
|
+
# 'mask_mode' into account.
|
224
|
+
#
|
222
225
|
# Use the FitResult#success? method to determine whether fitting was successful.
|
223
226
|
def fit(box)
|
224
227
|
fit_result = FitResult.new(box)
|
225
228
|
return fit_result if full?
|
226
229
|
|
230
|
+
margin = box.style.margin if box.style.margin?
|
231
|
+
|
227
232
|
position = if box.style.position != :flow || box.supports_position_flow?
|
228
233
|
box.style.position
|
229
234
|
else
|
230
235
|
:default
|
231
236
|
end
|
232
237
|
|
233
|
-
if position
|
234
|
-
x, y = box.style.
|
238
|
+
if position.kind_of?(Array)
|
239
|
+
x, y = box.style.position
|
235
240
|
|
236
241
|
aw = width - x
|
237
242
|
ah = height - y
|
@@ -240,23 +245,15 @@ module HexaPDF
|
|
240
245
|
|
241
246
|
x += left
|
242
247
|
y += bottom
|
243
|
-
rectangle = if box.style.margin?
|
244
|
-
margin = box.style.margin
|
245
|
-
create_rectangle(x - margin.left, y - margin.bottom,
|
246
|
-
x + box.width + margin.right, y + box.height + margin.top)
|
247
|
-
else
|
248
|
-
create_rectangle(x, y, x + box.width, y + box.height)
|
249
|
-
end
|
250
248
|
else
|
251
249
|
aw = available_width
|
252
250
|
ah = available_height
|
253
251
|
|
254
|
-
margin_top = margin_right = margin_left = 0
|
255
|
-
if
|
256
|
-
margin = box.style.margin
|
252
|
+
margin_top = margin_right = margin_left = margin_bottom = 0
|
253
|
+
if margin
|
257
254
|
aw -= margin_right = margin.right unless float_equal(@x + aw, @left + @width)
|
258
255
|
aw -= margin_left = margin.left unless float_equal(@x, @left)
|
259
|
-
ah -= margin.bottom unless float_equal(@y - ah, @bottom)
|
256
|
+
ah -= margin_bottom = margin.bottom unless float_equal(@y - ah, @bottom)
|
260
257
|
ah -= margin_top = margin.top unless float_equal(@y, @bottom + @height)
|
261
258
|
end
|
262
259
|
|
@@ -266,14 +263,9 @@ module HexaPDF
|
|
266
263
|
height = box.height
|
267
264
|
|
268
265
|
case position
|
269
|
-
when :
|
270
|
-
x =
|
271
|
-
|
272
|
-
rectangle = create_rectangle(left, [bottom, y - (margin&.bottom || 0)].max,
|
273
|
-
left + self.width, @y)
|
274
|
-
else
|
275
|
-
x = case box.style.position_hint
|
276
|
-
when nil, :left
|
266
|
+
when :default, :float
|
267
|
+
x = case box.style.align
|
268
|
+
when :left
|
277
269
|
@x + margin_left
|
278
270
|
when :right
|
279
271
|
@x + margin_left + aw - width
|
@@ -286,19 +278,63 @@ module HexaPDF
|
|
286
278
|
@x + margin_left + (aw - width) / 2.0
|
287
279
|
end
|
288
280
|
end
|
289
|
-
y =
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
281
|
+
y = case box.style.valign
|
282
|
+
when :top
|
283
|
+
@y - margin_top - height
|
284
|
+
when :bottom
|
285
|
+
@y - available_height + margin_bottom
|
286
|
+
when :center
|
287
|
+
max_margin = [margin_top, margin_bottom].max
|
288
|
+
# If we have enough space left for equal margins, we center perfectly
|
289
|
+
if available_height - height >= 2 * max_margin
|
290
|
+
@y - height - (available_height - height) / 2.0
|
291
|
+
else
|
292
|
+
@y - margin_top - height - (ah - height) / 2.0
|
293
|
+
end
|
294
|
+
end
|
295
|
+
when :flow
|
296
|
+
x = 0
|
297
|
+
y = @y - height
|
298
|
+
else
|
299
|
+
raise HexaPDF::Error, "Invalid value '#{position}' for style property position"
|
299
300
|
end
|
300
301
|
end
|
301
302
|
|
303
|
+
mask_mode = if box.style.mask_mode == :default
|
304
|
+
case position
|
305
|
+
when :default, :flow then :fill_frame_horizontal
|
306
|
+
else :box
|
307
|
+
end
|
308
|
+
else
|
309
|
+
box.style.mask_mode
|
310
|
+
end
|
311
|
+
rectangle =
|
312
|
+
case mask_mode
|
313
|
+
when :none
|
314
|
+
create_rectangle(x, y, x, y)
|
315
|
+
when :box
|
316
|
+
if margin
|
317
|
+
create_rectangle([left, x - (margin&.left || 0)].max,
|
318
|
+
[bottom, y - (margin&.bottom || 0)].max,
|
319
|
+
[left + self.width, x + box.width + (margin&.right || 0)].min,
|
320
|
+
[bottom + self.height, y + box.height + (margin&.top || 0)].min)
|
321
|
+
else
|
322
|
+
create_rectangle(x, y, x + box.width, y + box.height)
|
323
|
+
end
|
324
|
+
when :fill_horizontal
|
325
|
+
create_rectangle(@x, [bottom, y - (margin&.bottom || 0)].max,
|
326
|
+
@x + available_width,
|
327
|
+
[@y, y + box.height + (margin&.top || 0)].min)
|
328
|
+
when :fill_frame_horizontal
|
329
|
+
create_rectangle(left, [bottom, y - (margin&.bottom || 0)].max,
|
330
|
+
left + self.width, @y)
|
331
|
+
when :fill_vertical
|
332
|
+
create_rectangle([@x, x - (margin&.left || 0)].max, @y - available_height,
|
333
|
+
[@x + available_width, x + box.width + (margin&.right || 0)].min, @y)
|
334
|
+
when :fill
|
335
|
+
create_rectangle(@x, @y - available_height, @x + available_width, @y)
|
336
|
+
end
|
337
|
+
|
302
338
|
fit_result.available_width = aw
|
303
339
|
fit_result.available_height = ah
|
304
340
|
fit_result.x = x
|
@@ -326,7 +362,7 @@ module HexaPDF
|
|
326
362
|
|
327
363
|
# Finds the next region for placing boxes. Returns +false+ if no useful region was found.
|
328
364
|
#
|
329
|
-
# This method should be called after drawing a box
|
365
|
+
# This method should be called after fitting or drawing a box was not successful. It finds a
|
330
366
|
# different region on each invocation. So if a box doesn't fit into the first region, this
|
331
367
|
# method should be called again to find another region and to try again.
|
332
368
|
#
|
@@ -363,6 +399,8 @@ module HexaPDF
|
|
363
399
|
|
364
400
|
# Removes the given *rectilinear* polygon from the frame's shape.
|
365
401
|
def remove_area(polygon)
|
402
|
+
return if polygon.kind_of?(Geom2D::Rectangle) && (polygon.width == 0 || polygon.height == 0)
|
403
|
+
|
366
404
|
@shape = if @shape.kind_of?(Geom2D::Rectangle) && polygon.kind_of?(Geom2D::Rectangle) &&
|
367
405
|
float_equal(@shape.x, polygon.x) && float_equal(@shape.width, polygon.width) &&
|
368
406
|
float_equal(@shape.y + @shape.height, polygon.y + polygon.height)
|
@@ -56,7 +56,7 @@ module HexaPDF
|
|
56
56
|
# #>pdf-composer100
|
57
57
|
# composer.image(machu_picchu, width: 100, height: 30)
|
58
58
|
#
|
59
|
-
# * If neither has been set, the image is scaled to fit the
|
59
|
+
# * If neither has been set, the image is scaled to fit the current region.
|
60
60
|
#
|
61
61
|
# #>pdf-composer100
|
62
62
|
# composer.image(machu_picchu)
|
@@ -79,8 +79,8 @@ module HexaPDF
|
|
79
79
|
false
|
80
80
|
end
|
81
81
|
|
82
|
-
# Fits the image into the
|
83
|
-
# account (see the class description for details).
|
82
|
+
# Fits the image into the current region of the frame, taking the initially set width and
|
83
|
+
# height into account (see the class description for details).
|
84
84
|
def fit(available_width, available_height, _frame)
|
85
85
|
image_width = @image.width.to_f
|
86
86
|
image_height = @image.height.to_f
|
@@ -44,9 +44,10 @@ module HexaPDF
|
|
44
44
|
|
45
45
|
# A ListBox arranges its children as unordered or ordered list items.
|
46
46
|
#
|
47
|
-
# The indentation of the contents from the left (#content_indentation) as well as the
|
48
|
-
#
|
49
|
-
# for ordered lists (#start_number) and the amount of spacing between items
|
47
|
+
# The indentation of the contents from the left (#content_indentation) as well as the marker
|
48
|
+
# type of the items (#marker_type) can be specified. Additionally, it is possible to define the
|
49
|
+
# start number for ordered lists (#start_number) and the amount of spacing between items
|
50
|
+
# (#item_spacing).
|
50
51
|
#
|
51
52
|
# If the list box has padding and/or borders specified, they are handled like with any other
|
52
53
|
# box. This means they are around all items and their contents and are not used separately for
|
@@ -75,7 +76,7 @@ module HexaPDF
|
|
75
76
|
# Draws a filled disc for the items of the unordered list.
|
76
77
|
#
|
77
78
|
# #>pdf-composer100
|
78
|
-
# composer.box(:list,
|
79
|
+
# composer.box(:list, marker_type: :disc) do |list|
|
79
80
|
# list.lorem_ipsum_box(sentences: 1)
|
80
81
|
# end
|
81
82
|
#
|
@@ -84,7 +85,7 @@ module HexaPDF
|
|
84
85
|
# Draws an unfilled circle for the items of the unordered list.
|
85
86
|
#
|
86
87
|
# #>pdf-composer100
|
87
|
-
# composer.box(:list,
|
88
|
+
# composer.box(:list, marker_type: :circle) do |list|
|
88
89
|
# list.lorem_ipsum_box(sentences: 1)
|
89
90
|
# end
|
90
91
|
#
|
@@ -93,7 +94,7 @@ module HexaPDF
|
|
93
94
|
# Draws a filled square for the items of the unordered list.
|
94
95
|
#
|
95
96
|
# #>pdf-composer100
|
96
|
-
# composer.box(:list,
|
97
|
+
# composer.box(:list, marker_type: :square) do |list|
|
97
98
|
# list.lorem_ipsum_box(sentences: 1)
|
98
99
|
# end
|
99
100
|
#
|
@@ -103,7 +104,7 @@ module HexaPDF
|
|
103
104
|
# the ordered list.
|
104
105
|
#
|
105
106
|
# #>pdf-composer100
|
106
|
-
# composer.box(:list,
|
107
|
+
# composer.box(:list, marker_type: :decimal) do |list|
|
107
108
|
# 5.times { list.lorem_ipsum_box(sentences: 1) }
|
108
109
|
# end
|
109
110
|
#
|
@@ -118,19 +119,19 @@ module HexaPDF
|
|
118
119
|
# image = lambda do |document, box, index|
|
119
120
|
# document.layout.image_box(machu_picchu, height: box.style.font_size)
|
120
121
|
# end
|
121
|
-
# composer.box(:list,
|
122
|
+
# composer.box(:list, marker_type: image) do |list|
|
122
123
|
# 2.times { list.lorem_ipsum_box(sentences: 1) }
|
123
124
|
# end
|
124
|
-
attr_reader :
|
125
|
+
attr_reader :marker_type
|
125
126
|
|
126
|
-
# The start number when using
|
127
|
+
# The start number when using a #marker_type that represents an ordered list.
|
127
128
|
#
|
128
129
|
# The default value for this is 1.
|
129
130
|
#
|
130
131
|
# Example:
|
131
132
|
#
|
132
133
|
# #>pdf-composer100
|
133
|
-
# composer.box(:list,
|
134
|
+
# composer.box(:list, marker_type: :decimal, start_number: 3) do |list|
|
134
135
|
# 2.times { list.lorem_ipsum_box(sentences: 1) }
|
135
136
|
# end
|
136
137
|
attr_reader :start_number
|
@@ -162,11 +163,11 @@ module HexaPDF
|
|
162
163
|
attr_reader :item_spacing
|
163
164
|
|
164
165
|
# Creates a new ListBox object for the given child boxes in +children+.
|
165
|
-
def initialize(children: [],
|
166
|
+
def initialize(children: [], marker_type: :disc, content_indentation: nil, start_number: 1,
|
166
167
|
item_spacing: 0, **kwargs)
|
167
168
|
super(**kwargs)
|
168
169
|
@children = children
|
169
|
-
@
|
170
|
+
@marker_type = marker_type
|
170
171
|
@content_indentation = content_indentation || 2 * style.font_size
|
171
172
|
@start_number = start_number
|
172
173
|
@item_spacing = item_spacing
|
@@ -185,7 +186,7 @@ module HexaPDF
|
|
185
186
|
super && (!@results || @results.all? {|result| result.box_fitter.fit_results.empty? })
|
186
187
|
end
|
187
188
|
|
188
|
-
# Fits the list box into the
|
189
|
+
# Fits the list box into the current region of the frame.
|
189
190
|
def fit(available_width, available_height, frame)
|
190
191
|
@width = if @initial_width > 0
|
191
192
|
@initial_width
|
@@ -322,10 +323,10 @@ module HexaPDF
|
|
322
323
|
# Creates a box for the item marker at the given item index, using #item_style to decide on
|
323
324
|
# its contents.
|
324
325
|
def item_marker_box(document, index)
|
325
|
-
return @
|
326
|
+
return @marker_type.call(document, self, index) if @marker_type.kind_of?(Proc)
|
326
327
|
return @item_marker_box if defined?(@item_marker_box)
|
327
328
|
|
328
|
-
fragment = case @
|
329
|
+
fragment = case @marker_type
|
329
330
|
when :disc
|
330
331
|
TextFragment.create("•", font: document.fonts.add("Times"),
|
331
332
|
font_size: style.font_size, fill_color: style.fill_color)
|
@@ -347,10 +348,10 @@ module HexaPDF
|
|
347
348
|
}
|
348
349
|
TextFragment.create(text, decimal_style)
|
349
350
|
else
|
350
|
-
raise HexaPDF::Error, "Unknown list
|
351
|
+
raise HexaPDF::Error, "Unknown list marker type #{@marker_type.inspect}"
|
351
352
|
end
|
352
|
-
box = TextBox.new(items: [fragment], style: {
|
353
|
-
@item_marker_box = box unless @
|
353
|
+
box = TextBox.new(items: [fragment], style: {text_align: :right, padding: [0, 5, 0, 0]})
|
354
|
+
@item_marker_box = box unless @marker_type == :decimal
|
354
355
|
box
|
355
356
|
end
|
356
357
|
|