hexapdf 0.14.2 → 0.15.2

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +96 -0
  3. data/lib/hexapdf/cli/form.rb +30 -8
  4. data/lib/hexapdf/configuration.rb +19 -4
  5. data/lib/hexapdf/content/canvas.rb +1 -0
  6. data/lib/hexapdf/dictionary.rb +3 -0
  7. data/lib/hexapdf/dictionary_fields.rb +1 -1
  8. data/lib/hexapdf/encryption/security_handler.rb +7 -2
  9. data/lib/hexapdf/encryption/standard_security_handler.rb +12 -0
  10. data/lib/hexapdf/error.rb +4 -3
  11. data/lib/hexapdf/filter.rb +1 -0
  12. data/lib/hexapdf/filter/crypt.rb +60 -0
  13. data/lib/hexapdf/font/true_type/subsetter.rb +5 -1
  14. data/lib/hexapdf/font/type1/afm_parser.rb +2 -1
  15. data/lib/hexapdf/parser.rb +46 -14
  16. data/lib/hexapdf/pdf_array.rb +3 -0
  17. data/lib/hexapdf/revision.rb +16 -0
  18. data/lib/hexapdf/serializer.rb +10 -3
  19. data/lib/hexapdf/tokenizer.rb +44 -3
  20. data/lib/hexapdf/type/acro_form.rb +1 -0
  21. data/lib/hexapdf/type/acro_form/appearance_generator.rb +32 -17
  22. data/lib/hexapdf/type/acro_form/button_field.rb +8 -4
  23. data/lib/hexapdf/type/acro_form/field.rb +1 -0
  24. data/lib/hexapdf/type/acro_form/form.rb +37 -0
  25. data/lib/hexapdf/type/acro_form/signature_field.rb +223 -0
  26. data/lib/hexapdf/type/annotation.rb +13 -9
  27. data/lib/hexapdf/type/annotations/widget.rb +3 -1
  28. data/lib/hexapdf/type/font_descriptor.rb +9 -2
  29. data/lib/hexapdf/type/page.rb +81 -0
  30. data/lib/hexapdf/type/resources.rb +4 -0
  31. data/lib/hexapdf/type/xref_stream.rb +7 -0
  32. data/lib/hexapdf/utils/graphics_helpers.rb +4 -4
  33. data/lib/hexapdf/version.rb +1 -1
  34. data/test/hexapdf/content/test_canvas.rb +21 -0
  35. data/test/hexapdf/encryption/test_security_handler.rb +15 -0
  36. data/test/hexapdf/encryption/test_standard_security_handler.rb +26 -0
  37. data/test/hexapdf/filter/test_crypt.rb +21 -0
  38. data/test/hexapdf/font/true_type/test_subsetter.rb +2 -0
  39. data/test/hexapdf/font/type1/test_afm_parser.rb +5 -0
  40. data/test/hexapdf/test_dictionary_fields.rb +7 -0
  41. data/test/hexapdf/test_parser.rb +82 -2
  42. data/test/hexapdf/test_revision.rb +21 -0
  43. data/test/hexapdf/test_serializer.rb +10 -0
  44. data/test/hexapdf/test_tokenizer.rb +50 -0
  45. data/test/hexapdf/test_writer.rb +2 -2
  46. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +24 -3
  47. data/test/hexapdf/type/acro_form/test_button_field.rb +13 -7
  48. data/test/hexapdf/type/acro_form/test_field.rb +5 -0
  49. data/test/hexapdf/type/acro_form/test_form.rb +46 -2
  50. data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
  51. data/test/hexapdf/type/annotations/test_widget.rb +2 -0
  52. data/test/hexapdf/type/test_annotation.rb +20 -10
  53. data/test/hexapdf/type/test_font_descriptor.rb +7 -0
  54. data/test/hexapdf/type/test_page.rb +187 -49
  55. data/test/hexapdf/type/test_resources.rb +6 -0
  56. data/test/hexapdf/type/test_xref_stream.rb +7 -0
  57. data/test/hexapdf/utils/test_graphics_helpers.rb +8 -0
  58. metadata +6 -2
