hexapdf 0.32.1 → 0.33.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 +76 -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/lib/hexapdf/cli/command.rb +5 -2
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +3 -3
- data/lib/hexapdf/cli.rb +4 -0
- data/lib/hexapdf/composer.rb +104 -52
- data/lib/hexapdf/configuration.rb +44 -39
- data/lib/hexapdf/content/canvas.rb +393 -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 +52 -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 +6 -5
- data/lib/hexapdf/dictionary_fields.rb +42 -14
- 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 +2 -2
- 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 +167 -39
- data/lib/hexapdf/document/pages.rb +3 -2
- data/lib/hexapdf/document.rb +89 -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 +1 -1
- 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 +9 -2
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +18 -4
- data/lib/hexapdf/layout/frame.rb +30 -12
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +1 -0
- data/lib/hexapdf/layout/list_box.rb +17 -1
- data/lib/hexapdf/layout/page_style.rb +4 -4
- data/lib/hexapdf/layout/style.rb +18 -3
- data/lib/hexapdf/layout/table_box.rb +682 -0
- data/lib/hexapdf/layout/text_box.rb +5 -3
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +12 -4
- 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 +8 -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 +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +4 -2
- 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/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -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 +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +3 -3
- 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 +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/mark_information.rb +1 -1
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +7 -3
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +19 -10
- data/lib/hexapdf/type/page_label.rb +1 -1
- 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/utils/pdf_doc_encoding.rb +1 -1
- 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 +0 -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 +98 -0
- 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 +1 -1
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +14 -14
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +5 -0
- data/test/hexapdf/layout/test_list_box.rb +40 -6
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +50 -0
- data/test/hexapdf/layout/test_table_box.rb +722 -0
- data/test/hexapdf/layout/test_text_box.rb +18 -0
- data/test/hexapdf/layout/test_text_layouter.rb +4 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +1 -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_parser.rb +1 -1
- 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/test_object_stream.rb +9 -3
- data/test/hexapdf/type/test_page.rb +18 -4
- metadata +17 -8
@@ -48,6 +48,9 @@ module HexaPDF
|
|
48
48
|
# Represents a single glyph of the wrapped font.
|
49
49
|
class Glyph
|
50
50
|
|
51
|
+
# The associated font object.
|
52
|
+
attr_reader :font
|
53
|
+
|
51
54
|
# The name of the glyph.
|
52
55
|
attr_reader :name
|
53
56
|
alias id name
|
@@ -164,6 +167,20 @@ module HexaPDF
|
|
164
167
|
end
|
165
168
|
end
|
166
169
|
|
170
|
+
# Returns a custom Glyph object which represents the given +string+ via the given glyph
|
171
|
+
# +name+.
|
172
|
+
#
|
173
|
+
# This functionality can be used to associate a single glyph name with multiple, different
|
174
|
+
# strings for replacement glyph purposes. When used in such a way, the used glyph name is
|
175
|
+
# often :question.
|
176
|
+
def custom_glyph(name, string)
|
177
|
+
unless @wrapped_font.metrics.character_metrics.key?(name)
|
178
|
+
raise HexaPDF::Error, "Glyph named #{name.inspect} not found in " \
|
179
|
+
"font '#{@wrapped_font.full_name}'"
|
180
|
+
end
|
181
|
+
Glyph.new(@wrapped_font, name, string)
|
182
|
+
end
|
183
|
+
|
167
184
|
# Returns an array of glyph objects representing the characters in the UTF-8 encoded string.
|
168
185
|
#
|
169
186
|
# If a Unicode codepoint is not available as glyph object, it is tried to map the codepoint
|
@@ -188,9 +205,7 @@ module HexaPDF
|
|
188
205
|
def encode(glyph)
|
189
206
|
@encoded_glyphs[glyph.name] ||=
|
190
207
|
begin
|
191
|
-
|
192
|
-
raise HexaPDF::Error, "Glyph for #{glyph.str.inspect} missing"
|
193
|
-
end
|
208
|
+
raise HexaPDF::MissingGlyphError.new(glyph) if glyph.kind_of?(InvalidGlyph)
|
194
209
|
code = @encoding.code(glyph.name)
|
195
210
|
if code
|
196
211
|
code.chr.freeze
|
@@ -199,7 +214,7 @@ module HexaPDF
|
|
199
214
|
@encoding.code_to_name[@max_code] = glyph.name
|
200
215
|
@max_code.chr.freeze
|
201
216
|
else
|
202
|
-
raise HexaPDF::Error, "Type1 encoding has no codepoint for #{glyph.name}"
|
217
|
+
raise HexaPDF::Error, "Used Type1 encoding has no codepoint for #{glyph.name.inspect}"
|
203
218
|
end
|
204
219
|
end
|
205
220
|
end
|
@@ -43,13 +43,14 @@ module HexaPDF
|
|
43
43
|
# This module uses the configuration option 'font.map' for loading a font.
|
44
44
|
module FromConfiguration
|
45
45
|
|
46
|
-
#
|
46
|
+
# Returns a TrueType font wrapper for the given font by looking up the needed file in the
|
47
|
+
# 'font.map' configuration option.
|
47
48
|
#
|
48
49
|
# The file object representing the font file is *not* closed and if needed must be closed by
|
49
50
|
# the caller once the font is not needed anymore.
|
50
51
|
#
|
51
52
|
# +document+::
|
52
|
-
# The PDF document to associate the font
|
53
|
+
# The PDF document to associate the font wrapper with.
|
53
54
|
#
|
54
55
|
# +name+::
|
55
56
|
# The name of the font.
|
@@ -59,6 +60,8 @@ module HexaPDF
|
|
59
60
|
#
|
60
61
|
# +subset+::
|
61
62
|
# Specifies whether the font should be subset if possible.
|
63
|
+
#
|
64
|
+
# This method uses the FromFile font loader behind the scenes.
|
62
65
|
def self.call(document, name, variant: :none, subset: true)
|
63
66
|
file = document.config['font.map'].dig(name, variant)
|
64
67
|
return nil if file.nil?
|
@@ -39,15 +39,15 @@ require 'hexapdf/font/true_type_wrapper'
|
|
39
39
|
module HexaPDF
|
40
40
|
module FontLoader
|
41
41
|
|
42
|
-
# This module interprets the font name either as file name and tries to load it, or as
|
43
|
-
# object to be wrapped directly.
|
42
|
+
# This module interprets the font name either as file name and tries to load it, or as TrueType
|
43
|
+
# font object to be wrapped directly.
|
44
44
|
module FromFile
|
45
45
|
|
46
46
|
# :call-seq:
|
47
47
|
# FromFile.call(document, file_name, subset: true, **) -> wrapped_font
|
48
48
|
# FromFile.call(document, font_object, subset: true, **) -> wrapped_font
|
49
49
|
#
|
50
|
-
# Returns an appropriate font wrapper for the given file name or font object.
|
50
|
+
# Returns an appropriate font wrapper for the given file name or TrueType font object.
|
51
51
|
#
|
52
52
|
# If a file name is given, the file object representing the font file is *not* closed and if
|
53
53
|
# needed must be closed by the caller once the font is not needed anymore.
|
@@ -57,10 +57,10 @@ module HexaPDF
|
|
57
57
|
# font file.
|
58
58
|
#
|
59
59
|
# +document+::
|
60
|
-
# The PDF document to associate the font
|
60
|
+
# The PDF document to associate the font wrapper with.
|
61
61
|
#
|
62
62
|
# +file_name+/+font_object+::
|
63
|
-
# The file name or TrueType
|
63
|
+
# The file name or a HexaPDF::Font::TrueType::Font object.
|
64
64
|
#
|
65
65
|
# +subset+::
|
66
66
|
# Specifies whether the font should be subset if possible.
|
@@ -71,10 +71,10 @@ module HexaPDF
|
|
71
71
|
},
|
72
72
|
}.freeze
|
73
73
|
|
74
|
-
#
|
74
|
+
# Returns a font wrapper for the named Standard PDF font.
|
75
75
|
#
|
76
76
|
# +document+::
|
77
|
-
# The PDF document to associate the font
|
77
|
+
# The PDF document to associate the font wrapper with.
|
78
78
|
#
|
79
79
|
# +name+::
|
80
80
|
# The name of the built-in font. One of Times, Helvetica, Courier, Symbol or ZapfDingbats.
|
@@ -85,7 +85,7 @@ module HexaPDF
|
|
85
85
|
#
|
86
86
|
# +custom_encoding+::
|
87
87
|
# For Times, Helvetica and Courier the standard encoding WinAnsiEncoding is used. If this
|
88
|
-
#
|
88
|
+
# is not wanted because access to other glyphs is needed, set this to +true+
|
89
89
|
def self.call(document, name, variant: :none, custom_encoding: false, **)
|
90
90
|
name = MAPPING[name] && MAPPING[name][variant]
|
91
91
|
return nil if name.nil?
|
data/lib/hexapdf/font_loader.rb
CHANGED
@@ -63,6 +63,7 @@ module HexaPDF
|
|
63
63
|
# Optionally, a font loader can provide a method +available_fonts(document)+ that returns a hash
|
64
64
|
# where the keys are the font names and the values are the variants of all the provided fonts.
|
65
65
|
#
|
66
|
+
#
|
66
67
|
# == Font Wrappers
|
67
68
|
#
|
68
69
|
# A font wrapper needs to provide the following generic interface so that it can be used correctly
|
@@ -80,6 +81,8 @@ module HexaPDF
|
|
80
81
|
# and returns an encoded string that can be decoded with the font dictionary returned by
|
81
82
|
# \#dict.
|
82
83
|
#
|
84
|
+
# HexaPDF contains a font wrapper implementation for the Standard 14 PDF fonts (see
|
85
|
+
# HexaPDF::Font::Type1Wrapper) and one for TrueType fonts (see HexaPDF::Font::TrueTypeWrapper).
|
83
86
|
module FontLoader
|
84
87
|
|
85
88
|
autoload(:Standard14, 'hexapdf/font_loader/standard14')
|
@@ -41,7 +41,7 @@ module HexaPDF
|
|
41
41
|
|
42
42
|
# This module is used for loading images in the JPEG format from files or IO streams.
|
43
43
|
#
|
44
|
-
# See:
|
44
|
+
# See: PDF2.0 s7.4.8, ITU T.81 Annex B, ITU T.872
|
45
45
|
module JPEG
|
46
46
|
|
47
47
|
# The magic marker that tells us if the file/IO contains an image in JPEG format.
|
@@ -139,7 +139,7 @@ module HexaPDF
|
|
139
139
|
break if components != 4 || invert_colors
|
140
140
|
end
|
141
141
|
|
142
|
-
#
|
142
|
+
# PDF2.0 s8.9.5.1
|
143
143
|
if bits != 8
|
144
144
|
raise HexaPDF::Error, "Unsupported number of bits per component: #{bits}"
|
145
145
|
end
|
@@ -46,7 +46,7 @@ module HexaPDF
|
|
46
46
|
# image/xobject drawing methods of HexaPDF::Content::Canvas know how to handle them correctly so
|
47
47
|
# that this doesn't matter from a user's point of view.
|
48
48
|
#
|
49
|
-
# See:
|
49
|
+
# See: PDF2.0 s8.10
|
50
50
|
module PDF
|
51
51
|
|
52
52
|
# The magic marker that tells us if the file/IO contains an PDF file.
|
@@ -52,7 +52,7 @@ module HexaPDF
|
|
52
52
|
#
|
53
53
|
# All PNG specification section references are in reference to http://www.w3.org/TR/PNG/.
|
54
54
|
#
|
55
|
-
# See:
|
55
|
+
# See: PDF2.0 s7.4.4., s8.9
|
56
56
|
class PNG
|
57
57
|
|
58
58
|
# The magic marker that tells us if the file/IO contains an image in PNG format.
|
@@ -261,7 +261,7 @@ module HexaPDF
|
|
261
261
|
# Returns a hash for a CalRGB color space definition using the x,y chromaticity coordinates
|
262
262
|
# of the white point and the red, green and blue primaries.
|
263
263
|
#
|
264
|
-
# See:
|
264
|
+
# See: PDF2.0 s8.6.5.3
|
265
265
|
def calrgb_definition_from_chrm(xw, yw, xr, yr, xg, yg, xb, yb)
|
266
266
|
z = yw * ((xg - xb) * yr - (xr - xb) * yg + (xr - xg) * yb)
|
267
267
|
|
data/lib/hexapdf/image_loader.rb
CHANGED
@@ -59,7 +59,7 @@ module HexaPDF
|
|
59
59
|
# The image XObject may use any implemented filter. For example, an image loader for JPEG files
|
60
60
|
# would typically use the DCTDecode filter instead of decoding the image itself.
|
61
61
|
#
|
62
|
-
# See:
|
62
|
+
# See: PDF2.0 s8.9
|
63
63
|
module ImageLoader
|
64
64
|
|
65
65
|
autoload(:JPEG, 'hexapdf/image_loader/jpeg')
|
data/lib/hexapdf/importer.rb
CHANGED
@@ -68,6 +68,19 @@ module HexaPDF
|
|
68
68
|
@map[destination.hash] ||= new(destination)
|
69
69
|
end
|
70
70
|
|
71
|
+
# Imports the given +object+ (belonging to the +source+ document) by completely copying it and
|
72
|
+
# all referenced objects into the +destination+ object.
|
73
|
+
#
|
74
|
+
# Specifying +source+ is optionial if it can be determined through +object+.
|
75
|
+
#
|
76
|
+
# After the operation is finished, all state is discarded. This means that another call to this
|
77
|
+
# method for the same object will yield a new - and different - object. This is in contrast to
|
78
|
+
# using ::for together with #import which remembers and returns already imported objects (which
|
79
|
+
# is generally what one wants).
|
80
|
+
def self.copy(destination, object, source: nil)
|
81
|
+
new(NullableWeakRef.new(destination)).import(object, source: source)
|
82
|
+
end
|
83
|
+
|
71
84
|
private_class_method :new
|
72
85
|
|
73
86
|
attr_reader :destination #:nodoc:
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -60,18 +60,25 @@ module HexaPDF
|
|
60
60
|
# instantiated from the common convenience method HexaPDF::Document::Layout#box. To use this
|
61
61
|
# facility subclasses need to be registered with the configuration option 'layout.boxes.map'.
|
62
62
|
#
|
63
|
-
# The methods #fit, #split or #split_content, and #draw or
|
64
|
-
# according to the subclass's use case.
|
63
|
+
# The methods #fit, #supports_position_flow?, #split or #split_content, #empty?, and #draw or
|
64
|
+
# #draw_content need to be customized according to the subclass's use case.
|
65
65
|
#
|
66
66
|
# #fit:: This method should return +true+ if fitting was successful. Additionally, the
|
67
67
|
# @fit_successful instance variable needs to be set to the fit result as it is used in
|
68
68
|
# #split.
|
69
69
|
#
|
70
|
+
# #supports_position_flow?::
|
71
|
+
# If the subclass supports the value :flow of the 'position' style property, this method
|
72
|
+
# needs to be overridden to return +true+.
|
73
|
+
#
|
70
74
|
# #split:: This method splits the content so that the available space is used as good as
|
71
75
|
# possible. The default implementation should be fine for most use-cases, so only
|
72
76
|
# #split_content needs to be implemented. The method #create_split_box should be used
|
73
77
|
# for getting a basic cloned box.
|
74
78
|
#
|
79
|
+
# #empty?:: This method should return +true+ if the subclass won't draw anything when #draw is
|
80
|
+
# called.
|
81
|
+
#
|
75
82
|
# #draw:: This method draws the content and the default implementation already handles things
|
76
83
|
# like drawing the border and background. Therefore it's best to implement #draw_content
|
77
84
|
# which should just draw the content.
|
@@ -98,7 +98,7 @@ module HexaPDF
|
|
98
98
|
if result.success?
|
99
99
|
current_frame.remove_area(result.mask)
|
100
100
|
@content_heights[@frame_index] = [@content_heights[@frame_index],
|
101
|
-
@initial_frame_y[@frame_index] - result.mask
|
101
|
+
@initial_frame_y[@frame_index] - result.mask.y].max
|
102
102
|
@fit_results << result
|
103
103
|
box = nil
|
104
104
|
break
|
@@ -109,7 +109,7 @@ module HexaPDF
|
|
109
109
|
if draw_box
|
110
110
|
current_frame.remove_area(result.mask)
|
111
111
|
@content_heights[@frame_index] = [@content_heights[@frame_index],
|
112
|
-
@initial_frame_y[@frame_index] - result.mask
|
112
|
+
@initial_frame_y[@frame_index] - result.mask.y].max
|
113
113
|
@fit_results << result
|
114
114
|
elsif !current_frame.find_next_region
|
115
115
|
@frame_index += 1
|
@@ -62,8 +62,9 @@ module HexaPDF
|
|
62
62
|
|
63
63
|
# The columns definition.
|
64
64
|
#
|
65
|
-
#
|
66
|
-
# of columns.
|
65
|
+
# If the value is an array, it needs to contain the widths of the columns. The size of the
|
66
|
+
# array determines the number of columns. Otherwise, if the value is an integer, the value
|
67
|
+
# defines the number of equally sized columns, i.e. a value of +N+ is equal to [-1]*N.
|
67
68
|
#
|
68
69
|
# If a negative integer is used for the width, the column is auto-sized. Such columns split
|
69
70
|
# the remaining width (after substracting the widths of the fixed columns) proportionally
|
@@ -132,6 +133,11 @@ module HexaPDF
|
|
132
133
|
true
|
133
134
|
end
|
134
135
|
|
136
|
+
# Returns +true+ if no box was fitted into the columns.
|
137
|
+
def empty?
|
138
|
+
super && (!@box_fitter || @box_fitter.fit_results.empty?)
|
139
|
+
end
|
140
|
+
|
135
141
|
# Fits the column box into the available space.
|
136
142
|
#
|
137
143
|
# If the style property 'position' is set to :flow, the columns might not be rectangles but
|
@@ -199,6 +205,8 @@ module HexaPDF
|
|
199
205
|
|
200
206
|
@width = columns[-1].sum + reserved_width
|
201
207
|
@height = @box_fitter.content_heights.max + reserved_height
|
208
|
+
@draw_pos_x = frame.x + reserved_width_left
|
209
|
+
@draw_pos_y = frame.y - @height + reserved_height_bottom
|
202
210
|
|
203
211
|
@box_fitter.fit_successful?
|
204
212
|
end
|
@@ -237,8 +245,14 @@ module HexaPDF
|
|
237
245
|
end
|
238
246
|
|
239
247
|
# Draws the child boxes onto the canvas at position [x, y].
|
240
|
-
def draw_content(canvas,
|
241
|
-
|
248
|
+
def draw_content(canvas, x, y)
|
249
|
+
if style.position != :flow && (x != @draw_pos_x || y != @draw_pos_y)
|
250
|
+
canvas.translate(x - @draw_pos_x, y - @draw_pos_y) do
|
251
|
+
@box_fitter.fit_results.each {|result| result.draw(canvas) }
|
252
|
+
end
|
253
|
+
else
|
254
|
+
@box_fitter.fit_results.each {|result| result.draw(canvas) }
|
255
|
+
end
|
242
256
|
end
|
243
257
|
|
244
258
|
end
|
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.
|
@@ -157,7 +157,8 @@ module HexaPDF
|
|
157
157
|
# The height of the frame.
|
158
158
|
attr_reader :height
|
159
159
|
|
160
|
-
# The shape of the frame, a Geom2D::
|
160
|
+
# The shape of the frame, either a Geom2D::Rectangle in the simple case or a
|
161
|
+
# Geom2D::PolygonSet consisting of rectilinear polygons in the more complex case.
|
161
162
|
attr_reader :shape
|
162
163
|
|
163
164
|
# The x-coordinate where the next box will be placed.
|
@@ -187,9 +188,8 @@ module HexaPDF
|
|
187
188
|
@bottom = bottom
|
188
189
|
@width = width
|
189
190
|
@height = height
|
190
|
-
@shape = shape ||
|
191
|
-
|
192
|
-
)
|
191
|
+
@shape = shape || create_rectangle(left, bottom, left + width, bottom + height)
|
192
|
+
|
193
193
|
@x = left
|
194
194
|
@y = bottom + height
|
195
195
|
@available_width = width
|
@@ -319,8 +319,16 @@ module HexaPDF
|
|
319
319
|
def find_next_region
|
320
320
|
case @region_selection
|
321
321
|
when :max_width
|
322
|
-
|
323
|
-
|
322
|
+
if @shape.kind_of?(Geom2D::Rectangle)
|
323
|
+
@x = @shape.x
|
324
|
+
@y = @shape.y + @shape.height
|
325
|
+
@available_width = @shape.width
|
326
|
+
@available_height = @shape.height
|
327
|
+
@region_selection = :trim_shape
|
328
|
+
else
|
329
|
+
find_max_width_region
|
330
|
+
@region_selection = :max_height
|
331
|
+
end
|
324
332
|
when :max_height
|
325
333
|
x, y, aw, ah = @x, @y, @available_width, @available_height
|
326
334
|
find_max_height_region
|
@@ -338,7 +346,17 @@ module HexaPDF
|
|
338
346
|
|
339
347
|
# Removes the given *rectilinear* polygon from the frame's shape.
|
340
348
|
def remove_area(polygon)
|
341
|
-
@shape = Geom2D::
|
349
|
+
@shape = if @shape.kind_of?(Geom2D::Rectangle) && polygon.kind_of?(Geom2D::Rectangle) &&
|
350
|
+
float_equal(@shape.x, polygon.x) && float_equal(@shape.width, polygon.width) &&
|
351
|
+
float_equal(@shape.y + @shape.height, polygon.y + polygon.height)
|
352
|
+
if float_equal(@shape.height, polygon.height)
|
353
|
+
Geom2D::PolygonSet()
|
354
|
+
else
|
355
|
+
Geom2D::Rectangle(@shape.x, @shape.y, @shape.width, @shape.height - polygon.height)
|
356
|
+
end
|
357
|
+
else
|
358
|
+
Geom2D::Algorithms::PolygonOperation.run(@shape, polygon, :difference)
|
359
|
+
end
|
342
360
|
@region_selection = :max_width
|
343
361
|
find_next_region
|
344
362
|
end
|
@@ -369,8 +387,7 @@ module HexaPDF
|
|
369
387
|
# Creates a Geom2D::Polygon object representing the rectangle with the bottom left corner
|
370
388
|
# (blx, bly) and the top right corner (trx, try).
|
371
389
|
def create_rectangle(blx, bly, trx, try)
|
372
|
-
Geom2D::
|
373
|
-
Geom2D::Point(trx, try), Geom2D::Point(blx, try))
|
390
|
+
Geom2D::Rectangle(blx, bly, trx - blx, try - bly)
|
374
391
|
end
|
375
392
|
|
376
393
|
# Finds the region with the maximum width.
|
@@ -404,7 +421,8 @@ module HexaPDF
|
|
404
421
|
|
405
422
|
# Trims the frame's shape so that the next starting point is different.
|
406
423
|
def trim_shape
|
407
|
-
|
424
|
+
@x = @y = @available_width = @available_height = 0
|
425
|
+
return if @shape.kind_of?(Geom2D::Rectangle) || !(segments = find_starting_point)
|
408
426
|
|
409
427
|
# Just use the second top-most segment
|
410
428
|
# 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)
|
@@ -73,6 +73,7 @@ module HexaPDF
|
|
73
73
|
# The +valign+ argument can be used to specify the vertical alignment of the box relative to
|
74
74
|
# other items in the Line.
|
75
75
|
def initialize(box, valign: :baseline)
|
76
|
+
raise HexaPDF::Error, "Width of box not set" if box.width == 0
|
76
77
|
@box = box
|
77
78
|
@valign = valign
|
78
79
|
@fit_result = Frame.new(0, 0, box.width, box.height == 0 ? 100_000 : box.height).fit(box)
|
@@ -177,6 +177,11 @@ module HexaPDF
|
|
177
177
|
true
|
178
178
|
end
|
179
179
|
|
180
|
+
# Returns +true+ if no box was fitted into the list box.
|
181
|
+
def empty?
|
182
|
+
super && (!@results || @results.all? {|box_fitter| box_fitter.fit_results.empty? })
|
183
|
+
end
|
184
|
+
|
180
185
|
# Fits the list box into the available space.
|
181
186
|
def fit(available_width, available_height, frame)
|
182
187
|
@width = if @initial_width > 0
|
@@ -234,6 +239,8 @@ module HexaPDF
|
|
234
239
|
(@results.count - 1) * item_spacing +
|
235
240
|
reserved_height
|
236
241
|
|
242
|
+
@draw_pos_x = frame.x + reserved_width_left
|
243
|
+
@draw_pos_y = frame.y - @height + reserved_height_bottom
|
237
244
|
@fit_successful = @results.all?(&:fit_successful?) && @results.size == @children.size
|
238
245
|
end
|
239
246
|
|
@@ -333,7 +340,14 @@ module HexaPDF
|
|
333
340
|
end
|
334
341
|
|
335
342
|
# Draws the list items onto the canvas at position [x, y].
|
336
|
-
def draw_content(canvas,
|
343
|
+
def draw_content(canvas, x, y)
|
344
|
+
translate = (style.position != :flow && (x != @draw_pos_x || y != @draw_pos_y))
|
345
|
+
|
346
|
+
if translate
|
347
|
+
canvas.save_graphics_state
|
348
|
+
canvas.translate(x - @draw_pos_x, y - @draw_pos_y)
|
349
|
+
end
|
350
|
+
|
337
351
|
@results.each_with_index do |box_fitter, index|
|
338
352
|
if index != 0 || !split_box? || @split_box == :show_first_marker
|
339
353
|
box = item_marker_box(canvas.context.document, index)
|
@@ -343,6 +357,8 @@ module HexaPDF
|
|
343
357
|
end
|
344
358
|
box_fitter.fit_results.each {|result| result.draw(canvas) }
|
345
359
|
end
|
360
|
+
|
361
|
+
canvas.restore_graphics_state if translate
|
346
362
|
end
|
347
363
|
|
348
364
|
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.
|
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,7 +459,7 @@ 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.
|