hexapdf 0.32.2 → 0.34.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 +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
|
|