@@ -65,6 +65,9 @@ module HexaPDF
65
65
  # * Returns the native Ruby object for values with class HexaPDF::Object. However, all
66
66
  # subclasses of HexaPDF::Object are returned as is (it makes no sense, for example, to return
67
67
  # the hash that describes the Catalog instead of the Catalog object).
68
+ #
69
+ # Note: Hash or Array values will always be returned as-is, i.e. not wrapped with Dictionary or
70
+ # PDFArray.
68
71
  def [](arg1, arg2 = nil)
69
72
  data = arg2 ? value[arg1, arg2] : value[arg1]
70
73
  return if data.nil?
@@ -158,6 +158,22 @@ module HexaPDF
158
158
  add_without_check(obj)
159
159
  end
160
160
 
161
+ # :call-seq:
162
+ # revision.update(obj) -> obj or nil
163
+ #
164
+ # Updates the stored object to point to the given HexaPDF::Object wrapper, returning the object
165
+ # if successful or +nil+ otherwise.
166
+ #
167
+ # If +obj+ isn't stored in this revision or the stored object doesn't contain the same
168
+ # HexaPDF::PDFData object as the given object, nothing is done.
169
+ #
170
+ # This method should only be used if the wrong wrapper class is stored (e.g. because
171
+ # auto-detection didn't or couldn't work correctly) and thus needs correction.
172
+ def update(obj)
173
+ return nil if object(obj)&.data != obj.data
174
+ add_without_check(obj)
175
+ end
176
+
161
177
  # :call-seq:
162
178
  # revision.delete(ref, mark_as_free: true)
163
179
  # revision.delete(oid, mark_as_free: true)
@@ -191,7 +191,13 @@ module HexaPDF
191
191
  #
192
192
  # See: PDF1.7 s7.3.3
193
193
  def serialize_float(obj)
194
- -0.0001 < obj && obj < 0.0001 && obj != 0 ? sprintf("%.6f", obj) : obj.round(6).to_s
194
+ if -0.0001 < obj && obj < 0.0001 && obj != 0
195
+ sprintf("%.6f", obj)
196
+ elsif obj.finite?
197
+ obj.round(6).to_s
198
+ else
199
+ raise HexaPDF::Error, "Can't serialize special floating point number #{obj}"
200
+ end
195
201
  end
196
202
 
197
203
  # The regexp matches all characters that need to be escaped and the substs hash contains the
@@ -343,6 +349,7 @@ module HexaPDF
343
349
  @io << data.freeze
344
350
  end
345
351
  @io << "\nendstream"
352
+ @in_object = false
346
353
 
347
354
  nil
348
355
  else
@@ -350,12 +357,12 @@ module HexaPDF
350
357
  obj.value[:Length] = data.size
351
358
 
352
359
  str = serialize_hash(obj.value)
360
+ @in_object = false
361
+
353
362
  str << "stream\n"
354
363
  str << data
355
364
  str << "\nendstream"
356
365
  end
357
- ensure
358
- @in_object = false
359
366
  end
360
367
 
361
368
  # Invokes the correct serialization method for the object.
@@ -73,11 +73,15 @@ module HexaPDF
73
73
  # The IO object from the tokens are read.
74
74
  attr_reader :io
75
75
 
76
- # Creates a new tokenizer.
77
- def initialize(io)
76
+ # Creates a new tokenizer for the given IO stream.
77
+ #
78
+ # If +on_correctable_error+ is set to an object responding to +call(msg, pos)+, errors for
79
+ # correctable situations are only raised if the return value of calling the object is +true+.
80
+ def initialize(io, on_correctable_error: nil)
78
81
  @io = io
79
82
  @ss = StringScanner.new(''.b)
80
83
  @original_pos = -1
84
+ @on_correctable_error = on_correctable_error || proc { false }
81
85
  self.pos = 0
82
86
  end
83
87
 
