hexapdf 0.23.0 → 0.24.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 +66 -0
- data/LICENSE +1 -1
- data/Rakefile +1 -1
- data/examples/016-frame_automatic_box_placement.rb +7 -2
- data/examples/017-frame_text_flow.rb +10 -18
- data/examples/020-column_box.rb +40 -0
- data/examples/021-list_box.rb +26 -0
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +1 -1
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +2 -2
- data/lib/hexapdf/cli/image2pdf.rb +1 -1
- data/lib/hexapdf/cli/images.rb +1 -1
- data/lib/hexapdf/cli/info.rb +2 -2
- data/lib/hexapdf/cli/inspect.rb +2 -2
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +1 -1
- data/lib/hexapdf/cli.rb +1 -1
- data/lib/hexapdf/composer.rb +45 -126
- data/lib/hexapdf/configuration.rb +17 -1
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +2 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +1 -1
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/layout.rb +397 -0
- data/lib/hexapdf/document/pages.rb +17 -1
- data/lib/hexapdf/document/signatures.rb +5 -4
- data/lib/hexapdf/document.rb +8 -1
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +1 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +30 -21
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/error.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/crypt.rb +1 -1
- data/lib/hexapdf/filter/encryption.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 +1 -1
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +1 -1
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/base.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +2 -2
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
- 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 +1 -1
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +1 -1
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/font.rb +1 -1
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
- data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
- data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
- data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
- data/lib/hexapdf/font/true_type/table/name.rb +1 -1
- data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +1 -1
- data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
- data/lib/hexapdf/font/type1.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.rb +1 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/from_file.rb +1 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/layout/box.rb +121 -22
- data/lib/hexapdf/layout/box_fitter.rb +136 -0
- data/lib/hexapdf/layout/column_box.rb +247 -0
- data/lib/hexapdf/layout/frame.rb +155 -139
- data/lib/hexapdf/layout/image_box.rb +19 -4
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/list_box.rb +355 -0
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/style.rb +5 -1
- data/lib/hexapdf/layout/text_box.rb +20 -9
- data/lib/hexapdf/layout/text_fragment.rb +3 -2
- data/lib/hexapdf/layout/text_layouter.rb +17 -2
- data/lib/hexapdf/layout/text_shaper.rb +1 -1
- data/lib/hexapdf/layout/width_from_polygon.rb +12 -7
- data/lib/hexapdf/layout.rb +4 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +1 -1
- data/lib/hexapdf/parser.rb +1 -8
- 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 +1 -1
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +1 -1
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +1 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +1 -1
- 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 +1 -1
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
- 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/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +1 -1
- 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 +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +1 -1
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +1 -1
- data/lib/hexapdf/type/font_true_type.rb +1 -1
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +1 -1
- 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/names.rb +1 -1
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/page.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +19 -2
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
- data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +1 -1
- data/lib/hexapdf/type/signature/handler.rb +1 -1
- data/lib/hexapdf/type/signature/verification_result.rb +1 -1
- data/lib/hexapdf/type/signature.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 +1 -1
- data/lib/hexapdf/type.rb +1 -1
- data/lib/hexapdf/utils/bit_field.rb +1 -1
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
- data/lib/hexapdf/utils/lru_cache.rb +1 -1
- data/lib/hexapdf/utils/math_helpers.rb +1 -1
- data/lib/hexapdf/utils/object_hash.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +9 -7
- data/lib/hexapdf/xref_section.rb +1 -1
- data/lib/hexapdf.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +1 -1
- data/test/hexapdf/document/test_destinations.rb +1 -1
- data/test/hexapdf/document/test_images.rb +1 -1
- data/test/hexapdf/document/test_layout.rb +264 -0
- data/test/hexapdf/document/test_pages.rb +9 -0
- data/test/hexapdf/document/test_signatures.rb +10 -3
- data/test/hexapdf/encryption/test_security_handler.rb +1 -1
- data/test/hexapdf/font/encoding/test_glyph_list.rb +4 -0
- data/test/hexapdf/layout/test_box.rb +53 -3
- data/test/hexapdf/layout/test_box_fitter.rb +62 -0
- data/test/hexapdf/layout/test_column_box.rb +159 -0
- data/test/hexapdf/layout/test_frame.rb +99 -38
- data/test/hexapdf/layout/test_image_box.rb +1 -1
- data/test/hexapdf/layout/test_list_box.rb +249 -0
- data/test/hexapdf/layout/test_text_box.rb +17 -2
- data/test/hexapdf/layout/test_text_fragment.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +42 -17
- data/test/hexapdf/layout/test_width_from_polygon.rb +13 -0
- data/test/hexapdf/test_composer.rb +11 -0
- data/test/hexapdf/test_dictionary_fields.rb +9 -9
- data/test/hexapdf/test_document.rb +4 -4
- data/test/hexapdf/test_filter.rb +1 -1
- data/test/hexapdf/test_parser.rb +0 -2
- data/test/hexapdf/test_revisions.rb +2 -2
- data/test/hexapdf/test_serializer.rb +1 -5
- data/test/hexapdf/test_writer.rb +58 -3
- data/test/hexapdf/type/test_page_tree_node.rb +21 -1
- data/test/hexapdf/type/test_trailer.rb +3 -3
- data/test/test_helper.rb +5 -1
- metadata +28 -3
data/lib/hexapdf/layout/frame.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2022 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -75,53 +75,72 @@ module HexaPDF
|
|
|
75
75
|
# fitting, splitting or drawing a box. Note that the margin is ignored if a box's side coincides
|
|
76
76
|
# with the frame's original boundary.
|
|
77
77
|
#
|
|
78
|
-
# == Frame Shape
|
|
78
|
+
# == Frame Shape
|
|
79
79
|
#
|
|
80
|
-
# A frame's shape is used to determine the available space for laying out boxes
|
|
81
|
-
# line is used whenever text should be flown around objects. They are normally the same but can
|
|
82
|
-
# differ if a box with an arbitrary contour line is drawn onto the frame.
|
|
80
|
+
# A frame's shape is used to determine the available space for laying out boxes.
|
|
83
81
|
#
|
|
84
82
|
# Initially, a frame has a rectangular shape. However, once boxes are added and the frame's
|
|
85
83
|
# available area gets reduced, a frame may have a polygon set consisting of arbitrary
|
|
86
84
|
# rectilinear polygons as shape.
|
|
87
85
|
#
|
|
88
|
-
#
|
|
86
|
+
# It is also possible to provide a different initial shape on initialization.
|
|
89
87
|
class Frame
|
|
90
88
|
|
|
91
89
|
include Geom2D::Utils
|
|
92
90
|
|
|
93
|
-
#
|
|
94
|
-
class
|
|
91
|
+
# Stores the result of fitting a box in a Frame.
|
|
92
|
+
class FitResult
|
|
95
93
|
|
|
96
94
|
# The box that was fitted into the frame.
|
|
97
95
|
attr_accessor :box
|
|
98
96
|
|
|
99
|
-
# The
|
|
97
|
+
# The horizontal position where the box will be drawn.
|
|
98
|
+
attr_accessor :x
|
|
99
|
+
|
|
100
|
+
# The vertical position where the box will be drawn.
|
|
101
|
+
attr_accessor :y
|
|
102
|
+
|
|
103
|
+
# The available width in the frame for this particular box.
|
|
100
104
|
attr_accessor :available_width
|
|
101
105
|
|
|
102
|
-
# The available height for this particular box.
|
|
106
|
+
# The available height in the frame for this particular box.
|
|
103
107
|
attr_accessor :available_height
|
|
104
108
|
|
|
105
|
-
# The
|
|
106
|
-
|
|
109
|
+
# The rectangle (a Geom2D::Polygon object) that will be removed from the frame when drawing
|
|
110
|
+
# the box.
|
|
111
|
+
attr_accessor :mask
|
|
107
112
|
|
|
108
|
-
#
|
|
109
|
-
|
|
113
|
+
# Initialize the result object for the given box.
|
|
114
|
+
def initialize(box)
|
|
115
|
+
@box = box
|
|
116
|
+
@available_width = 0
|
|
117
|
+
@available_height = 0
|
|
118
|
+
@success = false
|
|
119
|
+
end
|
|
110
120
|
|
|
111
|
-
#
|
|
112
|
-
|
|
121
|
+
# Marks the fitting status as success.
|
|
122
|
+
def success!
|
|
123
|
+
@success = true
|
|
124
|
+
end
|
|
113
125
|
|
|
114
|
-
#
|
|
115
|
-
def
|
|
116
|
-
|
|
126
|
+
# Returns +true+ if fitting was successful.
|
|
127
|
+
def success?
|
|
128
|
+
@success
|
|
117
129
|
end
|
|
118
130
|
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
131
|
+
# Draws the #box onto the canvas at (#x, #y).
|
|
132
|
+
#
|
|
133
|
+
# The configuration option "debug" can be used to add visual debug output with respect to
|
|
134
|
+
# box placement.
|
|
135
|
+
def draw(canvas)
|
|
136
|
+
if canvas.context.document.config['debug']
|
|
137
|
+
canvas.save_graphics_state do
|
|
138
|
+
canvas.fill_color("green").stroke_color("darkgreen").
|
|
139
|
+
opacity(fill_alpha: 0.1, stroke_alpha: 0.2).
|
|
140
|
+
draw(:geom2d, object: mask, path_only: true).fill_stroke
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
box.draw(canvas, x, y)
|
|
125
144
|
end
|
|
126
145
|
|
|
127
146
|
end
|
|
@@ -163,130 +182,129 @@ module HexaPDF
|
|
|
163
182
|
attr_reader :available_height
|
|
164
183
|
|
|
165
184
|
# Creates a new Frame object for the given rectangular area.
|
|
166
|
-
def initialize(left, bottom, width, height,
|
|
185
|
+
def initialize(left, bottom, width, height, shape: nil)
|
|
167
186
|
@left = left
|
|
168
187
|
@bottom = bottom
|
|
169
188
|
@width = width
|
|
170
189
|
@height = height
|
|
171
|
-
@
|
|
172
|
-
@shape = Geom2D::PolygonSet.new(
|
|
190
|
+
@shape = shape || Geom2D::PolygonSet.new(
|
|
173
191
|
[create_rectangle(left, bottom, left + width, bottom + height)]
|
|
174
192
|
)
|
|
175
193
|
@x = left
|
|
176
194
|
@y = bottom + height
|
|
177
195
|
@available_width = width
|
|
178
196
|
@available_height = height
|
|
197
|
+
|
|
198
|
+
find_max_width_region if shape
|
|
179
199
|
@region_selection = :max_height
|
|
180
|
-
@fit_data = FitData.new
|
|
181
200
|
end
|
|
182
201
|
|
|
183
|
-
# Fits the given box into the current region of available space
|
|
202
|
+
# Fits the given box into the current region of available space and returns a FitResult
|
|
203
|
+
# object.
|
|
204
|
+
#
|
|
205
|
+
# Use the FitResult#success? method to determine whether fitting was successful.
|
|
184
206
|
def fit(box)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
207
|
+
fit_result = FitResult.new(box)
|
|
208
|
+
return fit_result if full?
|
|
209
|
+
|
|
210
|
+
position = if box.style.position != :flow || box.supports_position_flow?
|
|
211
|
+
box.style.position
|
|
212
|
+
else
|
|
213
|
+
:default
|
|
214
|
+
end
|
|
188
215
|
|
|
189
|
-
if
|
|
190
|
-
false
|
|
191
|
-
elsif box.style.position == :absolute
|
|
216
|
+
if position == :absolute
|
|
192
217
|
x, y = box.style.position_hint
|
|
193
|
-
box.fit(width - x, height - y, self)
|
|
194
|
-
true
|
|
195
|
-
else
|
|
196
|
-
if box.style.margin?
|
|
197
|
-
margin = box.style.margin
|
|
198
|
-
ah -= margin.bottom unless float_equal(@y - ah, @bottom)
|
|
199
|
-
ah -= @fit_data.margin_top = margin.top unless float_equal(@y, @bottom + @height)
|
|
200
|
-
aw -= @fit_data.margin_right = margin.right unless float_equal(@x + aw, @left + @width)
|
|
201
|
-
aw -= @fit_data.margin_left = margin.left unless float_equal(@x, @left)
|
|
202
|
-
@fit_data.available_width = aw
|
|
203
|
-
@fit_data.available_height = ah
|
|
204
|
-
end
|
|
205
218
|
|
|
219
|
+
aw = width - x
|
|
220
|
+
ah = height - y
|
|
206
221
|
box.fit(aw, ah, self)
|
|
207
|
-
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
# Tries to split the (fitted) box into two parts, where the first part needs to fit into the
|
|
211
|
-
# available space, and returns both parts.
|
|
212
|
-
#
|
|
213
|
-
# If the given box is not the last fitted box, #fit is called before splitting the box.
|
|
214
|
-
#
|
|
215
|
-
# See Box#split for further details.
|
|
216
|
-
def split(box)
|
|
217
|
-
fit(box) unless box == @fit_data.box
|
|
218
|
-
boxes = box.split(@fit_data.available_width, @fit_data.available_height, self)
|
|
219
|
-
@fit_data.reset unless boxes[0] == @fit_data.box
|
|
220
|
-
boxes
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# Draws the given (fitted) box onto the canvas at the frame's current position. Returns +true+
|
|
224
|
-
# if drawing was possible, +false+ otherwise.
|
|
225
|
-
#
|
|
226
|
-
# If the given box is not the last fitted box, #fit is called before drawing the box.
|
|
227
|
-
#
|
|
228
|
-
# After a box is successfully drawn, the frame's shape and contour line are adjusted to remove
|
|
229
|
-
# the occupied area.
|
|
230
|
-
def draw(canvas, box)
|
|
231
|
-
unless box == @fit_data.box
|
|
232
|
-
fit(box) || return
|
|
233
|
-
end
|
|
222
|
+
fit_result.success!
|
|
234
223
|
|
|
235
|
-
width = box.width
|
|
236
|
-
height = box.height
|
|
237
|
-
margin = box.style.margin if box.style.margin?
|
|
238
|
-
|
|
239
|
-
if height == 0
|
|
240
|
-
@fit_data.reset
|
|
241
|
-
return true
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
case box.style.position
|
|
245
|
-
when :absolute
|
|
246
|
-
x, y = box.style.position_hint
|
|
247
224
|
x += left
|
|
248
225
|
y += bottom
|
|
249
226
|
rectangle = if box.style.margin?
|
|
227
|
+
margin = box.style.margin
|
|
250
228
|
create_rectangle(x - margin.left, y - margin.bottom,
|
|
251
|
-
x + width + margin.right, y + height + margin.top)
|
|
229
|
+
x + box.width + margin.right, y + box.height + margin.top)
|
|
252
230
|
else
|
|
253
|
-
create_rectangle(x, y, x + width, y + height)
|
|
231
|
+
create_rectangle(x, y, x + box.width, y + box.height)
|
|
254
232
|
end
|
|
255
|
-
when :flow
|
|
256
|
-
x = 0
|
|
257
|
-
y = @y - height
|
|
258
|
-
rectangle = create_rectangle(left, y, left + self.width, @y)
|
|
259
233
|
else
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
234
|
+
aw = available_width
|
|
235
|
+
ah = available_height
|
|
236
|
+
|
|
237
|
+
margin_top = margin_right = margin_left = 0
|
|
238
|
+
if box.style.margin?
|
|
239
|
+
margin = box.style.margin
|
|
240
|
+
aw -= margin_right = margin.right unless float_equal(@x + aw, @left + @width)
|
|
241
|
+
aw -= margin_left = margin.left unless float_equal(@x, @left)
|
|
242
|
+
ah -= margin.bottom unless float_equal(@y - ah, @bottom)
|
|
243
|
+
ah -= margin_top = margin.top unless float_equal(@y, @bottom + @height)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
fit_result.success! if box.fit(aw, ah, self)
|
|
247
|
+
|
|
248
|
+
width = box.width
|
|
249
|
+
height = box.height
|
|
250
|
+
|
|
251
|
+
case position
|
|
252
|
+
when :flow
|
|
253
|
+
x = 0
|
|
254
|
+
y = @y - height
|
|
255
|
+
rectangle = create_rectangle(left, [bottom, y - (margin&.bottom || 0)].max,
|
|
256
|
+
left + self.width, @y)
|
|
257
|
+
else
|
|
258
|
+
x = case box.style.position_hint
|
|
259
|
+
when nil, :left
|
|
260
|
+
@x + margin_left
|
|
261
|
+
when :right
|
|
262
|
+
@x + margin_left + aw - width
|
|
263
|
+
when :center
|
|
264
|
+
max_margin = [margin_left, margin_right].max
|
|
265
|
+
# If we have enough space left for equal margins, we center perfectly
|
|
266
|
+
if available_width - width >= 2 * max_margin
|
|
267
|
+
@x + (available_width - width) / 2.0
|
|
268
|
+
else
|
|
269
|
+
@x + margin_left + (aw - width) / 2.0
|
|
270
|
+
end
|
|
270
271
|
end
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
end
|
|
272
|
+
y = @y - height - margin_top
|
|
273
|
+
rectangle = if position == :float
|
|
274
|
+
create_rectangle([left, x - (margin&.left || 0)].max,
|
|
275
|
+
[bottom, y - (margin&.bottom || 0)].max,
|
|
276
|
+
[left + self.width, x + width + (margin&.right || 0)].min,
|
|
277
|
+
@y)
|
|
278
|
+
else
|
|
279
|
+
create_rectangle(left, [bottom, y - (margin&.bottom || 0)].max,
|
|
280
|
+
left + self.width, @y)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
283
|
end
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
fit_result.available_width = aw
|
|
286
|
+
fit_result.available_height = ah
|
|
287
|
+
fit_result.x = x
|
|
288
|
+
fit_result.y = y
|
|
289
|
+
fit_result.mask = rectangle
|
|
290
|
+
fit_result
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Tries to split the box of the given FitResult into two parts and returns both parts.
|
|
294
|
+
#
|
|
295
|
+
# See Box#split for further details.
|
|
296
|
+
def split(fit_result)
|
|
297
|
+
fit_result.box.split(fit_result.available_width, fit_result.available_height, self)
|
|
298
|
+
end
|
|
288
299
|
|
|
289
|
-
|
|
300
|
+
# Draws the box of the given FitResult onto the canvas at the fitted position.
|
|
301
|
+
#
|
|
302
|
+
# After a box is successfully drawn, the frame's shape is adjusted to remove the occupied
|
|
303
|
+
# area.
|
|
304
|
+
def draw(canvas, fit_result)
|
|
305
|
+
return if fit_result.box.height == 0 || fit_result.box.width == 0
|
|
306
|
+
fit_result.draw(canvas)
|
|
307
|
+
remove_area(fit_result.mask)
|
|
290
308
|
end
|
|
291
309
|
|
|
292
310
|
# Finds the next region for placing boxes. Returns +false+ if no useful region was found.
|
|
@@ -315,18 +333,12 @@ module HexaPDF
|
|
|
315
333
|
trim_shape
|
|
316
334
|
end
|
|
317
335
|
|
|
318
|
-
@fit_data.reset
|
|
319
336
|
available_width != 0
|
|
320
337
|
end
|
|
321
338
|
|
|
322
|
-
# Removes the given *rectilinear* polygon from
|
|
323
|
-
# line.
|
|
339
|
+
# Removes the given *rectilinear* polygon from the frame's shape.
|
|
324
340
|
def remove_area(polygon)
|
|
325
341
|
@shape = Geom2D::Algorithms::PolygonOperation.run(@shape, polygon, :difference)
|
|
326
|
-
if @contour_line
|
|
327
|
-
@contour_line = Geom2D::Algorithms::PolygonOperation.run(@contour_line, polygon,
|
|
328
|
-
:difference)
|
|
329
|
-
end
|
|
330
342
|
@region_selection = :max_width
|
|
331
343
|
find_next_region
|
|
332
344
|
end
|
|
@@ -336,25 +348,20 @@ module HexaPDF
|
|
|
336
348
|
available_width == 0
|
|
337
349
|
end
|
|
338
350
|
|
|
339
|
-
#
|
|
340
|
-
|
|
341
|
-
@contour_line || @shape
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
# Returns a width specification for the frame's contour line that can be used, for example,
|
|
345
|
-
# with TextLayouter.
|
|
351
|
+
# Returns a width specification for the frame's shape that can be used, for example, with
|
|
352
|
+
# TextLayouter.
|
|
346
353
|
#
|
|
347
354
|
# Since not all text may start at the top of the frame, the offset argument can be used to
|
|
348
355
|
# specify a vertical offset from the top of the frame where layouting should start.
|
|
349
356
|
#
|
|
350
357
|
# To be compatible with TextLayouter, the top left corner of the bounding box of the frame's
|
|
351
|
-
#
|
|
352
|
-
#
|
|
358
|
+
# shape is the origin of the coordinate system for the width specification, with positive
|
|
359
|
+
# x-values to the right and positive y-values downwards.
|
|
353
360
|
#
|
|
354
361
|
# Depending on the complexity of the frame, the result may be any of the allowed width
|
|
355
362
|
# specifications of TextLayouter#fit.
|
|
356
363
|
def width_specification(offset = 0)
|
|
357
|
-
WidthFromPolygon.new(
|
|
364
|
+
WidthFromPolygon.new(shape, offset)
|
|
358
365
|
end
|
|
359
366
|
|
|
360
367
|
private
|
|
@@ -402,9 +409,18 @@ module HexaPDF
|
|
|
402
409
|
# Just use the second top-most segment
|
|
403
410
|
# TODO: not the optimal solution!
|
|
404
411
|
index = segments.rindex {|s| s.start_point.y < @y }
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
412
|
+
segment = segments[index]
|
|
413
|
+
y = segment.start_point.y
|
|
414
|
+
polygon = if segment.min.x == @x
|
|
415
|
+
# Trim the rectangular part from the left to the segment's length
|
|
416
|
+
Geom2D::Polygon([@x, @y], [@x, y],
|
|
417
|
+
[@x + segment.length, y], [@x + segment.length, @y])
|
|
418
|
+
else
|
|
419
|
+
# Trim the whole slice between the two top-most segments
|
|
420
|
+
Geom2D::Polygon([left, y], [left + width, y],
|
|
421
|
+
[left + width, @y], [left, @y])
|
|
422
|
+
end
|
|
423
|
+
remove_area(polygon)
|
|
408
424
|
end
|
|
409
425
|
|
|
410
426
|
# Finds and sets the top-left point for the next region. This is always the top-most,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2022 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -40,13 +40,27 @@ module HexaPDF
|
|
|
40
40
|
|
|
41
41
|
# An Image box object is used for displaying an image.
|
|
42
42
|
#
|
|
43
|
+
# It can either be used directly or through the HexaPDF::Composer#image method.
|
|
44
|
+
#
|
|
43
45
|
# How an image is displayed inside an image box, depends on whether the +width+ and/or +height+
|
|
44
46
|
# of the box has been set:
|
|
45
47
|
#
|
|
46
48
|
# * If one of them has been set, the other is adjusted to retain the image ratio.
|
|
49
|
+
#
|
|
50
|
+
# #>pdf-composer100
|
|
51
|
+
# composer.image(machu_picchu, width: 40)
|
|
52
|
+
# composer.image(machu_picchu, height: 40)
|
|
53
|
+
#
|
|
47
54
|
# * If both have been set, both are used as is.
|
|
55
|
+
#
|
|
56
|
+
# #>pdf-composer100
|
|
57
|
+
# composer.image(machu_picchu, width: 100, height: 30)
|
|
58
|
+
#
|
|
48
59
|
# * If neither has been set, the image is scaled to fit the available space.
|
|
49
60
|
#
|
|
61
|
+
# #>pdf-composer100
|
|
62
|
+
# composer.image(machu_picchu)
|
|
63
|
+
#
|
|
50
64
|
# Also see: HexaPDF::Content::Canvas#image
|
|
51
65
|
class ImageBox < Box
|
|
52
66
|
|
|
@@ -55,13 +69,14 @@ module HexaPDF
|
|
|
55
69
|
|
|
56
70
|
# Creates a new Image box object for the given +image+ argument which needs to be an image
|
|
57
71
|
# object (e.g. returned by HexaPDF::Document::Images#add).
|
|
58
|
-
def initialize(image
|
|
72
|
+
def initialize(image:, **kwargs)
|
|
59
73
|
super(**kwargs)
|
|
60
74
|
@image = image
|
|
61
75
|
end
|
|
62
76
|
|
|
63
|
-
# Fits the image into the available space
|
|
64
|
-
|
|
77
|
+
# Fits the image into the available space, taking the initially set width and height into
|
|
78
|
+
# account (see the class description for details).
|
|
79
|
+
def fit(available_width, available_height, _frame)
|
|
65
80
|
image_width = @image.width.to_f
|
|
66
81
|
image_height = @image.height.to_f
|
|
67
82
|
image_ratio = image_width / image_height
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2022 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/layout/line.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2022 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|