hexapdf 0.32.2 → 0.33.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 +63 -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/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 +7 -7
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +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 +2 -1
- 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_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_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.
|