hexapdf 0.32.2 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +104 -1
- data/README.md +9 -0
- data/examples/002-graphics.rb +15 -17
- data/examples/003-arcs.rb +9 -9
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +2 -2
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +7 -7
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +8 -7
- data/examples/016-frame_automatic_box_placement.rb +2 -2
- data/examples/017-frame_text_flow.rb +2 -1
- data/examples/018-composer.rb +1 -1
- data/examples/020-column_box.rb +2 -1
- data/examples/025-table_box.rb +46 -0
- data/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +12 -3
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +5 -7
- data/lib/hexapdf/composer.rb +106 -53
- data/lib/hexapdf/configuration.rb +65 -40
- data/lib/hexapdf/content/canvas.rb +445 -267
- data/lib/hexapdf/content/color_space.rb +72 -25
- data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
- data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
- data/lib/hexapdf/content/graphic_object.rb +6 -7
- data/lib/hexapdf/content/graphics_state.rb +54 -45
- data/lib/hexapdf/content/operator.rb +54 -54
- data/lib/hexapdf/content/parser.rb +2 -2
- data/lib/hexapdf/content/processor.rb +15 -15
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +5 -0
- data/lib/hexapdf/dictionary.rb +7 -5
- data/lib/hexapdf/dictionary_fields.rb +43 -16
- data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
- data/lib/hexapdf/digital_signature/signature.rb +6 -6
- data/lib/hexapdf/digital_signature/signatures.rb +13 -12
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
- data/lib/hexapdf/digital_signature/signing.rb +4 -0
- data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
- data/lib/hexapdf/digital_signature.rb +7 -2
- data/lib/hexapdf/document/destinations.rb +12 -11
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/layout.rb +170 -39
- data/lib/hexapdf/document/pages.rb +4 -3
- data/lib/hexapdf/document.rb +96 -55
- data/lib/hexapdf/encryption/aes.rb +5 -5
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +2 -2
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +31 -24
- data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
- data/lib/hexapdf/encryption.rb +7 -2
- data/lib/hexapdf/error.rb +18 -0
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +55 -6
- data/lib/hexapdf/font/cmap/parser.rb +2 -2
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +3 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
- data/lib/hexapdf/font/type1_wrapper.rb +19 -4
- data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
- data/lib/hexapdf/font_loader/from_file.rb +5 -5
- data/lib/hexapdf/font_loader/standard14.rb +3 -3
- data/lib/hexapdf/font_loader.rb +3 -0
- data/lib/hexapdf/image_loader/jpeg.rb +2 -2
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +13 -0
- data/lib/hexapdf/layout/box.rb +32 -5
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +20 -5
- data/lib/hexapdf/layout/frame.rb +53 -18
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +21 -9
- data/lib/hexapdf/layout/list_box.rb +50 -20
- data/lib/hexapdf/layout/page_style.rb +6 -5
- data/lib/hexapdf/layout/style.rb +64 -9
- data/lib/hexapdf/layout/table_box.rb +684 -0
- data/lib/hexapdf/layout/text_box.rb +12 -3
- data/lib/hexapdf/layout/text_fragment.rb +29 -3
- data/lib/hexapdf/layout/text_layouter.rb +32 -8
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +18 -7
- data/lib/hexapdf/parser.rb +7 -7
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +5 -4
- data/lib/hexapdf/tokenizer.rb +14 -14
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +2 -1
- data/lib/hexapdf/type/annotation.rb +3 -3
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +2 -3
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +11 -2
- data/lib/hexapdf/type/cid_font.rb +18 -4
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +3 -3
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +76 -6
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/mark_information.rb +1 -1
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
- data/lib/hexapdf/type/optional_content_group.rb +370 -0
- data/lib/hexapdf/type/optional_content_membership.rb +63 -0
- data/lib/hexapdf/type/optional_content_properties.rb +158 -0
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +46 -21
- data/lib/hexapdf/type/page_label.rb +5 -9
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +2 -2
- data/lib/hexapdf/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +4 -4
- data/lib/hexapdf/xref_section.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
- data/test/hexapdf/content/test_canvas.rb +49 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
- data/test/hexapdf/document/test_files.rb +2 -2
- data/test/hexapdf/document/test_layout.rb +105 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/encryption/test_security_handler.rb +12 -11
- data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
- data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
- data/test/hexapdf/layout/test_box.rb +14 -5
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +27 -15
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +84 -33
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +60 -0
- data/test/hexapdf/layout/test_table_box.rb +728 -0
- data/test/hexapdf/layout/test_text_box.rb +26 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +36 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +5 -0
- data/test/hexapdf/test_filter.rb +8 -0
- data/test/hexapdf/test_importer.rb +9 -0
- data/test/hexapdf/test_object.rb +16 -5
- data/test/hexapdf/test_stream.rb +7 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
- data/test/hexapdf/type/acro_form/test_form.rb +4 -3
- data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
- data/test/hexapdf/type/test_catalog.rb +11 -0
- data/test/hexapdf/type/test_form.rb +119 -0
- data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
- data/test/hexapdf/type/test_optional_content_group.rb +158 -0
- data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
- data/test/hexapdf/type/test_page.rb +20 -6
- metadata +28 -8
|
@@ -54,6 +54,13 @@ module HexaPDF
|
|
|
54
54
|
@result = nil
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
# Returns the text that will be drawn.
|
|
58
|
+
#
|
|
59
|
+
# This will ignore any inline boxes or kerning values.
|
|
60
|
+
def text
|
|
61
|
+
@items.map {|item| item.kind_of?(TextFragment) ? item.text : '' }.join
|
|
62
|
+
end
|
|
63
|
+
|
|
57
64
|
# Returns +true+ as the 'position' style property value :flow is supported.
|
|
58
65
|
def supports_position_flow?
|
|
59
66
|
true
|
|
@@ -74,13 +81,14 @@ module HexaPDF
|
|
|
74
81
|
|
|
75
82
|
@width = @height = 0
|
|
76
83
|
@result = if style.position == :flow
|
|
77
|
-
@tl.fit(@items, frame.width_specification, frame.shape.bbox.height
|
|
84
|
+
@tl.fit(@items, frame.width_specification, frame.shape.bbox.height,
|
|
85
|
+
apply_first_text_indent: !split_box?, frame: frame)
|
|
78
86
|
else
|
|
79
87
|
@width = reserved_width
|
|
80
88
|
@height = reserved_height
|
|
81
89
|
width = (@initial_width > 0 ? @initial_width : available_width) - @width
|
|
82
90
|
height = (@initial_height > 0 ? @initial_height : available_height) - @height
|
|
83
|
-
@tl.fit(@items, width, height)
|
|
91
|
+
@tl.fit(@items, width, height, apply_first_text_indent: !split_box?, frame: frame)
|
|
84
92
|
end
|
|
85
93
|
@width += if @initial_width > 0 || style.align == :center || style.align == :right
|
|
86
94
|
width
|
|
@@ -103,7 +111,8 @@ module HexaPDF
|
|
|
103
111
|
def split(available_width, available_height, frame)
|
|
104
112
|
fit(available_width, available_height, frame) unless @result
|
|
105
113
|
|
|
106
|
-
if style.position != :flow && (@width >
|
|
114
|
+
if style.position != :flow && (float_compare(@width, available_width) > 0 ||
|
|
115
|
+
float_compare(@height, available_height) > 0)
|
|
107
116
|
[nil, self]
|
|
108
117
|
elsif @result.remaining_items.empty?
|
|
109
118
|
[self]
|
|
@@ -111,6 +111,11 @@ module HexaPDF
|
|
|
111
111
|
@properties = properties
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# Returns the text of the fragment.
|
|
115
|
+
def text
|
|
116
|
+
items.reject {|i| i.kind_of?(Numeric) }.map(&:str).join
|
|
117
|
+
end
|
|
118
|
+
|
|
114
119
|
# Creates a new TextFragment with the same style and custom properties as this one but with
|
|
115
120
|
# the given +items+.
|
|
116
121
|
def dup_attributes(items)
|
|
@@ -259,7 +264,7 @@ module HexaPDF
|
|
|
259
264
|
# The width of the text fragment.
|
|
260
265
|
#
|
|
261
266
|
# It is the sum of the widths of its items and is calculated by using the algorithm presented
|
|
262
|
-
# in
|
|
267
|
+
# in PDF2.0 s9.4.4. By using kerning values as the first and/or last items, the text contained
|
|
263
268
|
# in the fragment may spill over the left and/or right boundary.
|
|
264
269
|
def width
|
|
265
270
|
@width ||= @items.sum {|item| style.scaled_item_width(item) }
|
|
@@ -283,6 +288,28 @@ module HexaPDF
|
|
|
283
288
|
:text
|
|
284
289
|
end
|
|
285
290
|
|
|
291
|
+
# Creates a new text fragment that repeats this fragment's items and applies the necessary
|
|
292
|
+
# spacing so that the returned text fragment fills the given +width+ completely.
|
|
293
|
+
#
|
|
294
|
+
# If the given +width+ is less than the fragment's width, +self+ is returned.
|
|
295
|
+
def fill_horizontal!(width)
|
|
296
|
+
return self if width < self.width
|
|
297
|
+
|
|
298
|
+
factor, rest = width.divmod(self.width)
|
|
299
|
+
items = @items * factor
|
|
300
|
+
rest = @items.inject(rest) do |available_width, item|
|
|
301
|
+
new_available_width = available_width - style.scaled_item_width(item)
|
|
302
|
+
break available_width if new_available_width < 0
|
|
303
|
+
items << item
|
|
304
|
+
new_available_width
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
spacing = rest / (items.size - 1)
|
|
308
|
+
new_style = @style.dup.update(character_spacing: spacing)
|
|
309
|
+
items << spacing / new_style.scaled_font_size # correct spacing after last item
|
|
310
|
+
self.class.new(items, new_style, properties: @properties.dup)
|
|
311
|
+
end
|
|
312
|
+
|
|
286
313
|
# Clears all cached values.
|
|
287
314
|
#
|
|
288
315
|
# This method needs to be called if the fragment's items or attributes are changed!
|
|
@@ -293,8 +320,7 @@ module HexaPDF
|
|
|
293
320
|
|
|
294
321
|
# :nodoc:
|
|
295
322
|
def inspect
|
|
296
|
-
"#<#{self.class.name} #{
|
|
297
|
-
"#{items.inspect}>"
|
|
323
|
+
"#<#{self.class.name} #{text.inspect} #{items.inspect}>"
|
|
298
324
|
end
|
|
299
325
|
|
|
300
326
|
private
|
|
@@ -51,7 +51,8 @@ module HexaPDF
|
|
|
51
51
|
# * Existing line breaking characters inside of TextFragment objects are respected when fitting
|
|
52
52
|
# text. If this is not wanted, they have to be removed beforehand.
|
|
53
53
|
#
|
|
54
|
-
# * The first line may be indented by setting Style#text_indent which may also
|
|
54
|
+
# * The first line of each paragraph may be indented by setting Style#text_indent which may also
|
|
55
|
+
# be negative.
|
|
55
56
|
#
|
|
56
57
|
# * Text can be fitted into arbitrarily shaped areas, even containing holes.
|
|
57
58
|
#
|
|
@@ -339,8 +340,8 @@ module HexaPDF
|
|
|
339
340
|
# current start of the line index should be stored for later use.
|
|
340
341
|
#
|
|
341
342
|
# After the algorithm is finished, it returns the unused items.
|
|
342
|
-
def self.call(items, width_block, &block)
|
|
343
|
-
obj = new(items, width_block)
|
|
343
|
+
def self.call(items, width_block, frame, &block)
|
|
344
|
+
obj = new(items, width_block, frame)
|
|
344
345
|
if width_block.arity == 1
|
|
345
346
|
obj.variable_width_wrapping(&block)
|
|
346
347
|
else
|
|
@@ -352,9 +353,10 @@ module HexaPDF
|
|
|
352
353
|
|
|
353
354
|
# Creates a new line wrapping object that arranges the +items+ on lines with the given
|
|
354
355
|
# width.
|
|
355
|
-
def initialize(items, width_block)
|
|
356
|
+
def initialize(items, width_block, frame)
|
|
356
357
|
@items = items
|
|
357
358
|
@width_block = width_block
|
|
359
|
+
@frame = frame
|
|
358
360
|
@line_items = []
|
|
359
361
|
@width = 0
|
|
360
362
|
@glue_items = []
|
|
@@ -362,6 +364,7 @@ module HexaPDF
|
|
|
362
364
|
@last_breakpoint_index = 0
|
|
363
365
|
@last_breakpoint_line_items_index = 0
|
|
364
366
|
@break_prohibited_state = false
|
|
367
|
+
@fill_horizontal = false
|
|
365
368
|
|
|
366
369
|
@height_calc = Line::HeightCalculator.new
|
|
367
370
|
@line = DummyLine.new(0, 0)
|
|
@@ -504,9 +507,11 @@ module HexaPDF
|
|
|
504
507
|
#
|
|
505
508
|
# Returns +true+ if the item could be added and +false+ otherwise.
|
|
506
509
|
def add_box_item(item)
|
|
510
|
+
item.fit_wrapped_box(@frame&.context) if item.kind_of?(InlineBox)
|
|
507
511
|
return false unless @width + item.width <= @available_width
|
|
508
512
|
@line_items.concat(@glue_items).push(item)
|
|
509
513
|
@width += item.width
|
|
514
|
+
@fill_horizontal ||= item.style.fill_horizontal
|
|
510
515
|
@glue_items.clear
|
|
511
516
|
true
|
|
512
517
|
end
|
|
@@ -546,6 +551,17 @@ module HexaPDF
|
|
|
546
551
|
|
|
547
552
|
# Creates a Line object from the current line items.
|
|
548
553
|
def create_line
|
|
554
|
+
if @fill_horizontal
|
|
555
|
+
rest_width = @available_width - @width
|
|
556
|
+
indices = []
|
|
557
|
+
@line_items.each_with_index do |item, index|
|
|
558
|
+
next unless item.style.fill_horizontal
|
|
559
|
+
indices << [index, item.style.fill_horizontal]
|
|
560
|
+
rest_width += item.width
|
|
561
|
+
end
|
|
562
|
+
unit_width = rest_width / indices.sum(&:last)
|
|
563
|
+
indices.each {|i, count| @line_items[i] = @line_items[i].fill_horizontal!(unit_width * count) }
|
|
564
|
+
end
|
|
549
565
|
Line.new(@line_items)
|
|
550
566
|
end
|
|
551
567
|
|
|
@@ -565,6 +581,7 @@ module HexaPDF
|
|
|
565
581
|
@last_breakpoint_index = index
|
|
566
582
|
@last_breakpoint_line_items_index = 0
|
|
567
583
|
@break_prohibited_state = false
|
|
584
|
+
@fill_horizontal = false
|
|
568
585
|
@available_width = @width_block.call(@line)
|
|
569
586
|
end
|
|
570
587
|
|
|
@@ -658,7 +675,7 @@ module HexaPDF
|
|
|
658
675
|
end
|
|
659
676
|
|
|
660
677
|
# :call-seq:
|
|
661
|
-
# text_layouter.fit(items, width, height) -> result
|
|
678
|
+
# text_layouter.fit(items, width, height, apply_first_text_indent: true) -> result
|
|
662
679
|
#
|
|
663
680
|
# Fits the items into the given area and returns a Result object with all the information.
|
|
664
681
|
#
|
|
@@ -693,7 +710,14 @@ module HexaPDF
|
|
|
693
710
|
# The text segmentation algorithm specified via #style is applied to the items in case they
|
|
694
711
|
# are not already in segmented form. This also means that Result#remaining_items always
|
|
695
712
|
# contains segmented items.
|
|
696
|
-
|
|
713
|
+
#
|
|
714
|
+
# Optional arguments:
|
|
715
|
+
#
|
|
716
|
+
# +apply_first_text_indent+::
|
|
717
|
+
# Specifies whether style.text_indent should be applied to the first line. This should be
|
|
718
|
+
# set to +false+ if the items start with a continuation of a paragraph instead of starting
|
|
719
|
+
# a new paragraph (e.g. after a page break).
|
|
720
|
+
def fit(items, width, height, apply_first_text_indent: true, frame: nil)
|
|
697
721
|
unless items.empty? || items[0].respond_to?(:type)
|
|
698
722
|
items = style.text_segmentation_algorithm.call(items)
|
|
699
723
|
end
|
|
@@ -704,7 +728,7 @@ module HexaPDF
|
|
|
704
728
|
rest = items
|
|
705
729
|
|
|
706
730
|
# processing state variables
|
|
707
|
-
indent = style.text_indent
|
|
731
|
+
indent = apply_first_text_indent ? style.text_indent : 0
|
|
708
732
|
line_fragments = []
|
|
709
733
|
line_height = 0
|
|
710
734
|
previous_line = nil
|
|
@@ -757,7 +781,7 @@ module HexaPDF
|
|
|
757
781
|
too_wide_box = nil
|
|
758
782
|
line_height = 0
|
|
759
783
|
|
|
760
|
-
rest = style.text_line_wrapping_algorithm.call(rest, width_block) do |line, item|
|
|
784
|
+
rest = style.text_line_wrapping_algorithm.call(rest, width_block, frame) do |line, item|
|
|
761
785
|
# make sure empty lines broken by mandatory paragraph breaks are not empty
|
|
762
786
|
line << TextFragment.new([], style) if item&.type != :box && line.items.empty?
|
|
763
787
|
|
data/lib/hexapdf/layout.rb
CHANGED
|
@@ -44,7 +44,7 @@ module HexaPDF
|
|
|
44
44
|
# Number trees are similar to name trees but use integers as keys instead of strings. See
|
|
45
45
|
# HexaPDF::NameTreeNode for a more detailed explanation.
|
|
46
46
|
#
|
|
47
|
-
# See:
|
|
47
|
+
# See: PDF2.0 s7.9.7, HexaPDF::NameTreeNode
|
|
48
48
|
class NumberTreeNode < Dictionary
|
|
49
49
|
|
|
50
50
|
include Utils::SortedTreeNode
|
data/lib/hexapdf/object.rb
CHANGED
|
@@ -117,7 +117,7 @@ module HexaPDF
|
|
|
117
117
|
#
|
|
118
118
|
# See: HexaPDF::Dictionary, HexaPDF::Stream, HexaPDF::Reference, HexaPDF::Document
|
|
119
119
|
#
|
|
120
|
-
# See:
|
|
120
|
+
# See: PDF2.0 s7.3.10, s7.3.8
|
|
121
121
|
class Object
|
|
122
122
|
|
|
123
123
|
include Comparable
|
|
@@ -143,18 +143,27 @@ module HexaPDF
|
|
|
143
143
|
|
|
144
144
|
# Makes sure that the object itself as well as all nested values are direct objects.
|
|
145
145
|
#
|
|
146
|
+
# The +document+ argument needs to contain the Document instance to which +object+ belongs so
|
|
147
|
+
# that references can be correctly resolved.
|
|
148
|
+
#
|
|
146
149
|
# If an indirect object is found, it is turned into a direct object and the indirect object is
|
|
147
150
|
# deleted from the document.
|
|
148
|
-
def self.make_direct(object)
|
|
151
|
+
def self.make_direct(object, document)
|
|
149
152
|
if object.kind_of?(HexaPDF::Object) && object.indirect?
|
|
153
|
+
raise HexaPDF::Error, "Can't make a stream object a direct object" if object.data.stream
|
|
150
154
|
object_to_delete = object
|
|
151
155
|
object = object.value
|
|
152
156
|
object_to_delete.document.delete(object_to_delete)
|
|
153
157
|
end
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
case object
|
|
159
|
+
when HexaPDF::Object
|
|
160
|
+
object.data.value = make_direct(object.data.value, document)
|
|
161
|
+
when Hash
|
|
162
|
+
object.transform_values! {|val| make_direct(val, document) }
|
|
163
|
+
when Array
|
|
164
|
+
object.map! {|val| make_direct(val, document) }
|
|
165
|
+
when Reference
|
|
166
|
+
object = make_direct(document.object(object), document)
|
|
158
167
|
end
|
|
159
168
|
object
|
|
160
169
|
end
|
|
@@ -255,7 +264,7 @@ module HexaPDF
|
|
|
255
264
|
# type.
|
|
256
265
|
#
|
|
257
266
|
# However, the Type and Subtype fields can easily be used for this. Subclasses for PDF objects
|
|
258
|
-
# that don't have such fields may use a unique name that has to begin with XX (see
|
|
267
|
+
# that don't have such fields may use a unique name that has to begin with XX (see PDF2.0 sE.2)
|
|
259
268
|
# and therefore doesn't clash with names defined by the PDF specification.
|
|
260
269
|
#
|
|
261
270
|
# For basic objects this always returns +:Unknown+.
|
|
@@ -297,6 +306,8 @@ module HexaPDF
|
|
|
297
306
|
end
|
|
298
307
|
|
|
299
308
|
# Makes a deep copy of the source PDF object and resets the object identifier.
|
|
309
|
+
#
|
|
310
|
+
# Note that indirect references are *not* copied! If that is also needed, use Importer::copy.
|
|
300
311
|
def deep_copy
|
|
301
312
|
obj = dup
|
|
302
313
|
obj.instance_variable_set(:@data, @data.dup)
|
data/lib/hexapdf/parser.rb
CHANGED
|
@@ -41,11 +41,11 @@ require 'hexapdf/xref_section'
|
|
|
41
41
|
|
|
42
42
|
module HexaPDF
|
|
43
43
|
|
|
44
|
-
# Parses an IO stream according to
|
|
44
|
+
# Parses an IO stream according to PDF2.0 to get at the contained objects.
|
|
45
45
|
#
|
|
46
46
|
# This class also contains higher-level methods for getting indirect objects and revisions.
|
|
47
47
|
#
|
|
48
|
-
# See:
|
|
48
|
+
# See: PDF2.0 s7
|
|
49
49
|
class Parser
|
|
50
50
|
|
|
51
51
|
# The IO stream which is parsed.
|
|
@@ -125,7 +125,7 @@ module HexaPDF
|
|
|
125
125
|
#
|
|
126
126
|
# Returns an array containing [object, oid, gen, stream].
|
|
127
127
|
#
|
|
128
|
-
# See:
|
|
128
|
+
# See: PDF2.0 s7.3.10, s7.3.8
|
|
129
129
|
def parse_indirect_object(offset = nil)
|
|
130
130
|
@tokenizer.pos = offset + @header_offset if offset
|
|
131
131
|
oid = @tokenizer.next_token
|
|
@@ -267,7 +267,7 @@ module HexaPDF
|
|
|
267
267
|
#
|
|
268
268
|
# This method can only parse cross-reference sections, not cross-reference streams!
|
|
269
269
|
#
|
|
270
|
-
# See:
|
|
270
|
+
# See: PDF2.0 s7.5.4, s7.5.5; ADB1.7 sH.3-3.4.3
|
|
271
271
|
def parse_xref_section_and_trailer(offset)
|
|
272
272
|
@tokenizer.pos = offset + @header_offset
|
|
273
273
|
token = @tokenizer.next_token
|
|
@@ -346,7 +346,7 @@ module HexaPDF
|
|
|
346
346
|
#
|
|
347
347
|
# If strict parsing is disabled, the whole file is searched for the offset.
|
|
348
348
|
#
|
|
349
|
-
# See:
|
|
349
|
+
# See: PDF2.0 s7.5.5, ADB1.7 sH.3-3.4.4
|
|
350
350
|
def startxref_offset
|
|
351
351
|
return @startxref_offset if defined?(@startxref_offset)
|
|
352
352
|
|
|
@@ -397,7 +397,7 @@ module HexaPDF
|
|
|
397
397
|
|
|
398
398
|
# Returns the PDF version number that is stored in the file header.
|
|
399
399
|
#
|
|
400
|
-
# See:
|
|
400
|
+
# See: PDF2.0 s7.5.2
|
|
401
401
|
def file_header_version
|
|
402
402
|
unless @header_version
|
|
403
403
|
raise_malformed("PDF file header is missing or corrupt", pos: 0)
|
|
@@ -413,7 +413,7 @@ module HexaPDF
|
|
|
413
413
|
# restriction so that the header may appear in the first 1024 bytes. We follow the Adobe
|
|
414
414
|
# convention.
|
|
415
415
|
#
|
|
416
|
-
# See:
|
|
416
|
+
# See: PDF2.0 s7.5.2, ADB1.7 sH.3-3.4.1
|
|
417
417
|
def retrieve_pdf_header_offset_and_version
|
|
418
418
|
@io.seek(0)
|
|
419
419
|
@header_offset = (@io.read(1024) || '').index(/%PDF-(\d\.\d)/) || 0
|
data/lib/hexapdf/pdf_array.rb
CHANGED
data/lib/hexapdf/rectangle.rb
CHANGED
|
@@ -51,7 +51,7 @@ module HexaPDF
|
|
|
51
51
|
# where +left+ is the bottom left x-coordinate, +bottom+ is the bottom left y-coordinate, +right+
|
|
52
52
|
# is the top right x-coordinate and +top+ is the top right y-coordinate.
|
|
53
53
|
#
|
|
54
|
-
# See:
|
|
54
|
+
# See: PDF2.0 s7.9.5
|
|
55
55
|
class Rectangle < HexaPDF::PDFArray
|
|
56
56
|
|
|
57
57
|
# Returns the x-coordinate of the bottom-left corner.
|
data/lib/hexapdf/reference.rb
CHANGED
|
@@ -50,7 +50,7 @@ module HexaPDF
|
|
|
50
50
|
# keys. Furthermore the implementation is compatible to the one of Object, i.e. the hash of a
|
|
51
51
|
# Reference object is the same as the hash of an indirect Object.
|
|
52
52
|
#
|
|
53
|
-
# See:
|
|
53
|
+
# See: PDF2.0 s7.3.10, Object
|
|
54
54
|
class Reference
|
|
55
55
|
|
|
56
56
|
include Comparable
|
data/lib/hexapdf/revision.rb
CHANGED
data/lib/hexapdf/revisions.rb
CHANGED
|
@@ -55,7 +55,7 @@ module HexaPDF
|
|
|
55
55
|
# this should only be done if one is familiar with the inner workings of HexaPDF. Otherwise it is
|
|
56
56
|
# best to use the convenience methods of this class to create, access or delete indirect objects.
|
|
57
57
|
#
|
|
58
|
-
# See:
|
|
58
|
+
# See: PDF2.0 s7.5.6, HexaPDF::Revision
|
|
59
59
|
class Revisions
|
|
60
60
|
|
|
61
61
|
class << self
|
|
@@ -76,7 +76,7 @@ module HexaPDF
|
|
|
76
76
|
seen_xref_offsets = {}
|
|
77
77
|
|
|
78
78
|
while offset && !seen_xref_offsets.key?(offset)
|
|
79
|
-
#
|
|
79
|
+
# PDF2.0 s7.5.5 states that :Prev needs to be indirect, Adobe's reference 3.4.4 says it
|
|
80
80
|
# should be direct. Adobe's POV is followed here. Same with :XRefStm.
|
|
81
81
|
xref_section, trailer = parser.load_revision(offset)
|
|
82
82
|
seen_xref_offsets[offset] = true
|
|
@@ -167,7 +167,7 @@ module HexaPDF
|
|
|
167
167
|
# For references to unknown objects, +nil+ is returned but free objects are represented by a
|
|
168
168
|
# PDF Null object, not by +nil+!
|
|
169
169
|
#
|
|
170
|
-
# See:
|
|
170
|
+
# See: PDF2.0 s7.3.9
|
|
171
171
|
def object(ref)
|
|
172
172
|
i = @revisions.size - 1
|
|
173
173
|
while i >= 0
|
data/lib/hexapdf/serializer.rb
CHANGED
|
@@ -79,7 +79,7 @@ module HexaPDF
|
|
|
79
79
|
#
|
|
80
80
|
# If no serialization method for a specific class is found, the ancestors classes are tried.
|
|
81
81
|
#
|
|
82
|
-
# See:
|
|
82
|
+
# See: PDF2.0 s7.3
|
|
83
83
|
class Serializer
|
|
84
84
|
|
|
85
85
|
# The encrypter to use for encrypting strings and streams. If +nil+, strings and streams are not
|
|
@@ -163,21 +163,21 @@ module HexaPDF
|
|
|
163
163
|
|
|
164
164
|
# Serializes the +nil+ value.
|
|
165
165
|
#
|
|
166
|
-
# See:
|
|
166
|
+
# See: PDF2.0 s7.3.9
|
|
167
167
|
def serialize_nilclass(_obj)
|
|
168
168
|
"null"
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
# Serializes the +true+ value.
|
|
172
172
|
#
|
|
173
|
-
# See:
|
|
173
|
+
# See: PDF2.0 s7.3.2
|
|
174
174
|
def serialize_trueclass(_obj)
|
|
175
175
|
"true"
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
# Serializes the +false+ value.
|
|
179
179
|
#
|
|
180
|
-
# See:
|
|
180
|
+
# See: PDF2.0 s7.3.2
|
|
181
181
|
def serialize_falseclass(_obj)
|
|
182
182
|
"false"
|
|
183
183
|
end
|
|
@@ -187,21 +187,21 @@ module HexaPDF
|
|
|
187
187
|
# This method should be used for cases where it is known that the object is either an Integer
|
|
188
188
|
# or a Float.
|
|
189
189
|
#
|
|
190
|
-
# See:
|
|
190
|
+
# See: PDF2.0 s7.3.3
|
|
191
191
|
def serialize_numeric(obj)
|
|
192
192
|
obj.kind_of?(Integer) ? obj.to_s : serialize_float(obj)
|
|
193
193
|
end
|
|
194
194
|
|
|
195
195
|
# Serializes an Integer object.
|
|
196
196
|
#
|
|
197
|
-
# See:
|
|
197
|
+
# See: PDF2.0 s7.3.3
|
|
198
198
|
def serialize_integer(obj)
|
|
199
199
|
obj.to_s
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
# Serializes a Float object.
|
|
203
203
|
#
|
|
204
|
-
# See:
|
|
204
|
+
# See: PDF2.0 s7.3.3
|
|
205
205
|
def serialize_float(obj)
|
|
206
206
|
if -0.0001 < obj && obj < 0.0001 && obj != 0
|
|
207
207
|
sprintf("%.6f", obj)
|
|
@@ -215,7 +215,7 @@ module HexaPDF
|
|
|
215
215
|
# The regexp matches all characters that need to be escaped and the substs hash contains the
|
|
216
216
|
# mapping from these characters to their escaped form.
|
|
217
217
|
#
|
|
218
|
-
# See
|
|
218
|
+
# See PDF2.0 s7.3.5
|
|
219
219
|
NAME_SUBSTS = {} # :nodoc:
|
|
220
220
|
[0..32, 127..255, Tokenizer::DELIMITER.bytes, Tokenizer::WHITESPACE.bytes, [35]].each do |a|
|
|
221
221
|
a.each {|c| NAME_SUBSTS[c.chr] = "##{c.to_s(16).rjust(2, '0')}" }
|
|
@@ -225,7 +225,7 @@ module HexaPDF
|
|
|
225
225
|
|
|
226
226
|
# Serializes a Symbol object (i.e. a PDF name object).
|
|
227
227
|
#
|
|
228
|
-
# See:
|
|
228
|
+
# See: PDF2.0 s7.3.5
|
|
229
229
|
def serialize_symbol(obj)
|
|
230
230
|
NAME_CACHE[obj] ||=
|
|
231
231
|
begin
|
|
@@ -240,7 +240,7 @@ module HexaPDF
|
|
|
240
240
|
|
|
241
241
|
# Serializes an Array object.
|
|
242
242
|
#
|
|
243
|
-
# See:
|
|
243
|
+
# See: PDF2.0 s7.3.6
|
|
244
244
|
def serialize_array(obj)
|
|
245
245
|
str = +"["
|
|
246
246
|
index = 0
|
|
@@ -256,7 +256,7 @@ module HexaPDF
|
|
|
256
256
|
|
|
257
257
|
# Serializes a Hash object (i.e. a PDF dictionary object).
|
|
258
258
|
#
|
|
259
|
-
# See:
|
|
259
|
+
# See: PDF2.0 s7.3.7
|
|
260
260
|
def serialize_hash(obj)
|
|
261
261
|
str = +"<<"
|
|
262
262
|
obj.each do |k, v|
|
|
@@ -274,7 +274,7 @@ module HexaPDF
|
|
|
274
274
|
|
|
275
275
|
# Serializes a String object.
|
|
276
276
|
#
|
|
277
|
-
# See:
|
|
277
|
+
# See: PDF2.0 s7.3.4
|
|
278
278
|
def serialize_string(obj)
|
|
279
279
|
obj = if @encrypter && @object.kind_of?(HexaPDF::Object) && @object.indirect?
|
|
280
280
|
encrypter.encrypt_string(obj, @object)
|
|
@@ -294,7 +294,7 @@ module HexaPDF
|
|
|
294
294
|
# The ISO PDF specification differs in respect to the supported date format. When converting
|
|
295
295
|
# to a date string, a format suitable for both is output.
|
|
296
296
|
#
|
|
297
|
-
# See:
|
|
297
|
+
# See: PDF2.0 s7.9.4, ADB1.7 3.8.3
|
|
298
298
|
def serialize_time(obj)
|
|
299
299
|
zone = obj.strftime("%z'")
|
|
300
300
|
if zone == "+0000'"
|
|
@@ -330,14 +330,14 @@ module HexaPDF
|
|
|
330
330
|
end
|
|
331
331
|
end
|
|
332
332
|
|
|
333
|
-
# See:
|
|
333
|
+
# See: PDF2.0 s7.3.10
|
|
334
334
|
def serialize_hexapdf_reference(obj)
|
|
335
335
|
"#{obj.oid} #{obj.gen} R"
|
|
336
336
|
end
|
|
337
337
|
|
|
338
338
|
# Serializes the streams dictionary and its stream.
|
|
339
339
|
#
|
|
340
|
-
# See:
|
|
340
|
+
# See: PDF2.0 s7.3.8
|
|
341
341
|
def serialize_hexapdf_stream(obj)
|
|
342
342
|
if !obj.indirect?
|
|
343
343
|
raise HexaPDF::Error, "Can't serialize PDF stream without object identifier"
|
data/lib/hexapdf/stream.rb
CHANGED
|
@@ -88,7 +88,9 @@ module HexaPDF
|
|
|
88
88
|
|
|
89
89
|
# Returns a Fiber for getting at the data of the stream represented by this object.
|
|
90
90
|
def fiber(chunk_size = 0)
|
|
91
|
-
if @source.kind_of?(
|
|
91
|
+
if @source.kind_of?(FiberDoubleForString)
|
|
92
|
+
@source.dup
|
|
93
|
+
elsif @source.kind_of?(Proc)
|
|
92
94
|
FiberWithLength.new(@length, &@source)
|
|
93
95
|
elsif @source.kind_of?(String)
|
|
94
96
|
HexaPDF::Filter.source_from_file(@source, pos: @offset || 0, length: @length || -1,
|
|
@@ -134,7 +136,7 @@ module HexaPDF
|
|
|
134
136
|
#
|
|
135
137
|
# Note that support for external streams (/F, /FFilter, /FDecodeParms) is not yet implemented!
|
|
136
138
|
#
|
|
137
|
-
# See:
|
|
139
|
+
# See: PDF2.0 s7.3.8, Dictionary
|
|
138
140
|
class Stream < Dictionary
|
|
139
141
|
|
|
140
142
|
define_field :Length, type: Integer # not required, will be auto-filled when writing
|
|
@@ -276,9 +278,8 @@ module HexaPDF
|
|
|
276
278
|
end
|
|
277
279
|
end
|
|
278
280
|
|
|
279
|
-
# :nodoc:
|
|
280
281
|
# A mapping from short name to long name for filters.
|
|
281
|
-
FILTER_MAP = {AHx: :ASCIIHexDecode, A85: :ASCII85Decode, LZW: :LZWDecode,
|
|
282
|
+
FILTER_MAP = {AHx: :ASCIIHexDecode, A85: :ASCII85Decode, LZW: :LZWDecode, # :nodoc:
|
|
282
283
|
Fl: :FlateDecode, RL: :RunLengthDecode, CCF: :CCITTFaxDecode,
|
|
283
284
|
DCT: :DCTDecode}.freeze
|
|
284
285
|
|