hexapdf 0.32.2 → 0.34.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 +104 -1
- data/README.md +9 -0
- data/examples/002-graphics.rb +15 -17
- data/examples/003-arcs.rb +9 -9
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +2 -2
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +7 -7
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +8 -7
- data/examples/016-frame_automatic_box_placement.rb +2 -2
- data/examples/017-frame_text_flow.rb +2 -1
- data/examples/018-composer.rb +1 -1
- data/examples/020-column_box.rb +2 -1
- data/examples/025-table_box.rb +46 -0
- data/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +12 -3
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +5 -7
- data/lib/hexapdf/composer.rb +106 -53
- data/lib/hexapdf/configuration.rb +65 -40
- data/lib/hexapdf/content/canvas.rb +445 -267
- data/lib/hexapdf/content/color_space.rb +72 -25
- data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
- data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
- data/lib/hexapdf/content/graphic_object.rb +6 -7
- data/lib/hexapdf/content/graphics_state.rb +54 -45
- data/lib/hexapdf/content/operator.rb +54 -54
- data/lib/hexapdf/content/parser.rb +2 -2
- data/lib/hexapdf/content/processor.rb +15 -15
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +5 -0
- data/lib/hexapdf/dictionary.rb +7 -5
- data/lib/hexapdf/dictionary_fields.rb +43 -16
- data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
- data/lib/hexapdf/digital_signature/signature.rb +6 -6
- data/lib/hexapdf/digital_signature/signatures.rb +13 -12
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
- data/lib/hexapdf/digital_signature/signing.rb +4 -0
- data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
- data/lib/hexapdf/digital_signature.rb +7 -2
- data/lib/hexapdf/document/destinations.rb +12 -11
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/layout.rb +170 -39
- data/lib/hexapdf/document/pages.rb +4 -3
- data/lib/hexapdf/document.rb +96 -55
- data/lib/hexapdf/encryption/aes.rb +5 -5
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +2 -2
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +31 -24
- data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
- data/lib/hexapdf/encryption.rb +7 -2
- data/lib/hexapdf/error.rb +18 -0
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +55 -6
- data/lib/hexapdf/font/cmap/parser.rb +2 -2
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +3 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
- data/lib/hexapdf/font/type1_wrapper.rb +19 -4
- data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
- data/lib/hexapdf/font_loader/from_file.rb +5 -5
- data/lib/hexapdf/font_loader/standard14.rb +3 -3
- data/lib/hexapdf/font_loader.rb +3 -0
- data/lib/hexapdf/image_loader/jpeg.rb +2 -2
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +13 -0
- data/lib/hexapdf/layout/box.rb +32 -5
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +20 -5
- data/lib/hexapdf/layout/frame.rb +53 -18
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +21 -9
- data/lib/hexapdf/layout/list_box.rb +50 -20
- data/lib/hexapdf/layout/page_style.rb +6 -5
- data/lib/hexapdf/layout/style.rb +64 -9
- data/lib/hexapdf/layout/table_box.rb +684 -0
- data/lib/hexapdf/layout/text_box.rb +12 -3
- data/lib/hexapdf/layout/text_fragment.rb +29 -3
- data/lib/hexapdf/layout/text_layouter.rb +32 -8
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +18 -7
- data/lib/hexapdf/parser.rb +7 -7
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +5 -4
- data/lib/hexapdf/tokenizer.rb +14 -14
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +2 -1
- data/lib/hexapdf/type/annotation.rb +3 -3
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +2 -3
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +11 -2
- data/lib/hexapdf/type/cid_font.rb +18 -4
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +3 -3
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +76 -6
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/mark_information.rb +1 -1
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
- data/lib/hexapdf/type/optional_content_group.rb +370 -0
- data/lib/hexapdf/type/optional_content_membership.rb +63 -0
- data/lib/hexapdf/type/optional_content_properties.rb +158 -0
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +46 -21
- data/lib/hexapdf/type/page_label.rb +5 -9
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +2 -2
- data/lib/hexapdf/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +4 -4
- data/lib/hexapdf/xref_section.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
- data/test/hexapdf/content/test_canvas.rb +49 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
- data/test/hexapdf/document/test_files.rb +2 -2
- data/test/hexapdf/document/test_layout.rb +105 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/encryption/test_security_handler.rb +12 -11
- data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
- data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
- data/test/hexapdf/layout/test_box.rb +14 -5
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +27 -15
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +84 -33
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +60 -0
- data/test/hexapdf/layout/test_table_box.rb +728 -0
- data/test/hexapdf/layout/test_text_box.rb +26 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +36 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +5 -0
- data/test/hexapdf/test_filter.rb +8 -0
- data/test/hexapdf/test_importer.rb +9 -0
- data/test/hexapdf/test_object.rb +16 -5
- data/test/hexapdf/test_stream.rb +7 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
- data/test/hexapdf/type/acro_form/test_form.rb +4 -3
- data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
- data/test/hexapdf/type/test_catalog.rb +11 -0
- data/test/hexapdf/type/test_form.rb +119 -0
- data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
- data/test/hexapdf/type/test_optional_content_group.rb +158 -0
- data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
- data/test/hexapdf/type/test_page.rb +20 -6
- metadata +28 -8
data/lib/hexapdf/layout/frame.rb
CHANGED
|
@@ -106,8 +106,8 @@ module HexaPDF
|
|
|
106
106
|
# The available height in the frame for this particular box.
|
|
107
107
|
attr_accessor :available_height
|
|
108
108
|
|
|
109
|
-
# The rectangle (a Geom2D::
|
|
110
|
-
# the box.
|
|
109
|
+
# The rectangle (a Geom2D::Rectangle object) that will be removed from the frame when
|
|
110
|
+
# drawing the box.
|
|
111
111
|
attr_accessor :mask
|
|
112
112
|
|
|
113
113
|
# Initialize the result object for the given box.
|
|
@@ -133,12 +133,18 @@ module HexaPDF
|
|
|
133
133
|
# The configuration option "debug" can be used to add visual debug output with respect to
|
|
134
134
|
# box placement.
|
|
135
135
|
def draw(canvas)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
doc = canvas.context.document
|
|
137
|
+
if doc.config['debug']
|
|
138
|
+
name = "#{box.class} (#{x.to_i},#{y.to_i}-#{box.width.to_i}x#{box.height.to_i})"
|
|
139
|
+
ocg = doc.optional_content.ocg(name)
|
|
140
|
+
canvas.optional_content(ocg) do
|
|
141
|
+
canvas.save_graphics_state do
|
|
142
|
+
canvas.fill_color("green").stroke_color("darkgreen").
|
|
143
|
+
opacity(fill_alpha: 0.1, stroke_alpha: 0.2).
|
|
144
|
+
draw(:geom2d, object: mask, path_only: true).fill_stroke
|
|
145
|
+
end
|
|
141
146
|
end
|
|
147
|
+
doc.optional_content.default_configuration.add_ocg_to_ui(ocg, path: 'Debug')
|
|
142
148
|
end
|
|
143
149
|
box.draw(canvas, x, y)
|
|
144
150
|
end
|
|
@@ -157,7 +163,8 @@ module HexaPDF
|
|
|
157
163
|
# The height of the frame.
|
|
158
164
|
attr_reader :height
|
|
159
165
|
|
|
160
|
-
# The shape of the frame, a Geom2D::
|
|
166
|
+
# The shape of the frame, either a Geom2D::Rectangle in the simple case or a
|
|
167
|
+
# Geom2D::PolygonSet consisting of rectilinear polygons in the more complex case.
|
|
161
168
|
attr_reader :shape
|
|
162
169
|
|
|
163
170
|
# The x-coordinate where the next box will be placed.
|
|
@@ -181,15 +188,19 @@ module HexaPDF
|
|
|
181
188
|
# Also see the note in the #x documentation for further information.
|
|
182
189
|
attr_reader :available_height
|
|
183
190
|
|
|
191
|
+
# The context object (a HexaPDF::Type::Page or HexaPDF::Type::Form) for which this frame
|
|
192
|
+
# should be used.
|
|
193
|
+
attr_reader :context
|
|
194
|
+
|
|
184
195
|
# Creates a new Frame object for the given rectangular area.
|
|
185
|
-
def initialize(left, bottom, width, height, shape: nil)
|
|
196
|
+
def initialize(left, bottom, width, height, shape: nil, context: nil)
|
|
186
197
|
@left = left
|
|
187
198
|
@bottom = bottom
|
|
188
199
|
@width = width
|
|
189
200
|
@height = height
|
|
190
|
-
@shape = shape ||
|
|
191
|
-
|
|
192
|
-
|
|
201
|
+
@shape = shape || create_rectangle(left, bottom, left + width, bottom + height)
|
|
202
|
+
@context = context
|
|
203
|
+
|
|
193
204
|
@x = left
|
|
194
205
|
@y = bottom + height
|
|
195
206
|
@available_width = width
|
|
@@ -199,6 +210,12 @@ module HexaPDF
|
|
|
199
210
|
@region_selection = :max_height
|
|
200
211
|
end
|
|
201
212
|
|
|
213
|
+
# Returns the HexaPDF::Document instance (through #context) that is associated with this Frame
|
|
214
|
+
# object or +nil+ if no context object has been set.
|
|
215
|
+
def document
|
|
216
|
+
@context&.document
|
|
217
|
+
end
|
|
218
|
+
|
|
202
219
|
# Fits the given box into the current region of available space and returns a FitResult
|
|
203
220
|
# object.
|
|
204
221
|
#
|
|
@@ -319,8 +336,16 @@ module HexaPDF
|
|
|
319
336
|
def find_next_region
|
|
320
337
|
case @region_selection
|
|
321
338
|
when :max_width
|
|
322
|
-
|
|
323
|
-
|
|
339
|
+
if @shape.kind_of?(Geom2D::Rectangle)
|
|
340
|
+
@x = @shape.x
|
|
341
|
+
@y = @shape.y + @shape.height
|
|
342
|
+
@available_width = @shape.width
|
|
343
|
+
@available_height = @shape.height
|
|
344
|
+
@region_selection = :trim_shape
|
|
345
|
+
else
|
|
346
|
+
find_max_width_region
|
|
347
|
+
@region_selection = :max_height
|
|
348
|
+
end
|
|
324
349
|
when :max_height
|
|
325
350
|
x, y, aw, ah = @x, @y, @available_width, @available_height
|
|
326
351
|
find_max_height_region
|
|
@@ -338,7 +363,17 @@ module HexaPDF
|
|
|
338
363
|
|
|
339
364
|
# Removes the given *rectilinear* polygon from the frame's shape.
|
|
340
365
|
def remove_area(polygon)
|
|
341
|
-
@shape = Geom2D::
|
|
366
|
+
@shape = if @shape.kind_of?(Geom2D::Rectangle) && polygon.kind_of?(Geom2D::Rectangle) &&
|
|
367
|
+
float_equal(@shape.x, polygon.x) && float_equal(@shape.width, polygon.width) &&
|
|
368
|
+
float_equal(@shape.y + @shape.height, polygon.y + polygon.height)
|
|
369
|
+
if float_equal(@shape.height, polygon.height)
|
|
370
|
+
Geom2D::PolygonSet()
|
|
371
|
+
else
|
|
372
|
+
Geom2D::Rectangle(@shape.x, @shape.y, @shape.width, @shape.height - polygon.height)
|
|
373
|
+
end
|
|
374
|
+
else
|
|
375
|
+
Geom2D::Algorithms::PolygonOperation.run(@shape, polygon, :difference)
|
|
376
|
+
end
|
|
342
377
|
@region_selection = :max_width
|
|
343
378
|
find_next_region
|
|
344
379
|
end
|
|
@@ -369,8 +404,7 @@ module HexaPDF
|
|
|
369
404
|
# Creates a Geom2D::Polygon object representing the rectangle with the bottom left corner
|
|
370
405
|
# (blx, bly) and the top right corner (trx, try).
|
|
371
406
|
def create_rectangle(blx, bly, trx, try)
|
|
372
|
-
Geom2D::
|
|
373
|
-
Geom2D::Point(trx, try), Geom2D::Point(blx, try))
|
|
407
|
+
Geom2D::Rectangle(blx, bly, trx - blx, try - bly)
|
|
374
408
|
end
|
|
375
409
|
|
|
376
410
|
# Finds the region with the maximum width.
|
|
@@ -404,7 +438,8 @@ module HexaPDF
|
|
|
404
438
|
|
|
405
439
|
# Trims the frame's shape so that the next starting point is different.
|
|
406
440
|
def trim_shape
|
|
407
|
-
|
|
441
|
+
@x = @y = @available_width = @available_height = 0
|
|
442
|
+
return if @shape.kind_of?(Geom2D::Rectangle) || !(segments = find_starting_point)
|
|
408
443
|
|
|
409
444
|
# Just use the second top-most segment
|
|
410
445
|
# TODO: not the optimal solution!
|
|
@@ -74,6 +74,11 @@ module HexaPDF
|
|
|
74
74
|
@image = image
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
+
# Returns +false+ since the image is always drawn if it fits.
|
|
78
|
+
def empty?
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
77
82
|
# Fits the image into the available space, taking the initially set width and height into
|
|
78
83
|
# account (see the class description for details).
|
|
79
84
|
def fit(available_width, available_height, _frame)
|
|
@@ -47,9 +47,10 @@ module HexaPDF
|
|
|
47
47
|
# beforehand! This means the box *must* have at least its width set. The height may either also
|
|
48
48
|
# be set or determined during fitting.
|
|
49
49
|
#
|
|
50
|
-
# Fitting of the wrapped box
|
|
51
|
-
# a frame is used that has the width of the
|
|
52
|
-
# practically infinite height. In the latter case
|
|
50
|
+
# Fitting of the wrapped box via #fit_wrapped_box needs to be done before accessing any other
|
|
51
|
+
# method that uses the wrapped box. For fitting, a frame is used that has the width of the
|
|
52
|
+
# wrapped box and its height, or if not set, a practically infinite height. In the latter case
|
|
53
|
+
# the height *must* be set during fitting.
|
|
53
54
|
class InlineBox
|
|
54
55
|
|
|
55
56
|
# Creates an InlineBox that wraps a basic Box. All arguments (except +valign+) and the block
|
|
@@ -73,14 +74,14 @@ module HexaPDF
|
|
|
73
74
|
# The +valign+ argument can be used to specify the vertical alignment of the box relative to
|
|
74
75
|
# other items in the Line.
|
|
75
76
|
def initialize(box, valign: :baseline)
|
|
77
|
+
raise HexaPDF::Error, "Width of box not set" if box.width == 0
|
|
76
78
|
@box = box
|
|
77
79
|
@valign = valign
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Returns the style of the wrapped box.
|
|
83
|
+
def style
|
|
84
|
+
box.style
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
# Returns +true+ if this inline box is just a placeholder without drawing operations.
|
|
@@ -125,6 +126,17 @@ module HexaPDF
|
|
|
125
126
|
height
|
|
126
127
|
end
|
|
127
128
|
|
|
129
|
+
# Fits the wrapped box, using the given context (see Frame#context).
|
|
130
|
+
def fit_wrapped_box(context)
|
|
131
|
+
@fit_result = Frame.new(0, 0, box.width, box.height == 0 ? 100_000 : box.height,
|
|
132
|
+
context: context).fit(box)
|
|
133
|
+
if !@fit_result.success?
|
|
134
|
+
raise HexaPDF::Error, "Box for inline use could not be fit"
|
|
135
|
+
elsif box.height > 99_000
|
|
136
|
+
raise HexaPDF::Error, "Box for inline use has no valid height set after fitting"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
128
140
|
end
|
|
129
141
|
|
|
130
142
|
end
|
|
@@ -60,6 +60,9 @@ module HexaPDF
|
|
|
60
60
|
# arguments are ignored.
|
|
61
61
|
class ListBox < Box
|
|
62
62
|
|
|
63
|
+
# Stores the information when fitting an item of the list box.
|
|
64
|
+
ItemResult = Struct.new(:box_fitter, :height, :marker, :marker_pos_x)
|
|
65
|
+
|
|
63
66
|
# The child boxes of this ListBox. They need to be finalized before #fit is called.
|
|
64
67
|
attr_reader :children
|
|
65
68
|
|
|
@@ -177,6 +180,11 @@ module HexaPDF
|
|
|
177
180
|
true
|
|
178
181
|
end
|
|
179
182
|
|
|
183
|
+
# Returns +true+ if no box was fitted into the list box.
|
|
184
|
+
def empty?
|
|
185
|
+
super && (!@results || @results.all? {|result| result.box_fitter.fit_results.empty? })
|
|
186
|
+
end
|
|
187
|
+
|
|
180
188
|
# Fits the list box into the available space.
|
|
181
189
|
def fit(available_width, available_height, frame)
|
|
182
190
|
@width = if @initial_width > 0
|
|
@@ -205,9 +213,10 @@ module HexaPDF
|
|
|
205
213
|
end
|
|
206
214
|
|
|
207
215
|
@results = []
|
|
208
|
-
@results_item_marker_x = []
|
|
209
216
|
|
|
210
|
-
@children.
|
|
217
|
+
@children.each_with_index do |child, index|
|
|
218
|
+
item_result = ItemResult.new
|
|
219
|
+
|
|
211
220
|
shape = Geom2D::Polygon([left, top - height],
|
|
212
221
|
[left + width, top - height],
|
|
213
222
|
[left + width, top],
|
|
@@ -217,24 +226,36 @@ module HexaPDF
|
|
|
217
226
|
remove_indent_from_frame_shape(shape) unless shape.polygons.empty?
|
|
218
227
|
end
|
|
219
228
|
|
|
220
|
-
item_frame = Frame.new(item_frame_left, top - height, item_frame_width, height,
|
|
221
|
-
|
|
229
|
+
item_frame = Frame.new(item_frame_left, top - height, item_frame_width, height,
|
|
230
|
+
shape: shape, context: frame.context)
|
|
231
|
+
|
|
232
|
+
if index != 0 || !split_box? || @split_box == :show_first_marker
|
|
233
|
+
box = item_marker_box(frame.document, index)
|
|
234
|
+
break unless box.fit(content_indentation, height, nil)
|
|
235
|
+
item_result.marker = box
|
|
236
|
+
item_result.marker_pos_x = item_frame.x - content_indentation
|
|
237
|
+
item_result.height = box.height
|
|
238
|
+
end
|
|
222
239
|
|
|
223
240
|
box_fitter = BoxFitter.new([item_frame])
|
|
224
241
|
Array(child).each {|box| box_fitter.fit(box) }
|
|
225
|
-
|
|
242
|
+
item_result.box_fitter = box_fitter
|
|
243
|
+
item_result.height = [item_result.height.to_i, box_fitter.content_heights[0]].max
|
|
244
|
+
@results << item_result
|
|
226
245
|
|
|
227
|
-
top -=
|
|
228
|
-
height -=
|
|
246
|
+
top -= item_result.height + item_spacing
|
|
247
|
+
height -= item_result.height + item_spacing
|
|
229
248
|
|
|
230
249
|
break if !box_fitter.fit_successful? || height <= 0
|
|
231
250
|
end
|
|
232
251
|
|
|
233
|
-
@height = @results.sum {|
|
|
252
|
+
@height = @results.sum {|item_result| item_result.height } +
|
|
234
253
|
(@results.count - 1) * item_spacing +
|
|
235
254
|
reserved_height
|
|
236
255
|
|
|
237
|
-
@
|
|
256
|
+
@draw_pos_x = frame.x + reserved_width_left
|
|
257
|
+
@draw_pos_y = frame.y - @height + reserved_height_bottom
|
|
258
|
+
@fit_successful = @results.all? {|r| r.box_fitter.fit_successful? } && @results.size == @children.size
|
|
238
259
|
end
|
|
239
260
|
|
|
240
261
|
private
|
|
@@ -285,7 +306,7 @@ module HexaPDF
|
|
|
285
306
|
|
|
286
307
|
# Splits the content of the list box. This method is called from Box#split.
|
|
287
308
|
def split_content(_available_width, _available_height, _frame)
|
|
288
|
-
remaining_boxes = @results[-1].remaining_boxes
|
|
309
|
+
remaining_boxes = @results[-1].box_fitter.remaining_boxes
|
|
289
310
|
first_is_split_box = remaining_boxes.first&.split_box?
|
|
290
311
|
children = (remaining_boxes.empty? ? [] : [remaining_boxes]) + @children[@results.size..-1]
|
|
291
312
|
|
|
@@ -294,7 +315,6 @@ module HexaPDF
|
|
|
294
315
|
box.instance_variable_set(:@start_number,
|
|
295
316
|
@start_number + @results.size + (first_is_split_box ? -1 : 0))
|
|
296
317
|
box.instance_variable_set(:@results, [])
|
|
297
|
-
box.instance_variable_set(:@results_item_marker_x, [])
|
|
298
318
|
|
|
299
319
|
[self, box]
|
|
300
320
|
end
|
|
@@ -308,20 +328,22 @@ module HexaPDF
|
|
|
308
328
|
fragment = case @item_type
|
|
309
329
|
when :disc
|
|
310
330
|
TextFragment.create("•", font: document.fonts.add("Times"),
|
|
311
|
-
font_size: style.font_size)
|
|
331
|
+
font_size: style.font_size, fill_color: style.fill_color)
|
|
312
332
|
when :circle
|
|
313
333
|
TextFragment.create("❍", font: document.fonts.add("ZapfDingbats"),
|
|
314
334
|
font_size: style.font_size / 2.0,
|
|
335
|
+
fill_color: style.fill_color,
|
|
315
336
|
text_rise: -style.font_size / 1.8)
|
|
316
337
|
when :square
|
|
317
338
|
TextFragment.create("■", font: document.fonts.add("ZapfDingbats"),
|
|
318
339
|
font_size: style.font_size / 2.0,
|
|
340
|
+
fill_color: style.fill_color,
|
|
319
341
|
text_rise: -style.font_size / 1.8)
|
|
320
342
|
when :decimal
|
|
321
343
|
text = (@start_number + index).to_s << "."
|
|
322
344
|
decimal_style = {
|
|
323
345
|
font: (style.font? ? style.font : document.fonts.add("Times")),
|
|
324
|
-
font_size: style.font_size || 10,
|
|
346
|
+
font_size: style.font_size || 10, fill_color: style.fill_color
|
|
325
347
|
}
|
|
326
348
|
TextFragment.create(text, decimal_style)
|
|
327
349
|
else
|
|
@@ -333,16 +355,24 @@ module HexaPDF
|
|
|
333
355
|
end
|
|
334
356
|
|
|
335
357
|
# Draws the list items onto the canvas at position [x, y].
|
|
336
|
-
def draw_content(canvas,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
358
|
+
def draw_content(canvas, x, y)
|
|
359
|
+
translate = (style.position != :flow && (x != @draw_pos_x || y != @draw_pos_y))
|
|
360
|
+
|
|
361
|
+
if translate
|
|
362
|
+
canvas.save_graphics_state
|
|
363
|
+
canvas.translate(x - @draw_pos_x, y - @draw_pos_y)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
@results.each do |item_result|
|
|
367
|
+
box_fitter = item_result.box_fitter
|
|
368
|
+
if (marker = item_result.marker)
|
|
369
|
+
marker.draw(canvas, item_result.marker_pos_x,
|
|
370
|
+
box_fitter.frames[0].bottom + box_fitter.frames[0].height - marker.height)
|
|
343
371
|
end
|
|
344
372
|
box_fitter.fit_results.each {|result| result.draw(canvas) }
|
|
345
373
|
end
|
|
374
|
+
|
|
375
|
+
canvas.restore_graphics_state if translate
|
|
346
376
|
end
|
|
347
377
|
|
|
348
378
|
end
|
|
@@ -95,8 +95,8 @@ module HexaPDF
|
|
|
95
95
|
# If this attribute is +nil+ (the default), it means that this style should be used again.
|
|
96
96
|
attr_accessor :next_style
|
|
97
97
|
|
|
98
|
-
# Creates a new page style instance for the given page size
|
|
99
|
-
# given, it is used as template for defining the initial content.
|
|
98
|
+
# Creates a new page style instance for the given page size, orientation and next style
|
|
99
|
+
# values. If a block is given, it is used as template for defining the initial content.
|
|
100
100
|
#
|
|
101
101
|
# Example:
|
|
102
102
|
#
|
|
@@ -105,12 +105,12 @@ module HexaPDF
|
|
|
105
105
|
# style.next_style = :other
|
|
106
106
|
# canvas.fill_color("fd0") { canvas.circle(100, 100, 50).fill }
|
|
107
107
|
# end
|
|
108
|
-
def initialize(page_size: :A4, orientation: :portrait, &block)
|
|
108
|
+
def initialize(page_size: :A4, orientation: :portrait, next_style: nil, &block)
|
|
109
109
|
@page_size = page_size
|
|
110
110
|
@orientation = orientation
|
|
111
111
|
@template = block
|
|
112
112
|
@frame = nil
|
|
113
|
-
@next_style =
|
|
113
|
+
@next_style = next_style
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
# Creates a new page in the given document with this page style and returns it.
|
|
@@ -135,7 +135,8 @@ module HexaPDF
|
|
|
135
135
|
Layout::Frame.new(box.left + margin.left,
|
|
136
136
|
box.bottom + margin.bottom,
|
|
137
137
|
box.width - margin.left - margin.right,
|
|
138
|
-
box.height - margin.bottom - margin.top
|
|
138
|
+
box.height - margin.bottom - margin.top,
|
|
139
|
+
context: page)
|
|
139
140
|
end
|
|
140
141
|
|
|
141
142
|
end
|
data/lib/hexapdf/layout/style.rb
CHANGED
|
@@ -206,11 +206,16 @@ module HexaPDF
|
|
|
206
206
|
# The styles of each edge. See Quad.
|
|
207
207
|
attr_reader :style
|
|
208
208
|
|
|
209
|
+
# Specifies whether the border should be drawn inside the provided rectangle (+false+,
|
|
210
|
+
# default) or on it (+true+).
|
|
211
|
+
attr_accessor :draw_on_bounds
|
|
212
|
+
|
|
209
213
|
# Creates a new border style. All arguments can be set to any value that a Quad can process.
|
|
210
|
-
def initialize(width: 0, color: 0, style: :solid)
|
|
214
|
+
def initialize(width: 0, color: 0, style: :solid, draw_on_bounds: false)
|
|
211
215
|
@width = Quad.new(width)
|
|
212
216
|
@color = Quad.new(color)
|
|
213
217
|
@style = Quad.new(style)
|
|
218
|
+
@draw_on_bounds = draw_on_bounds
|
|
214
219
|
end
|
|
215
220
|
|
|
216
221
|
# Duplicates a Border object's properties.
|
|
@@ -226,10 +231,20 @@ module HexaPDF
|
|
|
226
231
|
width.simple? && width.top == 0
|
|
227
232
|
end
|
|
228
233
|
|
|
229
|
-
# Draws the border onto the canvas
|
|
234
|
+
# Draws the border onto the canvas.
|
|
235
|
+
#
|
|
236
|
+
# Depending on #draw_on_bounds the border is drawn inside the rectangle (x, y, w, h) or on
|
|
237
|
+
# it.
|
|
230
238
|
def draw(canvas, x, y, w, h)
|
|
231
239
|
return if none?
|
|
232
240
|
|
|
241
|
+
if draw_on_bounds
|
|
242
|
+
x -= width.left / 2.0
|
|
243
|
+
y -= width.bottom / 2.0
|
|
244
|
+
w += (width.left + width.right) / 2.0
|
|
245
|
+
h += (width.top + width.bottom) / 2.0
|
|
246
|
+
end
|
|
247
|
+
|
|
233
248
|
canvas.save_graphics_state do
|
|
234
249
|
if width.simple? && color.simple? && style.simple?
|
|
235
250
|
draw_simple_border(canvas, x, y, w, h)
|
|
@@ -444,17 +459,17 @@ module HexaPDF
|
|
|
444
459
|
# Style objects using link annotations. Typical use cases would be linking to a (named)
|
|
445
460
|
# destination on a different page or executing a URI action.
|
|
446
461
|
#
|
|
447
|
-
# See:
|
|
462
|
+
# See: PDF2.0 s12.5.6.5, Layers, HexaPDF::Type::Annotations::Link
|
|
448
463
|
class LinkLayer
|
|
449
464
|
|
|
450
465
|
# Creates a new LinkLayer object.
|
|
451
466
|
#
|
|
452
|
-
# The following arguments are allowed (note that only *one* of +dest+, +uri
|
|
453
|
-
# be specified):
|
|
467
|
+
# The following arguments are allowed (note that only *one* of +dest+, +uri+, +file+ or
|
|
468
|
+
# +action+ may be specified):
|
|
454
469
|
#
|
|
455
470
|
# +dest+::
|
|
456
471
|
# The destination array or a name of a named destination for in-document links. If neither
|
|
457
|
-
# +dest
|
|
472
|
+
# +dest+, +uri+, +file+ nor +action+ is specified, it is assumed that the box has a custom
|
|
458
473
|
# property named 'link' which is used for the destination.
|
|
459
474
|
#
|
|
460
475
|
# +uri+::
|
|
@@ -465,6 +480,9 @@ module HexaPDF
|
|
|
465
480
|
# should be launched. Can either be a string or a Filespec object. Also see:
|
|
466
481
|
# HexaPDF::Type::FileSpecification.
|
|
467
482
|
#
|
|
483
|
+
# +action+::
|
|
484
|
+
# The PDF action that should be executed.
|
|
485
|
+
#
|
|
468
486
|
# +border+::
|
|
469
487
|
# If set to +true+, a standard border is used. Also accepts an array that adheres to the
|
|
470
488
|
# rules for annotation borders.
|
|
@@ -477,15 +495,17 @@ module HexaPDF
|
|
|
477
495
|
# LinkLayer.new(dest: [page, :XYZ, nil, nil, nil], border: true)
|
|
478
496
|
# LinkLayer.new(uri: "https://my.example.com/path", border: [5 5 2])
|
|
479
497
|
# LinkLayer.new # use 'link' custom box property for dest
|
|
480
|
-
def initialize(dest: nil, uri: nil, file: nil, border: false, border_color: nil)
|
|
481
|
-
if dest && (uri || file) || uri && file
|
|
482
|
-
raise ArgumentError, "Only one of dest, uri
|
|
498
|
+
def initialize(dest: nil, uri: nil, file: nil, action: nil, border: false, border_color: nil)
|
|
499
|
+
if dest && (uri || file || action) || uri && (file || action) || file && action
|
|
500
|
+
raise ArgumentError, "Only one of dest, uri, file or action is allowed"
|
|
483
501
|
end
|
|
484
502
|
@dest = dest
|
|
485
503
|
@action = if uri
|
|
486
504
|
{S: :URI, URI: uri}
|
|
487
505
|
elsif file
|
|
488
506
|
{S: :Launch, F: file, NewWindow: true}
|
|
507
|
+
elsif action
|
|
508
|
+
action
|
|
489
509
|
end
|
|
490
510
|
@border = case border
|
|
491
511
|
when false then [0, 0, 0]
|
|
@@ -1030,6 +1050,40 @@ module HexaPDF
|
|
|
1030
1050
|
# line_spacing: 1.5, last_line_gap: true)
|
|
1031
1051
|
# composer.text("There is spacing above this line due to last_line_gap.")
|
|
1032
1052
|
|
|
1053
|
+
##
|
|
1054
|
+
# :method: fill_horizontal
|
|
1055
|
+
# :call-seq:
|
|
1056
|
+
# fill_horizontal(factor = nil)
|
|
1057
|
+
#
|
|
1058
|
+
# If set to a positive number, it specifies that the content of the text item should be
|
|
1059
|
+
# repeated and appropriate spacing applied so that the remaining space of the line is
|
|
1060
|
+
# completely filled.
|
|
1061
|
+
#
|
|
1062
|
+
# If there are multiple text items with this property set for a single line, the remaining
|
|
1063
|
+
# space is split between those items using the set +factors+. For example, if item A has a
|
|
1064
|
+
# factor of 1 and item B a factor of 2, the remaining space will be split so that item
|
|
1065
|
+
# B will receive twice the space of A.
|
|
1066
|
+
#
|
|
1067
|
+
# Notes:
|
|
1068
|
+
#
|
|
1069
|
+
# * This property _must not_ be applied to inline boxes, it only works for text items.
|
|
1070
|
+
# * If the filling should be done with spaces, the non-breaking space character \u{00a0} has
|
|
1071
|
+
# to be used.
|
|
1072
|
+
#
|
|
1073
|
+
# Examples:
|
|
1074
|
+
#
|
|
1075
|
+
# #>pdf-composer100
|
|
1076
|
+
# composer.formatted_text(["Left", {text: "\u{00a0}", fill_horizontal: 1},
|
|
1077
|
+
# "Right"])
|
|
1078
|
+
# composer.formatted_text(["Typical table of contents entry",
|
|
1079
|
+
# {text: ".", fill_horizontal: 1}, "34"])
|
|
1080
|
+
# composer.formatted_text(["Factor 1", {text: "\u{00a0}", fill_horizontal: 1},
|
|
1081
|
+
# "Factor 3", {text: "\u{00a0}", fill_horizontal: 3}, "End"])
|
|
1082
|
+
# overlays = [proc {|c, b| c.line(0, b.height / 2.0, b.width, b.height / 2.0).stroke}]
|
|
1083
|
+
# composer.formatted_text([{text: "\u{00a0}", fill_horizontal: 1, overlays: overlays},
|
|
1084
|
+
# 'Centered',
|
|
1085
|
+
# {text: "\u{00a0}", fill_horizontal: 1, overlays: overlays}])
|
|
1086
|
+
|
|
1033
1087
|
##
|
|
1034
1088
|
# :method: background_color
|
|
1035
1089
|
# :call-seq:
|
|
@@ -1278,6 +1332,7 @@ module HexaPDF
|
|
|
1278
1332
|
"{type: value, value: extra_arg} : value))",
|
|
1279
1333
|
extra_args: ", extra_arg = nil"}],
|
|
1280
1334
|
[:last_line_gap, false, {valid_values: [true, false]}],
|
|
1335
|
+
[:fill_horizontal, nil],
|
|
1281
1336
|
[:background_color, nil],
|
|
1282
1337
|
[:background_alpha, 1],
|
|
1283
1338
|
[:padding, "Quad.new(0)", {setter: "Quad.new(value)"}],
|