hexapdf 0.34.1 → 0.35.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 +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
|
|