hexapdf 0.45.0 → 0.47.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 +120 -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 +19 -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/encryption/standard_security_handler.rb +32 -26
- 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/table_box.rb +57 -10
- 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/task/optimize.rb +4 -4
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +8 -4
- 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 +10 -6
- data/lib/hexapdf/type/acro_form/signature_field.rb +2 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +11 -3
- data/lib/hexapdf/type/annotations/widget.rb +4 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf +44 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf +0 -0
- 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/digital_signature/test_signatures.rb +4 -4
- data/test/hexapdf/document/test_layout.rb +28 -5
- data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -2
- 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_table_box.rb +52 -0
- data/test/hexapdf/layout/test_text_box.rb +3 -9
- data/test/hexapdf/layout/test_text_layouter.rb +0 -3
- data/test/hexapdf/task/test_optimize.rb +2 -0
- data/test/hexapdf/test_document.rb +30 -3
- data/test/hexapdf/test_importer.rb +24 -0
- data/test/hexapdf/test_revisions.rb +54 -41
- data/test/hexapdf/test_writer.rb +11 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +22 -5
- data/test/hexapdf/type/acro_form/test_form.rb +9 -5
- data/test/hexapdf/type/acro_form/test_signature_field.rb +3 -1
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +14 -1
- data/test/hexapdf/type/annotations/test_widget.rb +4 -0
- metadata +6 -2
@@ -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
|
#
|
@@ -382,7 +382,14 @@ module HexaPDF
|
|
382
382
|
def fit_rows(start_row, available_height, column_info, frame)
|
383
383
|
height = available_height
|
384
384
|
last_fitted_row_index = -1
|
385
|
+
row_heights = {}
|
386
|
+
zero_height_rows = {}
|
387
|
+
row_spans = []
|
388
|
+
|
385
389
|
@cells[start_row..-1].each.with_index(start_row) do |columns, row_index|
|
390
|
+
# 1. Fit all columns of the row and record the max height of all non-row-span cells. If
|
391
|
+
# a row has zero height (usually because it only has row-span cells), record that
|
392
|
+
# information. Additionally store all cells with row-spans.
|
386
393
|
row_fit = true
|
387
394
|
row_height = 0
|
388
395
|
columns.each_with_index do |cell, col_index|
|
@@ -396,27 +403,67 @@ module HexaPDF
|
|
396
403
|
row_fit = false
|
397
404
|
break
|
398
405
|
end
|
399
|
-
cell.
|
400
|
-
|
401
|
-
|
406
|
+
if row_height < cell.preferred_height && cell.row_span == 1
|
407
|
+
row_height = cell.preferred_height
|
408
|
+
end
|
409
|
+
row_spans << cell if cell.row_span > 1
|
402
410
|
end
|
403
411
|
|
404
|
-
if
|
405
|
-
seen = {}
|
406
|
-
columns.each do |cell|
|
407
|
-
next if seen[cell]
|
408
|
-
cell.update_height(cell.row == row_index ? row_height : cell.height + row_height)
|
409
|
-
seen[cell] = true
|
410
|
-
end
|
412
|
+
zero_height_rows[row_index] = true if row_height == 0
|
411
413
|
|
414
|
+
if row_fit
|
415
|
+
# 2. If all cells of the row fit, we subtract the recorded row height of the
|
416
|
+
# non-row-span cells from the available height for the next pass.
|
412
417
|
last_fitted_row_index = row_index
|
418
|
+
row_heights[row_index] = row_height
|
413
419
|
available_height -= row_height
|
420
|
+
|
421
|
+
# 3. We look at all row-span cells that end at the current row index. If the row-span
|
422
|
+
# cell is larger than the sum of the row heights, we proportionally enlarge the
|
423
|
+
# stored height of each spanned row and subtract the difference from the available
|
424
|
+
# height for the next pass. If the row span contains initially zero-height rows,
|
425
|
+
# only those rows are enlarged. Row-span cells themselves are not updated at this
|
426
|
+
# point!
|
427
|
+
row_spans.each do |cell|
|
428
|
+
upper_row_index = cell.row + cell.row_span - 1
|
429
|
+
next unless upper_row_index == row_index
|
430
|
+
|
431
|
+
rows = cell.row.upto(upper_row_index)
|
432
|
+
row_span_height = rows.sum {|ri| row_heights[ri] }
|
433
|
+
if row_span_height < cell.preferred_height
|
434
|
+
zero_height_rows_in_span = rows.select {|ri| zero_height_rows[ri] }
|
435
|
+
rows = zero_height_rows_in_span if zero_height_rows_in_span.size > 0
|
436
|
+
adjustment = (cell.preferred_height - row_span_height) / rows.size.to_f
|
437
|
+
rows.each {|ri| row_heights[ri] += adjustment }
|
438
|
+
available_height -= cell.preferred_height - row_span_height
|
439
|
+
end
|
440
|
+
end
|
414
441
|
else
|
415
442
|
last_fitted_row_index = columns.min_by(&:row).row - 1 if height != available_height
|
416
443
|
break
|
417
444
|
end
|
418
445
|
end
|
419
446
|
|
447
|
+
if last_fitted_row_index >= 0
|
448
|
+
# 4. Once all possible rows have been fitted and the heights of the rows are fixed, the
|
449
|
+
# final height and top-left corner of each cell needs to be set.
|
450
|
+
running_height = 0
|
451
|
+
@cells[start_row..last_fitted_row_index].each.with_index(start_row) do |columns, row_index|
|
452
|
+
columns.each_with_index do |cell, col_index|
|
453
|
+
next if cell.row != row_index || cell.column != col_index
|
454
|
+
cell.left = column_info[cell.column].first
|
455
|
+
cell.top = running_height
|
456
|
+
if cell.row_span == 1
|
457
|
+
cell.update_height(row_heights[row_index])
|
458
|
+
else
|
459
|
+
new_height = cell.row.upto(cell.row + cell.row_span - 1).sum {|ri| row_heights[ri] }
|
460
|
+
cell.update_height(new_height)
|
461
|
+
end
|
462
|
+
end
|
463
|
+
running_height += row_heights[row_index]
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
420
467
|
[height - available_height, last_fitted_row_index < start_row ? -1 : last_fitted_row_index]
|
421
468
|
end
|
422
469
|
|
@@ -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
|
|
@@ -214,13 +214,13 @@ module HexaPDF
|
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
217
|
-
# Deletes field entries of the object that are optional and currently set
|
218
|
-
# value.
|
217
|
+
# Deletes field entries (except for /Type) of the object that are optional and currently set
|
218
|
+
# to their default value.
|
219
219
|
def self.delete_fields_with_defaults(obj)
|
220
220
|
return unless obj.kind_of?(HexaPDF::Dictionary) && !obj.null?
|
221
221
|
obj.each do |name, value|
|
222
|
-
if (field = obj.class.field(name)) && !field.required? &&
|
223
|
-
|
222
|
+
if name != :Type && (field = obj.class.field(name)) && !field.required? &&
|
223
|
+
field.default? && value == field.default
|
224
224
|
obj.delete(name)
|
225
225
|
end
|
226
226
|
end
|
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
|
@@ -134,11 +134,12 @@ module HexaPDF
|
|
134
134
|
if !normal_appearance.kind_of?(HexaPDF::Dictionary) || normal_appearance.kind_of?(HexaPDF::Stream)
|
135
135
|
(@widget[:AP] ||= {})[:N] = {Off: nil}
|
136
136
|
normal_appearance = @widget[:AP][:N]
|
137
|
-
normal_appearance[@field
|
137
|
+
normal_appearance[@field.field_value&.to_sym || :Yes] = nil
|
138
138
|
end
|
139
139
|
on_name = (normal_appearance.value.keys - [:Off]).first
|
140
140
|
unless on_name
|
141
|
-
|
141
|
+
on_name = @field.field_value&.to_sym || :Yes
|
142
|
+
normal_appearance[on_name] = nil
|
142
143
|
end
|
143
144
|
|
144
145
|
@widget[:AS] = (@field[:V] == on_name ? on_name : :Off)
|
@@ -226,8 +227,11 @@ module HexaPDF
|
|
226
227
|
|
227
228
|
form = (@widget[:AP] ||= {})[:N] ||= @document.add({Type: :XObject, Subtype: :Form})
|
228
229
|
# Wrap existing object in Form class in case the PDF writer didn't include the /Subtype
|
229
|
-
# key; we can do this since we know this has to be a
|
230
|
-
|
230
|
+
# key or the type of the object is wrong; we can do this since we know this has to be a
|
231
|
+
# Form object
|
232
|
+
unless form.type == :XObject && form[:Subtype] == :Form
|
233
|
+
form = @document.wrap(form, type: :XObject, subtype: :Form)
|
234
|
+
end
|
231
235
|
form.value.replace({Type: :XObject, Subtype: :Form, BBox: [0, 0, width, height],
|
232
236
|
Matrix: matrix, Resources: HexaPDF::Object.deep_copy(default_resources)})
|
233
237
|
form.contents = ''
|
@@ -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]")
|
@@ -186,9 +186,14 @@ module HexaPDF
|
|
186
186
|
# or +font_color+ is specified but +font+ isn't, the font Helvetica is used.
|
187
187
|
#
|
188
188
|
# If no font is set on the text field, the default font properties of the AcroForm form
|
189
|
-
# are used. Note that field specific or form specific font properties have to be
|
190
|
-
# Otherwise there
|
191
|
-
#
|
189
|
+
# are used. Note that field specific or form specific font properties have to be
|
190
|
+
# set. Otherwise there might be problems when creating a visual appearance with other
|
191
|
+
# PDF libraries/viewers.
|
192
|
+
#
|
193
|
+
# If HexaPDF is used to create a visual appearance of the field value and neither field
|
194
|
+
# specific nor form specific font properties are available, the configuration option
|
195
|
+
# 'acro_form.fallback_default_appearance' defines whether and which field specific font
|
196
|
+
# properties are set and used.
|
192
197
|
#
|
193
198
|
# +font_options+::
|
194
199
|
# A hash with font options like :variant that should be used.
|
@@ -388,13 +393,14 @@ module HexaPDF
|
|
388
393
|
page_annots = page[:Annots].to_a - to_delete
|
389
394
|
page[:Annots].value.replace(page_annots)
|
390
395
|
end
|
391
|
-
to_delete.each {|widget| document.delete(widget) }
|
392
396
|
|
393
397
|
if field[:Parent]
|
394
398
|
field[:Parent][:Kids].delete(field)
|
395
399
|
else
|
396
400
|
self[:Fields].delete(field)
|
397
401
|
end
|
402
|
+
|
403
|
+
to_delete.each {|widget| document.delete(widget) }
|
398
404
|
document.delete(field)
|
399
405
|
end
|
400
406
|
|
@@ -624,8 +630,6 @@ module HexaPDF
|
|
624
630
|
if font_name && !(self[:DR][:Font] && self[:DR][:Font][font_name])
|
625
631
|
yield("The font specified in /DA is not in the /DR resource dictionary")
|
626
632
|
end
|
627
|
-
else
|
628
|
-
set_default_appearance_string
|
629
633
|
end
|
630
634
|
|
631
635
|
create_appearances if document.config['acro_form.create_appearances']
|
@@ -199,7 +199,8 @@ module HexaPDF
|
|
199
199
|
|
200
200
|
# Returns the associated signature dictionary or +nil+ if the signature is not filled in.
|
201
201
|
def field_value
|
202
|
-
self[:V]
|
202
|
+
val = self[:V]
|
203
|
+
val.instance_of?(Dictionary) ? document.wrap(val, type: :Sig) : val
|
203
204
|
end
|
204
205
|
|
205
206
|
# Sets the signature dictionary as value of this signature field.
|
@@ -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
|
@@ -106,7 +106,7 @@ module HexaPDF
|
|
106
106
|
font_params[2] = HexaPDF::Content::ColorSpace.prenormalized_device_color(params)
|
107
107
|
end
|
108
108
|
end
|
109
|
-
HexaPDF::Content::Parser.parse(appearance_string.sub(/\/\//, '/'), &block)
|
109
|
+
HexaPDF::Content::Parser.parse(appearance_string.to_s.sub(/\/\//, '/'), &block)
|
110
110
|
block_given? ? nil : font_params
|
111
111
|
end
|
112
112
|
|
@@ -154,13 +154,21 @@ module HexaPDF
|
|
154
154
|
# font_size, font_color].
|
155
155
|
#
|
156
156
|
# The default appearance string is taken from the given +widget+ of the field, falls back to
|
157
|
-
# the field itself
|
157
|
+
# the field itself and then the default appearance string of the form. If it still not
|
158
|
+
# available, a standard default appearance string is set (see
|
159
|
+
# #set_default_appearance_string) and used.
|
158
160
|
#
|
159
161
|
# The reason why a specific widget of the field can be specified is because the widgets of a
|
160
162
|
# field might differ in their visual representation.
|
161
163
|
def parse_default_appearance_string(widget = self)
|
162
164
|
da = widget[:DA] || self[:DA] || (document.acro_form && document.acro_form[:DA])
|
163
|
-
|
165
|
+
unless da
|
166
|
+
if (args = document.config['acro_form.fallback_default_appearance'])
|
167
|
+
da = set_default_appearance_string(**args)
|
168
|
+
else
|
169
|
+
raise HexaPDF::Error, "No default appearance string set"
|
170
|
+
end
|
171
|
+
end
|
164
172
|
self.class.parse_appearance_string(da)
|
165
173
|
end
|
166
174
|
|
@@ -259,7 +259,7 @@ module HexaPDF
|
|
259
259
|
# auto-sized checkmark (i.e. :check for for check boxes) or circle (:circle for radio
|
260
260
|
# buttons). This also means that multiple invocations will reset *all* prior values.
|
261
261
|
#
|
262
|
-
# Note: The marker is called "normal caption" in the PDF
|
262
|
+
# Note: The marker is called "normal caption" in the PDF 2.0 spec and the /CA entry of the
|
263
263
|
# associated appearance characteristics dictionary. The marker size and color are set using
|
264
264
|
# the /DA key on the widget (although /DA is not defined for widget, this is how Acrobat
|
265
265
|
# does it).
|
@@ -305,7 +305,9 @@ module HexaPDF
|
|
305
305
|
size = 0
|
306
306
|
color = HexaPDF::Content::ColorSpace.prenormalized_device_color([0])
|
307
307
|
if (da = self[:DA] || field[:DA])
|
308
|
-
_,
|
308
|
+
_, da_size, da_color = AcroForm::VariableTextField.parse_appearance_string(da)
|
309
|
+
size = da_size || size
|
310
|
+
color = da_color || color
|
309
311
|
end
|
310
312
|
|
311
313
|
MarkerStyle.new(style, size, color)
|
data/lib/hexapdf/version.rb
CHANGED
data/lib/hexapdf/writer.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
%PDF-1.7
|
2
|
+
%����
|
3
|
+
1 0 obj
|
4
|
+
<< /Extensions << /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> /Pages 3 0 R /Type /Catalog >>
|
5
|
+
endobj
|
6
|
+
2 0 obj
|
7
|
+
<< /ModDate <aa1d1637459ea17c7fc9709f0c0c7b64761ff74e905b3765f33425776aa3010163cd5e898cfa7c5fc16159359375c323> >>
|
8
|
+
endobj
|
9
|
+
3 0 obj
|
10
|
+
<< /Count 1 /Kids [ 4 0 R ] /MediaBox [ 0 0 612 446 ] /Type /Pages >>
|
11
|
+
endobj
|
12
|
+
4 0 obj
|
13
|
+
<< /Contents 5 0 R /Parent 3 0 R /Resources 6 0 R /Type /Page >>
|
14
|
+
endobj
|
15
|
+
5 0 obj
|
16
|
+
<< /Length 80 /Filter /FlateDecode >>
|
17
|
+
stream
|
18
|
+
J<~S�)��9�M�$S�z��g�j���r�.R�"PO���_���X���^�GP�&z�g�*$�2SΈ�z�Kl��5ĩendstream
|
19
|
+
endobj
|
20
|
+
6 0 obj
|
21
|
+
<< /Font << /F1 7 0 R >> /ProcSet [ /PDF /Text ] >>
|
22
|
+
endobj
|
23
|
+
7 0 obj
|
24
|
+
<< /BaseFont /Helvetica /Name /F1 /Subtype /Type1 /Type /Font >>
|
25
|
+
endobj
|
26
|
+
8 0 obj
|
27
|
+
<< /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >> /Filter /Standard /Length 256 /O <c60934c0925e8d3ceebd0609102d9407dcf22d7fb3d87d030fce633af6d2ed99c1c3382bee5e8afc0d55ff8bca441d48> /OE <3fa2e3ecf34ddcb38b44af372a43268dda32111f58dc79da74d960b8fa206ead> /P -4 /Perms <b6c6ab65529f0a3322f03909e8a5547a> /R 5 /StmF /StdCF /StrF /StdCF /U <18fbd94777c28531c495a3116d273de9f8f3ed338b31c07687ca0ba06812842843765a66484d098194bcef0f9ecaaace> /UE <496a81bbb3207dfd12ddca4c16b60a411c6a18e638205824295e09afde826b4a> /V 5 >>
|
28
|
+
endobj
|
29
|
+
xref
|
30
|
+
0 9
|
31
|
+
0000000000 65535 f
|
32
|
+
0000000015 00000 n
|
33
|
+
0000000130 00000 n
|
34
|
+
0000000259 00000 n
|
35
|
+
0000000344 00000 n
|
36
|
+
0000000424 00000 n
|
37
|
+
0000000574 00000 n
|
38
|
+
0000000641 00000 n
|
39
|
+
0000000721 00000 n
|
40
|
+
trailer << /Info 2 0 R /Root 1 0 R /Size 9 /ID [<6790ffa610024e78369114311fc0df96><ed6c0810cb0d19599ac62042a0487749>] /Encrypt 8 0 R >>
|
41
|
+
startxref
|
42
|
+
1268
|
43
|
+
%%EOF
|