@@ -180,7 +184,8 @@ module HexaPDF
180
184
  end
181
185
  else
182
186
  unless allow_keyword
183
- raise HexaPDF::MalformedPDFError.new("Invalid object, got token #{token}", pos: pos)
187
+ maybe_raise("Invalid object, got token #{token}", force: token !~ /^-?(nan|inf)$/i)
188
+ token = 0
184
189
  end
185
190
  end
186
191
  end
@@ -188,6 +193,28 @@ module HexaPDF
188
193
  token
189
194
  end
190
195
 
196
+ # Returns a single integer or keyword token read from the current position and advances the scan
197
+ # pointer. If the current position doesn't contain such a token, +nil+ is returned without
198
+ # advancing the scan pointer. The value +NO_MORE_TOKENS+ is returned if there are no more tokens
199
+ # available.
200
+ #
201
+ # Initial runs of whitespace characters are ignored.
202
+ #
203
+ # Note: This is a special method meant for use with reconstructing the cross-reference table!
204
+ def next_integer_or_keyword
205
+ skip_whitespace
206
+ byte = @ss.string.getbyte(@ss.pos) || -1
207
+ if 48 <= byte && byte <= 57
208
+ parse_number
209
+ elsif (97 <= byte && byte <= 122) || (65 <= byte && byte <= 90)
210
+ parse_keyword
211
+ elsif byte == -1 # we reached the end of the file
212
+ NO_MORE_TOKENS
213
+ else
214
+ nil
215
+ end
216
+ end
217
+
191
218
  # Reads the byte (an integer) at the current position and advances the scan pointer.
192
219
  def next_byte
193
220
  prepare_string_scanner(1)
@@ -406,6 +433,20 @@ module HexaPDF
406
433
  true
407
434
  end
408
435
 
436
+ # Calls the @on_correctable_error callable object with the given message and the current
437
+ # position. If the returned value is +true+, raises a HexaPDF::MalformedPDFError. Otherwise the
438
+ # error is corrected (by the caller) and tokenization continues.
439
+ #
440
+ # If the option +force+ is used, the callable object is not called and the error is raised
441
+ # immediately.
442
+ def maybe_raise(msg, force: false)
443
+ if force || @on_correctable_error.call(msg, pos)
444
+ error = HexaPDF::MalformedPDFError.new(msg, pos: pos)
445
+ error.set_backtrace(caller(1))
446
+ raise error
447
+ end
448
+ end
449
+
409
450
  end
410
451
 
411
452
  end
@@ -48,6 +48,7 @@ module HexaPDF
48
48
  autoload(:TextField, 'hexapdf/type/acro_form/text_field')
49
49
  autoload(:ButtonField, 'hexapdf/type/acro_form/button_field')
50
50
  autoload(:ChoiceField, 'hexapdf/type/acro_form/choice_field')
51
+ autoload(:SignatureField, 'hexapdf/type/acro_form/signature_field')
51
52
 
52
53
  autoload(:AppearanceGenerator, 'hexapdf/type/acro_form/appearance_generator')
53
54
 
@@ -120,7 +120,7 @@ module HexaPDF
120
120
  # widget.marker_style(style: :cross)
121
121
  # # => no visible rectangle, gray background, cross mark when checked
122
122
  def create_check_box_appearances
123
- unless @widget.appearance&.normal_appearance&.value&.size == 2
123
+ unless @widget.appearance_dict&.normal_appearance&.value&.size == 2
124
124
  raise HexaPDF::Error, "Widget of check box doesn't define name for on state"
125
125
  end
126
126
  border_style = @widget.border_style
@@ -128,11 +128,11 @@ module HexaPDF
128
128
 
129
129
  rect = update_widget(@field[:V], border_width)
130
130
 
131
- off_form = @widget.appearance.normal_appearance[:Off] =
131
+ off_form = @widget.appearance_dict.normal_appearance[:Off] =
132
132
  @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
133
133
  apply_background_and_border(border_style, off_form.canvas)
134
134
 
