hexapdf 0.21.1 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +137 -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 +20 -37
- 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 +31 -4
- 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 +19 -6
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +24 -4
- 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 +66 -125
- 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 +2 -2
- data/lib/hexapdf/document/destinations.rb +396 -0
- 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 +46 -90
- 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 +168 -89
- data/lib/hexapdf/layout/frame.rb +155 -140
- 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 +285 -8
- data/lib/hexapdf/layout/text_box.rb +30 -11
- data/lib/hexapdf/layout/text_fragment.rb +3 -2
- data/lib/hexapdf/layout/text_layouter.rb +23 -3
- 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 +9 -2
- data/lib/hexapdf/revisions.rb +152 -51
- 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 +22 -12
- 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 +12 -6
- data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +9 -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 +10 -2
- 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 +48 -4
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/names.rb +14 -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 +3 -2
- 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 +4 -2
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +23 -8
- 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 +338 -0
- 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 +3 -3
- 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 +114 -39
- 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 +33 -2
- data/test/hexapdf/layout/test_text_fragment.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +49 -17
- data/test/hexapdf/layout/test_width_from_polygon.rb +13 -0
- data/test/hexapdf/task/test_optimize.rb +17 -4
- data/test/hexapdf/test_composer.rb +35 -1
- data/test/hexapdf/test_dictionary_fields.rb +10 -10
- data/test/hexapdf/test_document.rb +33 -136
- data/test/hexapdf/test_filter.rb +1 -1
- data/test/hexapdf/test_parser.rb +1 -3
- data/test/hexapdf/test_revision.rb +14 -0
- data/test/hexapdf/test_revisions.rb +137 -29
- data/test/hexapdf/test_serializer.rb +1 -5
- data/test/hexapdf/test_writer.rb +99 -15
- data/test/hexapdf/type/acro_form/test_form.rb +2 -1
- data/test/hexapdf/type/acro_form/test_text_field.rb +17 -0
- data/test/hexapdf/type/test_catalog.rb +8 -0
- data/test/hexapdf/type/test_image.rb +45 -9
- data/test/hexapdf/type/test_names.rb +20 -0
- data/test/hexapdf/type/test_page_tree_node.rb +21 -1
- data/test/hexapdf/type/test_trailer.rb +3 -3
- data/test/hexapdf/type/test_xref_stream.rb +2 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +11 -1
- data/test/test_helper.rb +5 -1
- metadata +29 -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,131 +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
|
+
fit_result.success!
|
222
223
|
|
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
|
234
|
-
|
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 :float
|
256
|
-
x = @x + @fit_data.margin_left
|
257
|
-
x += @fit_data.available_width - width if box.style.position_hint == :right
|
258
|
-
y = @y - height - @fit_data.margin_top
|
259
|
-
# We use the real margins from the box because they either have the desired effect or just
|
260
|
-
# extend the rectangle outside the frame.
|
261
|
-
rectangle = create_rectangle(x - (margin&.left || 0), y - (margin&.bottom || 0),
|
262
|
-
x + width + (margin&.right || 0), @y)
|
263
|
-
when :flow
|
264
|
-
x = 0
|
265
|
-
y = @y - height
|
266
|
-
rectangle = create_rectangle(left, y, left + self.width, @y)
|
267
233
|
else
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
278
271
|
end
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
284
283
|
end
|
285
284
|
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
289
299
|
|
290
|
-
|
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)
|
291
308
|
end
|
292
309
|
|
293
310
|
# Finds the next region for placing boxes. Returns +false+ if no useful region was found.
|
@@ -316,18 +333,12 @@ module HexaPDF
|
|
316
333
|
trim_shape
|
317
334
|
end
|
318
335
|
|
319
|
-
@fit_data.reset
|
320
336
|
available_width != 0
|
321
337
|
end
|
322
338
|
|
323
|
-
# Removes the given *rectilinear* polygon from
|
324
|
-
# line.
|
339
|
+
# Removes the given *rectilinear* polygon from the frame's shape.
|
325
340
|
def remove_area(polygon)
|
326
341
|
@shape = Geom2D::Algorithms::PolygonOperation.run(@shape, polygon, :difference)
|
327
|
-
if @contour_line
|
328
|
-
@contour_line = Geom2D::Algorithms::PolygonOperation.run(@contour_line, polygon,
|
329
|
-
:difference)
|
330
|
-
end
|
331
342
|
@region_selection = :max_width
|
332
343
|
find_next_region
|
333
344
|
end
|
@@ -337,25 +348,20 @@ module HexaPDF
|
|
337
348
|
available_width == 0
|
338
349
|
end
|
339
350
|
|
340
|
-
#
|
341
|
-
|
342
|
-
@contour_line || @shape
|
343
|
-
end
|
344
|
-
|
345
|
-
# Returns a width specification for the frame's contour line that can be used, for example,
|
346
|
-
# with TextLayouter.
|
351
|
+
# Returns a width specification for the frame's shape that can be used, for example, with
|
352
|
+
# TextLayouter.
|
347
353
|
#
|
348
354
|
# Since not all text may start at the top of the frame, the offset argument can be used to
|
349
355
|
# specify a vertical offset from the top of the frame where layouting should start.
|
350
356
|
#
|
351
357
|
# To be compatible with TextLayouter, the top left corner of the bounding box of the frame's
|
352
|
-
#
|
353
|
-
#
|
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.
|
354
360
|
#
|
355
361
|
# Depending on the complexity of the frame, the result may be any of the allowed width
|
356
362
|
# specifications of TextLayouter#fit.
|
357
363
|
def width_specification(offset = 0)
|
358
|
-
WidthFromPolygon.new(
|
364
|
+
WidthFromPolygon.new(shape, offset)
|
359
365
|
end
|
360
366
|
|
361
367
|
private
|
@@ -403,9 +409,18 @@ module HexaPDF
|
|
403
409
|
# Just use the second top-most segment
|
404
410
|
# TODO: not the optimal solution!
|
405
411
|
index = segments.rindex {|s| s.start_point.y < @y }
|
406
|
-
|
407
|
-
|
408
|
-
|
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)
|
409
424
|
end
|
410
425
|
|
411
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
|