hexapdf 0.45.0 → 0.46.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 +87 -47
- data/examples/019-acro_form.rb +5 -0
- data/lib/hexapdf/cli/inspect.rb +5 -0
- data/lib/hexapdf/composer.rb +1 -1
- data/lib/hexapdf/configuration.rb +8 -0
- data/lib/hexapdf/digital_signature/cms_handler.rb +31 -3
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +9 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +5 -1
- data/lib/hexapdf/document/layout.rb +48 -27
- data/lib/hexapdf/document.rb +24 -2
- data/lib/hexapdf/importer.rb +15 -5
- data/lib/hexapdf/layout/box.rb +25 -28
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +17 -23
- data/lib/hexapdf/layout/list_box.rb +24 -29
- data/lib/hexapdf/layout/page_style.rb +23 -16
- data/lib/hexapdf/layout/style.rb +2 -2
- data/lib/hexapdf/layout/text_box.rb +2 -6
- data/lib/hexapdf/parser.rb +5 -1
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/stream.rb +3 -3
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/acro_form/button_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/field.rb +8 -0
- data/lib/hexapdf/type/acro_form/form.rb +2 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/digital_signature/common.rb +66 -84
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +7 -0
- data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +9 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +41 -1
- data/test/hexapdf/digital_signature/test_handler.rb +2 -1
- data/test/hexapdf/document/test_layout.rb +28 -5
- data/test/hexapdf/layout/test_box.rb +12 -5
- data/test/hexapdf/layout/test_frame.rb +12 -2
- data/test/hexapdf/layout/test_inline_box.rb +17 -28
- data/test/hexapdf/layout/test_list_box.rb +5 -5
- data/test/hexapdf/layout/test_page_style.rb +7 -2
- data/test/hexapdf/layout/test_text_box.rb +3 -9
- data/test/hexapdf/layout/test_text_layouter.rb +0 -3
- data/test/hexapdf/test_document.rb +27 -0
- data/test/hexapdf/test_importer.rb +17 -0
- data/test/hexapdf/test_revisions.rb +54 -41
- data/test/hexapdf/type/acro_form/test_form.rb +9 -0
- metadata +2 -2
data/lib/hexapdf/document.rb
CHANGED
@@ -278,8 +278,9 @@ module HexaPDF
|
|
278
278
|
# If the same argument is provided in multiple invocations, the import is done only once and
|
279
279
|
# the previously imported object is returned.
|
280
280
|
#
|
281
|
-
# Note: If you first create a PDF document from scratch
|
282
|
-
# into another PDF document, you need to run the
|
281
|
+
# Note: If you first create a PDF document from scratch or if you modify an existing document,
|
282
|
+
# and then want to import objects from it into another PDF document, you need to run the
|
283
|
+
# following on the source document:
|
283
284
|
#
|
284
285
|
# doc.dispatch_message(:complete_objects)
|
285
286
|
# doc.validate
|
@@ -701,6 +702,27 @@ module HexaPDF
|
|
701
702
|
result
|
702
703
|
end
|
703
704
|
|
705
|
+
# Returns an in-memory copy of the PDF document.
|
706
|
+
#
|
707
|
+
# In the context of this method this means that the returned PDF document contains the same PDF
|
708
|
+
# object tree as this document, starting at the trailer. A possibly set encryption is not
|
709
|
+
# transferred to the returned document.
|
710
|
+
#
|
711
|
+
# Note: If this PDF document was created from scratch or if it is an existing document that was
|
712
|
+
# modified, the following commands need to be run on this document beforehand:
|
713
|
+
#
|
714
|
+
# doc.dispatch_message(:complete_objects)
|
715
|
+
# doc.validate
|
716
|
+
#
|
717
|
+
# This ensures that all the necessary PDF structures set-up correctly.
|
718
|
+
def duplicate
|
719
|
+
dest = HexaPDF::Document.new
|
720
|
+
dupped_trailer = HexaPDF::Importer.copy(dest, trailer, allow_all: true)
|
721
|
+
dest.revisions.current.trailer.value.replace(dupped_trailer.value)
|
722
|
+
dest.trailer.delete(:Encrypt)
|
723
|
+
dest
|
724
|
+
end
|
725
|
+
|
704
726
|
# :call-seq:
|
705
727
|
# doc.write(filename, incremental: false, validate: true, update_fields: true, optimize: false)
|
706
728
|
# doc.write(io, incremental: false, validate: true, update_fields: true, optimize: false)
|
data/lib/hexapdf/importer.rb
CHANGED
@@ -71,14 +71,18 @@ module HexaPDF
|
|
71
71
|
# Imports the given +object+ (belonging to the +source+ document) by completely copying it and
|
72
72
|
# all referenced objects into the +destination+ object.
|
73
73
|
#
|
74
|
+
# If the +allow_all+ argument is set to +true+, then the usually omitted catalog and page tree
|
75
|
+
# node objects (see the class description for details) are also copied which allows one to make
|
76
|
+
# an in-memory duplicate of a HexaPDF::Document object.
|
77
|
+
#
|
74
78
|
# Specifying +source+ is optionial if it can be determined through +object+.
|
75
79
|
#
|
76
80
|
# After the operation is finished, all state is discarded. This means that another call to this
|
77
81
|
# method for the same object will yield a new - and different - object. This is in contrast to
|
78
82
|
# using ::for together with #import which remembers and returns already imported objects (which
|
79
83
|
# is generally what one wants).
|
80
|
-
def self.copy(destination, object, source: nil)
|
81
|
-
new(NullableWeakRef.new(destination)).import(object, source: source)
|
84
|
+
def self.copy(destination, object, allow_all: false, source: nil)
|
85
|
+
new(NullableWeakRef.new(destination), allow_all: allow_all).import(object, source: source)
|
82
86
|
end
|
83
87
|
|
84
88
|
private_class_method :new
|
@@ -86,9 +90,10 @@ module HexaPDF
|
|
86
90
|
attr_reader :destination #:nodoc:
|
87
91
|
|
88
92
|
# Initializes a new importer that can import objects to the +destination+ document.
|
89
|
-
def initialize(destination)
|
93
|
+
def initialize(destination, allow_all: false)
|
90
94
|
@destination = destination
|
91
95
|
@mapper = {}
|
96
|
+
@allow_all = allow_all
|
92
97
|
end
|
93
98
|
|
94
99
|
SourceWrapper = Struct.new(:source) #:nodoc:
|
@@ -136,7 +141,7 @@ module HexaPDF
|
|
136
141
|
internal_import(wrapper.source.object(object), wrapper)
|
137
142
|
when HexaPDF::Object
|
138
143
|
wrapper.source ||= object.document
|
139
|
-
if object.type == :Catalog || object.type == :Pages
|
144
|
+
if !@allow_all && (object.type == :Catalog || object.type == :Pages)
|
140
145
|
@mapper[object.data] = nil
|
141
146
|
elsif (mapped_object = @mapper[object.data]&.__getobj__) && !mapped_object.null?
|
142
147
|
mapped_object
|
@@ -149,7 +154,12 @@ module HexaPDF
|
|
149
154
|
obj.data.gen = 0
|
150
155
|
@destination.add(obj) if object.indirect?
|
151
156
|
|
152
|
-
|
157
|
+
stream = obj.data.stream
|
158
|
+
if stream.kind_of?(String)
|
159
|
+
obj.data.stream = stream.dup
|
160
|
+
elsif stream&.source.kind_of?(FiberDoubleForString)
|
161
|
+
obj.data.stream = stream.fiber.resume.dup
|
162
|
+
end
|
153
163
|
obj.data.value = duplicate(obj.data.value, wrapper)
|
154
164
|
obj.data.value.update(duplicate(object.copy_inherited_values, wrapper)) if object.type == :Page
|
155
165
|
obj
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -69,6 +69,10 @@ module HexaPDF
|
|
69
69
|
# If the subclass supports the value :flow of the 'position' style property, this method
|
70
70
|
# needs to be overridden to return +true+.
|
71
71
|
#
|
72
|
+
# Additionally, if a box object uses flow positioning, #fit_result.x should be set to the
|
73
|
+
# correct value since Frame#fit can't determine this and uses Frame#left in the absence of a
|
74
|
+
# set value.
|
75
|
+
#
|
72
76
|
# #empty?::
|
73
77
|
# This method should return +true+ if the subclass won't draw anything when #draw is called.
|
74
78
|
#
|
@@ -89,28 +93,13 @@ module HexaPDF
|
|
89
93
|
# This method draws the box specific content and is called from #draw which already handles
|
90
94
|
# things like drawing the border and background. So #draw should usually not be overridden.
|
91
95
|
#
|
92
|
-
# This base class provides various
|
93
|
-
#
|
94
|
-
# +reserved_width+, +reserved_height+::
|
95
|
-
# Returns the width respectively the height of the reserved space inside the box that is
|
96
|
-
# used for the border and padding.
|
97
|
-
#
|
98
|
-
# +reserved_width_left+, +reserved_width_right+, +reserved_height_top+,
|
99
|
-
# +reserved_height_bottom+::
|
100
|
-
# Returns the reserved space inside the box at the specified edge (left, right, top,
|
101
|
-
# bottom).
|
102
|
-
#
|
103
|
-
# +update_content_width+, +update_content_height+::
|
104
|
-
# Takes a block that should return the content width respectively height and sets the box's
|
105
|
-
# width respectively height accordingly.
|
106
|
-
#
|
107
|
-
# +create_split_box+::
|
108
|
-
# Creates a new box based on this one and resets the internal data back to their original
|
109
|
-
# values.
|
96
|
+
# This base class also provides various protected helper methods for use in the above methods:
|
110
97
|
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
98
|
+
# * #reserved_width, #reserved_height
|
99
|
+
# * #reserved_width_left, #reserved_width_right, #reserved_height_top,
|
100
|
+
# #reserved_height_bottom
|
101
|
+
# * #update_content_width, #update_content_height
|
102
|
+
# * #create_split_box
|
114
103
|
class Box
|
115
104
|
|
116
105
|
include HexaPDF::Utils
|
@@ -352,7 +341,9 @@ module HexaPDF
|
|
352
341
|
available_height
|
353
342
|
end
|
354
343
|
return @fit_result if !position_flow && (float_compare(@width, available_width) > 0 ||
|
355
|
-
float_compare(@height, available_height) > 0
|
344
|
+
float_compare(@height, available_height) > 0 ||
|
345
|
+
@width - reserved_width < 0 ||
|
346
|
+
@height - reserved_height < 0)
|
356
347
|
|
357
348
|
fit_content(available_width, available_height, frame)
|
358
349
|
|
@@ -432,7 +423,7 @@ module HexaPDF
|
|
432
423
|
(style.overlays? && !style.overlays.none?))
|
433
424
|
end
|
434
425
|
|
435
|
-
|
426
|
+
protected
|
436
427
|
|
437
428
|
# Returns the width that is reserved by the padding and border style properties.
|
438
429
|
def reserved_width
|
@@ -480,12 +471,18 @@ module HexaPDF
|
|
480
471
|
result
|
481
472
|
end
|
482
473
|
|
474
|
+
# :call-seq:
|
475
|
+
# update_content_width { block }
|
476
|
+
#
|
483
477
|
# Updates the width of the box using the content width returned by the block.
|
484
478
|
def update_content_width
|
485
479
|
return if @initial_width > 0
|
486
480
|
@width = yield + reserved_width
|
487
481
|
end
|
488
482
|
|
483
|
+
# :call-seq:
|
484
|
+
# update_content_height { block }
|
485
|
+
#
|
489
486
|
# Updates the height of the box using the content height returned by the block.
|
490
487
|
def update_content_height
|
491
488
|
return if @initial_height > 0
|
@@ -494,13 +491,12 @@ module HexaPDF
|
|
494
491
|
|
495
492
|
# Fits the content of the box and returns whether fitting was successful.
|
496
493
|
#
|
497
|
-
# This is just a stub implementation that sets the #fit_result status to success
|
498
|
-
#
|
499
|
-
# specific behaviour.
|
494
|
+
# This is just a stub implementation that sets the #fit_result status to success. Subclasses
|
495
|
+
# should override it to provide the box specific behaviour.
|
500
496
|
#
|
501
497
|
# See #fit for details.
|
502
498
|
def fit_content(_available_width, _available_height, _frame)
|
503
|
-
fit_result.success!
|
499
|
+
fit_result.success!
|
504
500
|
end
|
505
501
|
|
506
502
|
# Splits the content of the box.
|
@@ -525,7 +521,8 @@ module HexaPDF
|
|
525
521
|
end
|
526
522
|
end
|
527
523
|
|
528
|
-
# Creates a new box based on this one and resets the data back to their original
|
524
|
+
# Creates a new box based on this one and resets the internal data back to their original
|
525
|
+
# values.
|
529
526
|
#
|
530
527
|
# The variable +@split_box+ is set to +split_box_value+ (defaults to +true+) to make the new
|
531
528
|
# box aware that it is a split box. If needed, subclasses can set the variable to other truthy
|
data/lib/hexapdf/layout/frame.rb
CHANGED
@@ -43,14 +43,12 @@ module HexaPDF
|
|
43
43
|
# An InlineBox wraps a regular Box so that it can be used as an item for a Line. This enables
|
44
44
|
# inline graphics.
|
45
45
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
46
|
+
# When an inline box gets placed on a line, the method #fit_wrapped_box is called to fit the
|
47
|
+
# wrapped box. This allows the wrapped box to correctly set its width and height which are
|
48
|
+
# needed by the TextLayouter algorithm.
|
49
49
|
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
# wrapped box and its height, or if not set, a practically infinite height. In the latter case
|
53
|
-
# the height *must* be set during fitting.
|
50
|
+
# Note: It is *mandatory* that the wrapped box sets its width and height without relying on the
|
51
|
+
# dimensions of the frame's current region.
|
54
52
|
class InlineBox
|
55
53
|
|
56
54
|
# Creates an InlineBox that wraps a basic Box. All arguments (except +valign+) and the block
|
@@ -74,7 +72,6 @@ module HexaPDF
|
|
74
72
|
# The +valign+ argument can be used to specify the vertical alignment of the box relative to
|
75
73
|
# other items in the Line.
|
76
74
|
def initialize(box, valign: :baseline)
|
77
|
-
raise HexaPDF::Error, "Width of box not set" if box.width == 0
|
78
75
|
@box = box
|
79
76
|
@valign = valign
|
80
77
|
end
|
@@ -102,8 +99,7 @@ module HexaPDF
|
|
102
99
|
# Draws the wrapped box. If the box has margins specified, the x and y offsets are correctly
|
103
100
|
# adjusted.
|
104
101
|
def draw(canvas, x, y)
|
105
|
-
|
106
|
-
y - @fit_result.y + box.style.margin.bottom) { @fit_result.draw(canvas) }
|
102
|
+
@fit_result.draw(canvas, dx: x, dy: y)
|
107
103
|
end
|
108
104
|
|
109
105
|
# The minimum x-coordinate which is always 0.
|
@@ -129,19 +125,17 @@ module HexaPDF
|
|
129
125
|
# Fits the wrapped box.
|
130
126
|
#
|
131
127
|
# If the +frame+ argument is +nil+, a custom frame is created. Otherwise the given +frame+ is
|
132
|
-
# used for the fitting operation.
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
@fit_result =
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
raise HexaPDF::Error, "Box for inline use has no valid height set after fitting"
|
144
|
-
end
|
128
|
+
# used for creating an appropriate child frame for the fitting operation.
|
129
|
+
#
|
130
|
+
# After this operation the caller is responsible for checking the actual width and height of
|
131
|
+
# the inline box and whether it really fits.
|
132
|
+
def fit_wrapped_box(frame = nil)
|
133
|
+
@fit_result = box.fit(100_000, 100_000, frame || Frame.new(0, 0, 100_000, 100_000))
|
134
|
+
margin = box.style.margin if box.style.margin?
|
135
|
+
@fit_result.x = margin&.left.to_i
|
136
|
+
@fit_result.y = margin&.bottom.to_i
|
137
|
+
@fit_result.mask = Geom2D::Rectangle(0, 0, @fit_result.x + box.width + margin&.right.to_i,
|
138
|
+
@fit_result.y + box.height + margin&.top.to_i)
|
145
139
|
end
|
146
140
|
|
147
141
|
end
|
@@ -243,7 +243,7 @@ module HexaPDF
|
|
243
243
|
break if !box_fitter.success? || height <= 0
|
244
244
|
end
|
245
245
|
|
246
|
-
|
246
|
+
update_content_height { @results.sum(&:height) + (@results.count - 1) * item_spacing }
|
247
247
|
|
248
248
|
if @results.size == @children.size && @results.all? {|r| r.box_fitter.success? }
|
249
249
|
fit_result.success!
|
@@ -316,37 +316,32 @@ module HexaPDF
|
|
316
316
|
def item_marker_box(document, index)
|
317
317
|
return @marker_type.call(document, self, index) if @marker_type.kind_of?(Proc)
|
318
318
|
|
319
|
-
unless (
|
319
|
+
unless (items = @item_marker_items)
|
320
320
|
marker_style = {
|
321
|
-
|
322
|
-
|
321
|
+
font_size: style.font_size || 10,
|
322
|
+
fill_color: style.fill_color,
|
323
323
|
}
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
TextFragment.create(text, marker_style)
|
344
|
-
else
|
345
|
-
raise HexaPDF::Error, "Unknown list marker type #{@marker_type.inspect}"
|
346
|
-
end
|
347
|
-
@item_marker_fragment = fragment unless @marker_type == :decimal
|
324
|
+
marker_style[:font] = style.font if style.font?
|
325
|
+
items = case @marker_type
|
326
|
+
when :disc
|
327
|
+
document.layout.text_fragments("•", style: marker_style)
|
328
|
+
when :circle
|
329
|
+
document.layout.text_fragments("❍", style: marker_style,
|
330
|
+
font_size: style.font_size / 2.0,
|
331
|
+
text_rise: -style.font_size / 1.8)
|
332
|
+
when :square
|
333
|
+
document.layout.text_fragments("■", style: marker_style,
|
334
|
+
font_size: style.font_size / 2.0,
|
335
|
+
text_rise: -style.font_size / 1.8)
|
336
|
+
when :decimal
|
337
|
+
text = (@start_number + index).to_s << "."
|
338
|
+
document.layout.text_fragments(text, style: marker_style)
|
339
|
+
else
|
340
|
+
raise HexaPDF::Error, "Unknown list marker type #{@marker_type.inspect}"
|
341
|
+
end
|
342
|
+
@item_marker_items = items unless @marker_type == :decimal
|
348
343
|
end
|
349
|
-
TextBox.new(items:
|
344
|
+
TextBox.new(items: items, style: {text_align: :right, padding: [0, 5, 0, 0]})
|
350
345
|
end
|
351
346
|
|
352
347
|
# Draws the list items onto the canvas at position [x, y].
|
@@ -41,7 +41,10 @@ require 'hexapdf/layout/frame'
|
|
41
41
|
module HexaPDF
|
42
42
|
module Layout
|
43
43
|
|
44
|
-
# A PageStyle defines the
|
44
|
+
# A PageStyle defines the dimensions of a page, its initial look, the Frame for object placement
|
45
|
+
# and which page style should be used next.
|
46
|
+
#
|
47
|
+
# This class is used by HexaPDF::Composer to style the individual pages.
|
45
48
|
class PageStyle
|
46
49
|
|
47
50
|
# The page size.
|
@@ -65,14 +68,15 @@ module HexaPDF
|
|
65
68
|
# The callable object is given a canvas and the page style as arguments. It needs to draw the
|
66
69
|
# initial content of the page. Note that the graphics state of the canvas is *not* saved
|
67
70
|
# before executing the template code and restored afterwards. If this is needed, the object
|
68
|
-
# needs to do it itself.
|
71
|
+
# needs to do it itself. The #next_style attribute can optionally be set.
|
69
72
|
#
|
70
|
-
# Furthermore
|
71
|
-
#
|
73
|
+
# Furthermore, the callable object should set the #frame that defines the area on the page
|
74
|
+
# where content should be placed. The #create_frame method can be used for easily creating a
|
75
|
+
# rectangular frame.
|
72
76
|
#
|
73
77
|
# Example:
|
74
78
|
#
|
75
|
-
# page_style.template = lambda do |canvas, style
|
79
|
+
# page_style.template = lambda do |canvas, style|
|
76
80
|
# box = canvas.context.box
|
77
81
|
# canvas.fill_color("fd0") do
|
78
82
|
# canvas.rectangle(0, 0, box.width, box.height).fill
|
@@ -81,22 +85,24 @@ module HexaPDF
|
|
81
85
|
# end
|
82
86
|
attr_accessor :template
|
83
87
|
|
84
|
-
# The
|
85
|
-
# placed.
|
88
|
+
# The Frame object that defines the area for the last page created with #create_page where
|
89
|
+
# content should be placed.
|
86
90
|
#
|
87
|
-
# This
|
88
|
-
#
|
89
|
-
#
|
90
|
-
# is set during #create_page.
|
91
|
+
# This value is usually updated during execution of the #template. If the value is not
|
92
|
+
# updated, a frame covering the page except for a default margin on all sides is set during
|
93
|
+
# #create_page.
|
91
94
|
attr_accessor :frame
|
92
95
|
|
93
96
|
# Defines the name of the page style that should be used for the next page.
|
94
97
|
#
|
98
|
+
# Note that this value can be different each time a new page is created via #create_page.
|
99
|
+
#
|
95
100
|
# If this attribute is +nil+ (the default), it means that this style should be used again.
|
96
101
|
attr_accessor :next_style
|
97
102
|
|
98
103
|
# 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
|
104
|
+
# values. If a block is given, it is used as #template for defining the initial content of a
|
105
|
+
# page.
|
100
106
|
#
|
101
107
|
# Example:
|
102
108
|
#
|
@@ -113,14 +119,15 @@ module HexaPDF
|
|
113
119
|
@next_style = next_style
|
114
120
|
end
|
115
121
|
|
116
|
-
# Creates a new page in the given document
|
122
|
+
# Creates a new page in the given document using this page style and returns it.
|
117
123
|
#
|
118
|
-
# If #frame has not
|
119
|
-
#
|
124
|
+
# If the #frame has not changed during execution of the #template, a default frame covering
|
125
|
+
# the whole page except a margin of 36 is assigned.
|
120
126
|
def create_page(document)
|
127
|
+
frame_before = @frame
|
121
128
|
page = document.pages.create(media_box: page_size, orientation: orientation)
|
122
129
|
template&.call(page.canvas, self)
|
123
|
-
self.frame
|
130
|
+
self.frame = create_frame(page, 36) if @frame.equal?(frame_before)
|
124
131
|
page
|
125
132
|
end
|
126
133
|
|
data/lib/hexapdf/layout/style.rb
CHANGED
@@ -1369,8 +1369,8 @@ module HexaPDF
|
|
1369
1369
|
# composer.text('Text underneath')
|
1370
1370
|
#
|
1371
1371
|
# :fill_frame_horizontal::
|
1372
|
-
# The mask covers the box including the margin around the box
|
1373
|
-
#
|
1372
|
+
# The mask covers the box including the margin around the box, the space to the left and
|
1373
|
+
# right in the frame and the space to the top of the current region.
|
1374
1374
|
#
|
1375
1375
|
# Examples:
|
1376
1376
|
#
|
@@ -106,11 +106,6 @@ module HexaPDF
|
|
106
106
|
true
|
107
107
|
end
|
108
108
|
|
109
|
-
# :nodoc:
|
110
|
-
def draw(canvas, x, y)
|
111
|
-
super(canvas, x + @x_offset, y)
|
112
|
-
end
|
113
|
-
|
114
109
|
# :nodoc:
|
115
110
|
def empty?
|
116
111
|
super && (!@result || @result.lines.empty?)
|
@@ -137,7 +132,8 @@ module HexaPDF
|
|
137
132
|
min_x = [min_x, line.x_offset].min
|
138
133
|
max_x = [max_x, line.x_offset + line.width].max
|
139
134
|
end
|
140
|
-
@width = (min_x.finite? ?
|
135
|
+
@width = (min_x.finite? ? max_x - min_x : 0) + reserved_width
|
136
|
+
fit_result.x = @x_offset = min_x
|
141
137
|
@height = @initial_height > 0 ? @initial_height : @result.height + reserved_height
|
142
138
|
else
|
143
139
|
@result = @tl.fit(@items, @width - reserved_width, @height - reserved_height,
|
data/lib/hexapdf/parser.rb
CHANGED
@@ -71,6 +71,10 @@ module HexaPDF
|
|
71
71
|
end
|
72
72
|
|
73
73
|
# Returns +true+ if the PDF file is a linearized file.
|
74
|
+
#
|
75
|
+
# Note: The method uses heuristics to determine whether a PDF file is linearized. In case of
|
76
|
+
# slightly invalid or damaged PDFs that HexaPDF can recover from it is possible that this method
|
77
|
+
# returns +true+ even though the PDF isn't actually linearized.
|
74
78
|
def linearized?
|
75
79
|
@linearized ||=
|
76
80
|
begin
|
@@ -293,7 +297,7 @@ module HexaPDF
|
|
293
297
|
next
|
294
298
|
elsif type == 'n'
|
295
299
|
if pos == 0 || gen > 65535
|
296
|
-
maybe_raise("Invalid in use cross-reference entry",
|
300
|
+
maybe_raise("Invalid in use cross-reference entry for object number #{oid}",
|
297
301
|
pos: @tokenizer.pos)
|
298
302
|
xref.add_free_entry(oid, gen)
|
299
303
|
else
|
data/lib/hexapdf/revisions.rb
CHANGED
@@ -97,7 +97,7 @@ module HexaPDF
|
|
97
97
|
merge_revision = offset
|
98
98
|
end
|
99
99
|
|
100
|
-
if merge_revision == offset
|
100
|
+
if merge_revision == offset && !revisions.empty?
|
101
101
|
xref_section.merge!(revisions.first.xref_section)
|
102
102
|
offset = trailer[:Prev] # Get possible next offset before overwriting trailer
|
103
103
|
trailer = revisions.first.trailer
|
data/lib/hexapdf/stream.rb
CHANGED
@@ -51,6 +51,9 @@ module HexaPDF
|
|
51
51
|
# normalized to arrays on assignment to ease further processing.
|
52
52
|
class StreamData
|
53
53
|
|
54
|
+
# The source.
|
55
|
+
attr_reader :source
|
56
|
+
|
54
57
|
# The filter(s) that need to be applied for getting the decoded stream data.
|
55
58
|
attr_reader :filter
|
56
59
|
|
@@ -110,9 +113,6 @@ module HexaPDF
|
|
110
113
|
|
111
114
|
protected
|
112
115
|
|
113
|
-
# The source.
|
114
|
-
attr_reader :source
|
115
|
-
|
116
116
|
# The optional offset into the bytes provided by source.
|
117
117
|
attr_reader :offset
|
118
118
|
|
data/lib/hexapdf/tokenizer.rb
CHANGED
@@ -82,6 +82,7 @@ module HexaPDF
|
|
82
82
|
# correctable situations are only raised if the return value of calling the object is +true+.
|
83
83
|
def initialize(io, on_correctable_error: nil)
|
84
84
|
@io = io
|
85
|
+
@io_chunk = String.new(''.b)
|
85
86
|
@ss = StringScanner.new(''.b)
|
86
87
|
@original_pos = -1
|
87
88
|
@on_correctable_error = on_correctable_error || proc { false }
|
@@ -439,9 +440,9 @@ module HexaPDF
|
|
439
440
|
@io.seek(@next_read_pos)
|
440
441
|
return false if @io.eof?
|
441
442
|
|
442
|
-
@ss << @io.read(8192)
|
443
|
+
@ss << @io.read(8192, @io_chunk)
|
443
444
|
if @ss.pos > 8192 && @ss.string.length > 16384
|
444
|
-
@ss.string.
|
445
|
+
@ss.string.replace(@ss.string.byteslice(8192..-1))
|
445
446
|
@ss.pos -= 8192
|
446
447
|
@original_pos += 8192
|
447
448
|
end
|
@@ -65,6 +65,8 @@ module HexaPDF
|
|
65
65
|
#
|
66
66
|
# == Type Specific Field Flags
|
67
67
|
#
|
68
|
+
# See the class description for Field for the general field flags.
|
69
|
+
#
|
68
70
|
# :no_toggle_to_off:: Only used with radio buttons fields. If this flag is set, one button
|
69
71
|
# needs to be selected at all times. Otherwise, clicking on the selected
|
70
72
|
# button deselects it.
|
@@ -51,6 +51,8 @@ module HexaPDF
|
|
51
51
|
#
|
52
52
|
# == Type Specific Field Flags
|
53
53
|
#
|
54
|
+
# See the class description for Field for the general field flags.
|
55
|
+
#
|
54
56
|
# :combo:: If set, the field represents a combo box.
|
55
57
|
#
|
56
58
|
# :edit:: If set, the combo box includes an editable text box for entering arbitrary values.
|
@@ -76,6 +76,8 @@ module HexaPDF
|
|
76
76
|
#
|
77
77
|
# :no_export:: The field should *not* be exported by a submit-form action.
|
78
78
|
#
|
79
|
+
# Also see the class description of the subclasses for additional, type specific field flags.
|
80
|
+
#
|
79
81
|
# == Field Type Implementation Notes
|
80
82
|
#
|
81
83
|
# If an AcroForm field type adds additional inheritable dictionary fields, it has to set the
|
@@ -124,6 +126,8 @@ module HexaPDF
|
|
124
126
|
#
|
125
127
|
# Returns an array of flag names representing the set bit flags.
|
126
128
|
#
|
129
|
+
# See the class description for a list of available flags.
|
130
|
+
#
|
127
131
|
|
128
132
|
##
|
129
133
|
# :method: flagged?
|
@@ -133,6 +137,8 @@ module HexaPDF
|
|
133
137
|
# Returns +true+ if the given flag is set. The argument can either be the flag name or the
|
134
138
|
# bit index.
|
135
139
|
#
|
140
|
+
# See the class description for a list of available flags.
|
141
|
+
#
|
136
142
|
|
137
143
|
##
|
138
144
|
# :method: flag
|
@@ -142,6 +148,8 @@ module HexaPDF
|
|
142
148
|
# Sets the given flags, given as flag names or bit indices. If +clear_existing+ is +true+,
|
143
149
|
# all prior flags will be cleared.
|
144
150
|
#
|
151
|
+
# See the class description for a list of available flags.
|
152
|
+
#
|
145
153
|
bit_field(:flags, {read_only: 0, required: 1, no_export: 2},
|
146
154
|
lister: "flags", getter: "flagged?", setter: "flag", unsetter: "unflag",
|
147
155
|
value_getter: "self[:Ff]", value_setter: "self[:Ff]")
|
@@ -388,13 +388,14 @@ module HexaPDF
|
|
388
388
|
page_annots = page[:Annots].to_a - to_delete
|
389
389
|
page[:Annots].value.replace(page_annots)
|
390
390
|
end
|
391
|
-
to_delete.each {|widget| document.delete(widget) }
|
392
391
|
|
393
392
|
if field[:Parent]
|
394
393
|
field[:Parent][:Kids].delete(field)
|
395
394
|
else
|
396
395
|
self[:Fields].delete(field)
|
397
396
|
end
|
397
|
+
|
398
|
+
to_delete.each {|widget| document.delete(widget) }
|
398
399
|
document.delete(field)
|
399
400
|
end
|
400
401
|
|
@@ -50,6 +50,8 @@ module HexaPDF
|
|
50
50
|
#
|
51
51
|
# == Type Specific Field Flags
|
52
52
|
#
|
53
|
+
# See the class description for Field for the general field flags.
|
54
|
+
#
|
53
55
|
# :multiline:: If set, the text field may contain multiple lines.
|
54
56
|
#
|
55
57
|
# :password:: The field is a password field. This changes the behaviour of the PDF reader
|