135
- on_form = @widget.appearance.normal_appearance[@field.check_box_on_name] =
135
+ on_form = @widget.appearance_dict.normal_appearance[@field.check_box_on_name] =
136
136
  @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
137
137
  canvas = on_form.canvas
138
138
  apply_background_and_border(border_style, canvas)
@@ -169,22 +169,22 @@ module HexaPDF
169
169
  # widget.marker_style(style: :circle, size: 0, color: 0)
170
170
  # # => default appearance
171
171
  def create_radio_button_appearances
172
- unless @widget.appearance&.normal_appearance&.value&.size == 2
172
+ unless @widget.appearance_dict&.normal_appearance&.value&.size == 2
173
173
  raise HexaPDF::Error, "Widget of radio button doesn't define unique name for on state"
174
174
  end
175
175
 
176
- on_name = (@widget.appearance.normal_appearance.value.keys - [:Off]).first
176
+ on_name = (@widget.appearance_dict.normal_appearance.value.keys - [:Off]).first
177
177
  border_style = @widget.border_style
178
178
  marker_style = @widget.marker_style
179
179
 
180
180
  rect = update_widget(@field[:V] == on_name ? on_name : :Off, border_style.width)
181
181
 
182
- off_form = @widget.appearance.normal_appearance[:Off] =
182
+ off_form = @widget.appearance_dict.normal_appearance[:Off] =
183
183
  @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
184
184
  apply_background_and_border(border_style, off_form.canvas,
185
185
  circular: marker_style.style == :circle)
186
186
 
187
- on_form = @widget.appearance.normal_appearance[on_name] =
187
+ on_form = @widget.appearance_dict.normal_appearance[on_name] =
188
188
  @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
189
189
  canvas = on_form.canvas
190
190
  apply_background_and_border(border_style, canvas,
@@ -219,17 +219,8 @@ module HexaPDF
219
219
  #
220
220
  # Note: Multiline, comb and rich text fields are currently not supported!
221
221
  def create_text_appearances
222
- font_name, font_size = @field.parse_default_appearance_string
223
222
  default_resources = @document.acro_form.default_resources
224
- font = default_resources.font(font_name).font_wrapper rescue nil
225
- unless font
226
- fallback_font_name, fallback_font_options = @document.config['acro_form.fallback_font']
227
- if fallback_font_name
228
- font = @document.fonts.add(fallback_font_name, **(fallback_font_options || {}))
229
- else
230
- raise(HexaPDF::Error, "Font #{font_name} of the AcroForm's default resources not usable")
231
- end
232
- end
223
+ font, font_size = retrieve_font_information(default_resources)
233
224
  style = HexaPDF::Layout::Style.new(font: font)
234
225
  border_style = @widget.border_style
235
226
  padding = [1, border_style.width].max
@@ -245,6 +236,9 @@ module HexaPDF
245
236
  end
246
237
 
247
238
  form = (@widget[:AP] ||= {})[:N] ||= @document.add({Type: :XObject, Subtype: :Form})
239
+ # Wrap existing object in Form class in case the PDF writer didn't include the /Subtype
240
+ # key; we can do this since we know this has to be a Form object
241
+ form = @document.wrap(form, type: :XObject, subtype: :Form) unless form[:Subtype] == :Form
248
242
  form.value.replace({Type: :XObject, Subtype: :Form, BBox: [0, 0, rect.width, rect.height]})
249
243
  form.contents = ''
250
244
  form[:Resources] = HexaPDF::Object.deep_copy(default_resources)
@@ -479,6 +473,27 @@ module HexaPDF
479
473
  end
480
474
  end
481
475
 
476
+ # Returns the font wrapper and font size to be used for a variable text field.
477
+ def retrieve_font_information(resources)
478
+ font_name, font_size = @field.parse_default_appearance_string
479
+ font_object = resources.font(font_name) rescue nil
480
+ font = font_object&.font_wrapper
481
+ unless font
482
+ fallback_font = @document.config['acro_form.fallback_font']
483
+ fallback_font_name, fallback_font_options = if fallback_font.respond_to?(:call)
484
+ fallback_font.call(@field, font_object)
485
+ else
486
+ fallback_font
487
+ end
488
+ if fallback_font_name
489
+ font = @document.fonts.add(fallback_font_name, **(fallback_font_options || {}))
490
+ else
491
+ raise(HexaPDF::Error, "Font #{font_name} of the AcroForm's default resources not usable")
492
+ end
493
+ end
494
+ [font, font_size]
495
+ end
496
+
482
497
  # Calculates the font size for text fields based on the font and font size of the default
483
498
  # appearance string, the annotation rectangle and the border style.
484
499
  def calculate_font_size(font, font_size, rect, border_style)
@@ -184,7 +184,7 @@ module HexaPDF
184
184
  #
185
185
  # Defaults to :Yes if no other name could be determined.
186
186
  def check_box_on_name
187
- each_widget.to_a.first&.appearance&.normal_appearance&.value&.each_key&.
187
+ each_widget.to_a.first&.appearance_dict&.normal_appearance&.value&.each_key&.
188
188
  find {|key| key != :Off } || :Yes
189
189
  end
190
190
 
@@ -192,7 +192,7 @@ module HexaPDF
192
192
  # button.
193
193
  def radio_button_values
194
194
  each_widget.map do |widget|
195
- widget.appearance&.normal_appearance&.value&.each_key&.find {|key| key != :Off }
195
+ widget.appearance_dict&.normal_appearance&.value&.each_key&.find {|key| key != :Off }
196
196
  end.compact
197
197
  end
198
198
 
@@ -233,7 +233,11 @@ module HexaPDF
233
233
  def create_appearances(force: false)
234
234
  appearance_generator_class = document.config.constantize('acro_form.appearance_generator')
235
235
  each_widget do |widget|
236
- next if !force && widget.appearance?
236
+ normal_appearance = widget.appearance_dict&.normal_appearance
237
+ next if !force && normal_appearance &&
238
+ ((!push_button? && normal_appearance.value.length == 2 &&
239
+ normal_appearance.value.each_value.all?(HexaPDF::Stream)) ||
240
+ (push_button? && normal_appearance.kind_of?(HexaPDF::Stream)))
237
241
  if check_box?
238
242
  appearance_generator_class.new(widget).create_check_box_appearances
239
243
  elsif radio_button?
@@ -250,7 +254,7 @@ module HexaPDF
250
254
  create_appearances
251
255
  value = self[:V]
252
256
  each_widget do |widget|
253
- widget[:AS] = (widget.appearance&.normal_appearance&.value&.key?(value) ? value : :Off)
257
+ widget[:AS] = (widget.appearance_dict&.normal_appearance&.key?(value) ? value : :Off)
254
258
  end
255
259
  end
256
260
 
@@ -315,6 +315,7 @@ module HexaPDF
315
315
 
316
316
  if embedded_widget?
317
317
  WIDGET_FIELDS.each {|key| delete(key) }
318
+ document.revisions.each {|revision| break if revision.update(self)}
318
319
  else
319
320
  self[:Kids].delete_at(widget_index)
320
321
  document.delete(widget)
@@ -331,6 +331,43 @@ module HexaPDF
331
331
  end
332
332
  end
333
333
 
334
+ # Flattens the whole interactive form or only the given fields, and returns the fields that
335
+ # couldn't be flattened.
336
+ #
337
+ # Flattening means making the appearance streams of the field widgets part of the respective
338
+ # page's content stream and removing the fields themselves.
339
+ #
340
+ # If the whole interactive form is flattened, the form object itself is also removed if all
341
+ # fields were flattened.
342
+ #
343
+ # The +create_appearances+ argument controls whether missing appearances should
344
+ # automatically be created.
345
+ #
346
+ # See: HexaPDF::Type::Page#flatten_annotations
347
+ def flatten(fields: nil, create_appearances: true)
348
+ remove_form = fields.nil?
349
+ fields ||= each_field.to_a
350
+ if create_appearances
351
+ fields.each {|field| field.create_appearances if field.respond_to?(:create_appearances) }
352
+ end
353
+
354
+ not_flattened = fields.map {|field| field.each_widget.to_a }.flatten
355
+ document.pages.each {|page| not_flattened = page.flatten_annotations(not_flattened) }
356
+ fields -= not_flattened.map(&:form_field)
357
+
358
+ fields.each do |field|
359
+ (field[:Parent]&.[](:Kids) || self[:Fields]).delete(field)
360
+ document.delete(field)
361
+ end
362
+
363
+ if remove_form && not_flattened.empty?
364
+ document.catalog.delete(:AcroForm)
365
+ document.delete(self)
366
+ end
367
+
368
+ not_flattened
369
+ end
370
+
334
371
  private
335
372
 
336
373
  # Helper method for bit field getter access.
@@ -0,0 +1,223 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2021 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/type/acro_form/field'
38
+ require 'hexapdf/type/acro_form/appearance_generator'
39
+
40
+ module HexaPDF
41
+ module Type
42
+ module AcroForm
43
+
44
+ # AcroForm signature fields represent a digital signature.
45
+ #
46
+ # It serves two purposes: To visually display the signature and to hold the information of the
47
+ # digital signature itself.
48
+ #
49
+ # If the signature should not be visible, the associated widget annotation should have zero
50
+ # width and height; and/or the 'hidden' or 'no_view' flags of the annotation should be set.
51
+ #
52
+ # See: PDF1.7 s12.7.4.5
53
+ class SignatureField < Field
54
+
55
+ # A signature field lock dictionary specifies a set of form fields that should be locked
56
+ # once the associated signature field is signed.
57
+ #
58
+ # See: PDF1.7 s12.7.4.5
59
+ class LockDictionary < Dictionary
60
+
61
+ define_type :SigFieldLock
62
+
63
+ define_field :Type, type: Symbol, default: type
64
+ define_field :Action, type: Symbol, required: true,
65
+ allowed_values: [:All, :Include, :Exclude]
66
+ define_field :Fields, type: PDFArray
67
+
68
+ private
69
+
70
+ def perform_validation #:nodoc:
71
+ if self[:Action] != :All && !key?(:Fields)
72
+ yield("The /Fields key of the signature lock dictionary is missing")
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ # A seed value dictionary contains information that constrains the properties of a signature
79
+ # that is applied to the associated signature field.
80
+ #
81
+ # == Flags
82
+ #
83
+ # If a flag is set it means that the associated entry is a required constraint. Otherwise it
84
+ # is optional.
85
+ #
86
+ # The available flags are: filter, sub_filter, v, reasons, legal_attestation, add_rev_info
87
+ # and digest_method.
88
+ #
89
+ # See: PDF1.7 s12.7.4.5
90
+ class SeedValueDictionary < Dictionary
91
+
92
+ extend Utils::BitField
93
+
94
+ define_type :SV
95
+
96
+ define_field :Type, type: Symbol, default: type
97
+ define_field :Ff, type: Integer, default: 0
98
+ define_field :Filter, type: Symbol
99
+ define_field :SubFilter, type: PDFArray
100
+ define_field :DigestMethod, type: PDFArray, version: '1.7'
101
+ define_field :V, type: Float
102
+ define_field :Cert, type: :SVCert
103
+ define_field :Reasons, type: PDFArray
104
+ define_field :MDP, type: Dictionary, version: '1.6'
105
+ define_field :TimeStamp, type: Dictionary, version: '1.6'
106
+ define_field :LegalAttestation, type: PDFArray, version: '1.6'
107
+ define_field :AddRevInfo, type: Boolean, version: '1.7'
108
+
109
+ ##
110
+ # :method: flags
111
+ #
112
+ # Returns an array of flag names representing the set bit flags.
113
+ #
114
+
115
+ ##
116
+ # :method: flagged?
117
+ # :call-seq:
118
+ # flagged?(flag)
119
+ #
120
+ # Returns +true+ if the given flag is set. The argument can either be the flag name or the
121
+ # bit index.
122
+ #
123
+
124
+ ##
125
+ # :method: flag
126
+ # :call-seq:
127
+ # flag(*flags, clear_existing: false)
128
+ #
129
+ # Sets the given flags, given as flag names or bit indices. If +clear_existing+ is +true+,
130
+ # all prior flags will be cleared.
131
+ #
132
+ bit_field(:flags, {filter: 0, sub_filter: 1, v: 2, reasons: 3, legal_attestation: 4,
133
+ add_rev_info: 5, digest_method: 6},
134
+ lister: "flags", getter: "flagged?", setter: "flag", unsetter: "unflag",
135
+ value_getter: "self[:Ff]", value_setter: "self[:Ff]")
136
+
137
+ end
138
+
139
+ # A certificate seed value dictionary contains information about the characteristics of the
140
+ # certificate that shall be used when signing.
141
+ #
142
+ # == Flags
143
+ #
144
+ # The flags describe the entries that a signer is required to use.
145
+ #
146
+ # The available flags are: subject, issuer, oid, subject_dn, reserved, key_usage and url.
147
+ #
148
+ # See: PDF1.7 s12.7.4.5
149
+ class CertificateSeedValueDictionary < Dictionary
150
+
151
+ extend Utils::BitField
152
+
153
+ define_type :SVCert
154
+
155
+ define_field :Type, type: Symbol, default: type
156
+ define_field :Ff, type: Integer, default: 0
157
+ define_field :Subject, type: PDFArray
158
+ define_field :SubjectDN, type: PDFArray, version: '1.7'
159
+ define_field :KeyUsage, type: PDFArray, version: '1.7'
160
+ define_field :Issuer, type: PDFArray
161
+ define_field :OID, type: PDFArray
162
+ define_field :URL, type: String
163
+ define_field :URLType, type: Symbol, default: :Browser
164
+
165
+ ##
166
+ # :method: flags
167
+ #
168
+ # Returns an array of flag names representing the set bit flags.
169
+ #
170
+
171
+ ##
172
+ # :method: flagged?
173
+ # :call-seq:
174
+ # flagged?(flag)
175
+ #
176
+ # Returns +true+ if the given flag is set. The argument can either be the flag name or the
177
+ # bit index.
178
+ #
179
+
180
+ ##
181
+ # :method: flag
182
+ # :call-seq:
183
+ # flag(*flags, clear_existing: false)
184
+ #
185
+ # Sets the given flags, given as flag names or bit indices. If +clear_existing+ is +true+,
186
+ # all prior flags will be cleared.
187
+ #
188
+ bit_field(:flags, {subject: 0, issuer: 1, oid: 2, subject_dn: 3, reserved: 4,
189
+ key_usage: 5, url: 6},
190
+ lister: "flags", getter: "flagged?", setter: "flag", unsetter: "unflag",
191
+ value_getter: "self[:Ff]", value_setter: "self[:Ff]")
192
+
193
+ end
194
+
195
+ define_field :Lock, type: :SigFieldLock, indirect: true, version: '1.5'
196
+ define_field :SV, type: :SV, indirect: true, version: '1.5'
197
+
198
+ # Returns the associated signature dictionary or +nil+ if the signature is not filled in.
199
+ def field_value
200
+ self[:V]
201
+ end
202
+
203
+ # Sets the signature dictionary as value of this signature field.
204
+ def field_value=(sig_dict)
205
+ self[:V] = sig_dict
206
+ end
207
+
208
+ private
209
+
210
+ def perform_validation #:nodoc:
211
+ if field_type != :Sig
212
+ yield("Field /FT of AcroForm signature field has to be :Sig", true)
213
+ self[:FT] = :Sig
214
+ end
215
+
216
+ super
217
+ end
218
+
219
+ end
220
+
221
+ end
222
+ end
223
+ end