hexapdf 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +35 -50
  5. data/VERSION +1 -1
  6. data/lib/hexapdf/cli.rb +4 -0
  7. data/lib/hexapdf/cli/command.rb +6 -2
  8. data/lib/hexapdf/cli/image2pdf.rb +141 -0
  9. data/lib/hexapdf/cli/info.rb +1 -1
  10. data/lib/hexapdf/cli/inspect.rb +32 -2
  11. data/lib/hexapdf/cli/modify.rb +1 -1
  12. data/lib/hexapdf/cli/optimize.rb +1 -1
  13. data/lib/hexapdf/cli/watermark.rb +130 -0
  14. data/lib/hexapdf/composer.rb +2 -2
  15. data/lib/hexapdf/configuration.rb +7 -1
  16. data/lib/hexapdf/content/canvas.rb +2 -2
  17. data/lib/hexapdf/content/graphic_object/arc.rb +2 -2
  18. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +2 -2
  19. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  20. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  21. data/lib/hexapdf/dictionary.rb +11 -3
  22. data/lib/hexapdf/dictionary_fields.rb +32 -3
  23. data/lib/hexapdf/document.rb +7 -3
  24. data/lib/hexapdf/document/files.rb +1 -1
  25. data/lib/hexapdf/document/fonts.rb +21 -1
  26. data/lib/hexapdf/document/pages.rb +2 -2
  27. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -2
  28. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  29. data/lib/hexapdf/font/true_type/table/head.rb +2 -2
  30. data/lib/hexapdf/font/true_type/table/os2.rb +4 -4
  31. data/lib/hexapdf/font/true_type_wrapper.rb +16 -16
  32. data/lib/hexapdf/font/type1_wrapper.rb +16 -16
  33. data/lib/hexapdf/font_loader.rb +2 -0
  34. data/lib/hexapdf/font_loader/from_configuration.rb +5 -0
  35. data/lib/hexapdf/font_loader/standard14.rb +5 -0
  36. data/lib/hexapdf/image_loader/png.rb +1 -1
  37. data/lib/hexapdf/layout/box.rb +2 -2
  38. data/lib/hexapdf/layout/image_box.rb +1 -1
  39. data/lib/hexapdf/layout/style.rb +50 -24
  40. data/lib/hexapdf/layout/text_box.rb +1 -1
  41. data/lib/hexapdf/layout/text_fragment.rb +2 -2
  42. data/lib/hexapdf/layout/text_layouter.rb +14 -10
  43. data/lib/hexapdf/name_tree_node.rb +3 -3
  44. data/lib/hexapdf/number_tree_node.rb +3 -3
  45. data/lib/hexapdf/pdf_array.rb +207 -0
  46. data/lib/hexapdf/rectangle.rb +12 -12
  47. data/lib/hexapdf/serializer.rb +1 -1
  48. data/lib/hexapdf/stream.rb +6 -4
  49. data/lib/hexapdf/task/optimize.rb +3 -3
  50. data/lib/hexapdf/type.rb +2 -0
  51. data/lib/hexapdf/type/acro_form.rb +51 -0
  52. data/lib/hexapdf/type/acro_form/field.rb +129 -0
  53. data/lib/hexapdf/type/acro_form/form.rb +124 -0
  54. data/lib/hexapdf/type/action.rb +1 -1
  55. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  56. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  57. data/lib/hexapdf/type/actions/launch.rb +1 -1
  58. data/lib/hexapdf/type/annotation.rb +2 -2
  59. data/lib/hexapdf/type/annotations.rb +1 -0
  60. data/lib/hexapdf/type/annotations/link.rb +4 -15
  61. data/lib/hexapdf/type/annotations/markup_annotation.rb +2 -1
  62. data/lib/hexapdf/type/annotations/text.rb +3 -6
  63. data/lib/hexapdf/type/annotations/widget.rb +90 -0
  64. data/lib/hexapdf/type/catalog.rb +12 -9
  65. data/lib/hexapdf/type/cid_font.rb +3 -3
  66. data/lib/hexapdf/type/file_specification.rb +2 -2
  67. data/lib/hexapdf/type/font_descriptor.rb +5 -2
  68. data/lib/hexapdf/type/font_simple.rb +1 -1
  69. data/lib/hexapdf/type/font_type0.rb +1 -1
  70. data/lib/hexapdf/type/font_type3.rb +1 -1
  71. data/lib/hexapdf/type/form.rb +2 -2
  72. data/lib/hexapdf/type/graphics_state_parameter.rb +11 -6
  73. data/lib/hexapdf/type/icon_fit.rb +58 -0
  74. data/lib/hexapdf/type/image.rb +14 -8
  75. data/lib/hexapdf/type/info.rb +2 -1
  76. data/lib/hexapdf/type/page.rb +4 -4
  77. data/lib/hexapdf/type/page_tree_node.rb +3 -7
  78. data/lib/hexapdf/type/resources.rb +1 -1
  79. data/lib/hexapdf/type/trailer.rb +4 -4
  80. data/lib/hexapdf/type/viewer_preferences.rb +7 -4
  81. data/lib/hexapdf/type/xref_stream.rb +2 -2
  82. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  83. data/lib/hexapdf/version.rb +1 -1
  84. data/man/man1/hexapdf.1 +77 -8
  85. data/test/hexapdf/content/test_canvas.rb +2 -2
  86. data/test/hexapdf/content/test_processor.rb +3 -3
  87. data/test/hexapdf/document/test_files.rb +4 -4
  88. data/test/hexapdf/document/test_fonts.rb +13 -1
  89. data/test/hexapdf/document/test_images.rb +6 -6
  90. data/test/hexapdf/document/test_pages.rb +8 -8
  91. data/test/hexapdf/encryption/test_security_handler.rb +7 -7
  92. data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -5
  93. data/test/hexapdf/font/test_true_type_wrapper.rb +2 -2
  94. data/test/hexapdf/font_loader/test_from_configuration.rb +4 -0
  95. data/test/hexapdf/font_loader/test_standard14.rb +10 -0
  96. data/test/hexapdf/image_loader/test_jpeg.rb +1 -1
  97. data/test/hexapdf/image_loader/test_png.rb +3 -3
  98. data/test/hexapdf/layout/test_box.rb +2 -2
  99. data/test/hexapdf/layout/test_frame.rb +1 -1
  100. data/test/hexapdf/layout/test_image_box.rb +1 -1
  101. data/test/hexapdf/layout/test_style.rb +18 -13
  102. data/test/hexapdf/layout/test_text_box.rb +1 -1
  103. data/test/hexapdf/layout/test_text_layouter.rb +11 -6
  104. data/test/hexapdf/task/test_dereference.rb +2 -2
  105. data/test/hexapdf/task/test_optimize.rb +11 -11
  106. data/test/hexapdf/test_composer.rb +1 -1
  107. data/test/hexapdf/test_dictionary.rb +10 -2
  108. data/test/hexapdf/test_dictionary_fields.rb +27 -3
  109. data/test/hexapdf/test_document.rb +16 -15
  110. data/test/hexapdf/test_importer.rb +4 -4
  111. data/test/hexapdf/test_object.rb +1 -1
  112. data/test/hexapdf/test_pdf_array.rb +162 -0
  113. data/test/hexapdf/test_rectangle.rb +3 -5
  114. data/test/hexapdf/test_serializer.rb +1 -1
  115. data/test/hexapdf/test_stream.rb +1 -0
  116. data/test/hexapdf/test_writer.rb +3 -3
  117. data/test/hexapdf/type/acro_form/test_field.rb +85 -0
  118. data/test/hexapdf/type/acro_form/test_form.rb +69 -0
  119. data/test/hexapdf/type/annotations/test_text.rb +2 -6
  120. data/test/hexapdf/type/annotations/test_widget.rb +24 -0
  121. data/test/hexapdf/type/test_annotation.rb +1 -1
  122. data/test/hexapdf/type/test_catalog.rb +1 -1
  123. data/test/hexapdf/type/test_cid_font.rb +3 -3
  124. data/test/hexapdf/type/test_font.rb +2 -2
  125. data/test/hexapdf/type/test_font_descriptor.rb +2 -1
  126. data/test/hexapdf/type/test_font_simple.rb +3 -3
  127. data/test/hexapdf/type/test_font_true_type.rb +6 -6
  128. data/test/hexapdf/type/test_font_type0.rb +5 -5
  129. data/test/hexapdf/type/test_font_type1.rb +8 -8
  130. data/test/hexapdf/type/test_font_type3.rb +4 -4
  131. data/test/hexapdf/type/test_image.rb +16 -12
  132. data/test/hexapdf/type/test_page.rb +11 -11
  133. data/test/hexapdf/type/test_page_tree_node.rb +20 -20
  134. data/test/hexapdf/type/test_resources.rb +6 -6
  135. data/test/hexapdf/type/test_trailer.rb +5 -2
  136. data/test/hexapdf/type/test_xref_stream.rb +1 -0
  137. data/test/hexapdf/utils/test_sorted_tree_node.rb +35 -35
  138. metadata +23 -7
  139. data/test/hexapdf/type/annotations/test_link.rb +0 -19
@@ -60,9 +60,9 @@ module HexaPDF
60
60
 
61
61
  include Utils::SortedTreeNode
62
62
 
63
- define_field :Kids, type: Array
64
- define_field :Names, type: Array
65
- define_field :Limits, type: Array
63
+ define_field :Kids, type: PDFArray
64
+ define_field :Names, type: PDFArray
65
+ define_field :Limits, type: PDFArray
66
66
 
67
67
  private
68
68
 
@@ -49,9 +49,9 @@ module HexaPDF
49
49
 
50
50
  include Utils::SortedTreeNode
51
51
 
52
- define_field :Kids, type: Array
53
- define_field :Nums, type: Array
54
- define_field :Limits, type: Array
52
+ define_field :Kids, type: PDFArray
53
+ define_field :Nums, type: PDFArray
54
+ define_field :Limits, type: PDFArray
55
55
 
56
56
  private
57
57
 
@@ -0,0 +1,207 @@
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-2019 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/object'
38
+
39
+ module HexaPDF
40
+
41
+ # Implementation of the PDF array type.
42
+ #
43
+ # This is mainly done to provide automatic resolution of indirect object references when using the
44
+ # #[] method. Therefore not all Array methods are implemented - use the #value directly if other
45
+ # methods are needed.
46
+ #
47
+ # See: PDF1.7 s7.3.6
48
+ class PDFArray < HexaPDF::Object
49
+
50
+ include Enumerable
51
+
52
+ # :call-seq:
53
+ # array[index] -> obj or nil
54
+ # array[start, length] -> new_array or nil
55
+ # array[range] -> new_array or nil
56
+ #
57
+ # Returns the value at the given index, or a subarray using the given +start+ and +length+, or a
58
+ # subarray specified by +range+.
59
+ #
60
+ # This method should be used instead of direct access to a value because it provides some
61
+ # advantages:
62
+ #
63
+ # * References are automatically resolved.
64
+ #
65
+ # * Returns the native Ruby object for values with class HexaPDF::Object. However, all
66
+ # subclasses of HexaPDF::Object are returned as is (it makes no sense, for example, to return
67
+ # the hash that describes the Catalog instead of the Catalog object).
68
+ def [](arg1, arg2 = nil)
69
+ data = value[arg1, *arg2]
70
+ return if data.nil?
71
+
72
+ if arg2 || arg1.kind_of?(Range)
73
+ index = (arg2 ? arg1 : arg1.begin)
74
+ data.map! {|item| process_entry(item, index).tap { index += 1 } }
75
+ else
76
+ process_entry(data, arg1)
77
+ end
78
+ end
79
+
80
+ # Stores the data under the given index in the array.
81
+ #
82
+ # If the current value for this index has the class HexaPDF::Object (and only this, no
83
+ # subclasses) and the given data has not (including subclasses), the data is stored inside the
84
+ # HexaPDF::Object.
85
+ def []=(index, data)
86
+ if value[index].class == HexaPDF::Object && !data.kind_of?(HexaPDF::Object) &&
87
+ !data.kind_of?(HexaPDF::Reference)
88
+ value[index].value = data
89
+ else
90
+ value[index] = data
91
+ end
92
+ end
93
+
94
+ # Returns the values at the given indices.
95
+ #
96
+ # See #[] for details
97
+ def values_at(*indices)
98
+ indices.map! {|index| self[index] }
99
+ end
100
+
101
+ # Append a value to the array.
102
+ def <<(data)
103
+ value << data
104
+ end
105
+
106
+ # Insert one or more values into the array at the given index.
107
+ def insert(index, *objects)
108
+ value.insert(index, *objects)
109
+ end
110
+
111
+ # Deletes the value at the given index.
112
+ def delete_at(index)
113
+ value.delete_at(index)
114
+ end
115
+
116
+ # :call-seq:
117
+ # array.slice!(index) -> obj or nil
118
+ # array.slice!(start, length) -> new_array or nil
119
+ # array.slice!(range) -> new_array or nil
120
+ #
121
+ # Deletes the element(s) given by an index (and optionally a length) or by a range, and returns
122
+ # them or +nil+ if the index is out of range.
123
+ def slice!(arg1, arg2 = nil)
124
+ data = value.slice!(arg1, *arg2)
125
+ if arg2 || arg1.kind_of?(Range)
126
+ data.map! {|item| process_entry(item) }
127
+ else
128
+ process_entry(data)
129
+ end
130
+ end
131
+
132
+ # :call-seq:
133
+ # array.reject! {|item| block } -> array or nil
134
+ # array.reject! -> Enumerator
135
+ #
136
+ # Deletes all elements from the array for which the block returns +true+. If no changes were
137
+ # done, returns +nil+.
138
+ def reject!
139
+ value.reject! {|item| yield(process_entry(item)) }
140
+ end
141
+
142
+ # :call-seq:
143
+ # array.index(obj) -> int or nil
144
+ # array.index {|item| block } -> int or nil
145
+ # array.index -> Enumerator
146
+ #
147
+ # Returns the index of the first object such that object is == to +obj+, or, if a block is
148
+ # given, the index of the first object for which the block returns +true+.
149
+ def index(*obj, &block)
150
+ find_index(*obj, &block)
151
+ end
152
+
153
+ # Returns the number of elements in the array.
154
+ def length
155
+ value.length
156
+ end
157
+ alias size length
158
+
159
+ # Returns +true+ if the array has no elements.
160
+ def empty?
161
+ value.empty?
162
+ end
163
+
164
+ # :call-seq:
165
+ # array.each {|value| block} -> array
166
+ # array.each -> Enumerator
167
+ #
168
+ # Calls the given block once for every value of the array.
169
+ #
170
+ # Note that the yielded value is already preprocessed like in #[].
171
+ def each
172
+ return to_enum(__method__) unless block_given?
173
+ value.each_index {|index| yield(self[index]) }
174
+ self
175
+ end
176
+
177
+ # Returns a duplicate of the underlying array.
178
+ def to_ary
179
+ value.dup
180
+ end
181
+
182
+ private
183
+
184
+ # Ensures that the value is useful for a PDFArray.
185
+ def after_data_change # :nodoc:
186
+ super
187
+ data.value ||= []
188
+ unless value.kind_of?(Array)
189
+ raise ArgumentError, "A PDF array object needs an array value, not a #{value.class}"
190
+ end
191
+ end
192
+
193
+ # Processes the given array entry with index +index+.
194
+ def process_entry(data, index = nil)
195
+ if data.kind_of?(HexaPDF::Reference)
196
+ data = document.deref(data)
197
+ value[index] = data if index
198
+ end
199
+ if data.class == HexaPDF::Object || (data.kind_of?(HexaPDF::Object) && data.value.nil?)
200
+ data = data.value
201
+ end
202
+ data
203
+ end
204
+
205
+ end
206
+
207
+ end
@@ -34,7 +34,7 @@
34
34
  # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
35
  #++
36
36
 
37
- require 'hexapdf/object'
37
+ require 'hexapdf/pdf_array'
38
38
 
39
39
  module HexaPDF
40
40
 
@@ -52,36 +52,36 @@ module HexaPDF
52
52
  # is the top right x-coordinate and +top+ is the top right y-coordinate.
53
53
  #
54
54
  # See: PDF1.7 s7.9.5
55
- class Rectangle < HexaPDF::Object
55
+ class Rectangle < HexaPDF::PDFArray
56
56
 
57
57
  # Returns the x-coordinate of the bottom-left corner.
58
58
  def left
59
- value[0]
59
+ self[0]
60
60
  end
61
61
 
62
62
  # Returns the x-coordinate of the top-right corner.
63
63
  def right
64
- value[2]
64
+ self[2]
65
65
  end
66
66
 
67
67
  # Returns the y-coordinate of the bottom-left corner.
68
68
  def bottom
69
- value[1]
69
+ self[1]
70
70
  end
71
71
 
72
72
  # Returns the y-coordinate of the top-right corner.
73
73
  def top
74
- value[3]
74
+ self[3]
75
75
  end
76
76
 
77
77
  # Returns the width of the rectangle.
78
78
  def width
79
- value[2] - value[0]
79
+ self[2] - self[0]
80
80
  end
81
81
 
82
82
  # Returns the height of the rectangle.
83
83
  def height
84
- value[3] - value[1]
84
+ self[3] - self[1]
85
85
  end
86
86
 
87
87
  # Compares this rectangle to +other+ like in Object#== but also allows comparison to simple
@@ -96,16 +96,16 @@ module HexaPDF
96
96
  # top right corner.
97
97
  def after_data_change
98
98
  super
99
- unless value.kind_of?(Array) && value.size == 4 && value.all? {|i| i.kind_of?(Numeric) }
99
+ unless value.size == 4 && all?(Numeric)
100
100
  raise ArgumentError, "A PDF rectangle structure must contain an array of four numbers"
101
101
  end
102
- value[0], value[2] = value[2], value[0] if value[0] > value[2]
103
- value[1], value[3] = value[3], value[1] if value[1] > value[3]
102
+ self[0], self[2] = self[2], self[0] if self[0] > self[2]
103
+ self[1], self[3] = self[3], self[1] if self[1] > self[3]
104
104
  end
105
105
 
106
106
  def perform_validation #:nodoc:
107
107
  super
108
- unless value.kind_of?(Array) && value.size == 4 && value.all? {|i| i.kind_of?(Numeric) }
108
+ unless value.size == 4 && all?(Numeric)
109
109
  yield("A PDF rectangle structure must contain an array of four numbers", false)
110
110
  end
111
111
  end
@@ -185,7 +185,7 @@ module HexaPDF
185
185
  def serialize_symbol(obj)
186
186
  NAME_CACHE[obj] ||=
187
187
  begin
188
- str = obj.to_s.force_encoding(Encoding::BINARY)
188
+ str = obj.to_s.dup.force_encoding(Encoding::BINARY)
189
189
  str.gsub!(NAME_REGEXP, NAME_SUBSTS)
190
190
  "/#{str}"
191
191
  end
@@ -132,15 +132,17 @@ module HexaPDF
132
132
  # The basic Object class cannot hold stream data, only this subclass contains the necessary
133
133
  # methods to conveniently work with the stream data!
134
134
  #
135
+ # Note that support for external streams (/F, /FFilter, /FDecodeParms) is not yet implemented!
136
+ #
135
137
  # See: PDF1.7 s7.3.8, Dictionary
136
138
  class Stream < Dictionary
137
139
 
138
140
  define_field :Length, type: Integer # not required, will be auto-filled when writing
139
- define_field :Filter, type: [Symbol, Array]
140
- define_field :DecodeParms, type: [Dictionary, Array]
141
+ define_field :Filter, type: [Symbol, PDFArray]
142
+ define_field :DecodeParms, type: [Dictionary, PDFArray]
141
143
  define_field :F, type: :Filespec, version: '1.2'
142
- define_field :FFilter, type: [Symbol, Array], version: '1.2'
143
- define_field :FDecodeParms, type: [Dictionary, Array], version: '1.2'
144
+ define_field :FFilter, type: [Symbol, PDFArray], version: '1.2'
145
+ define_field :FDecodeParms, type: [Dictionary, PDFArray], version: '1.2'
144
146
  define_field :DL, type: Integer
145
147
 
146
148
  # Stream objects must always be indirect.
@@ -115,7 +115,7 @@ module HexaPDF
115
115
  if object_streams == :generate
116
116
  process_object_streams(doc, :generate, xref_streams)
117
117
  elsif xref_streams == :generate
118
- doc.add(Type: :XRef)
118
+ doc.add({Type: :XRef})
119
119
  end
120
120
  end
121
121
 
@@ -142,7 +142,7 @@ module HexaPDF
142
142
  doc.revisions.each_with_index do |rev, rev_index|
143
143
  xref_stream = false
144
144
  count = 0
145
- objstms = [doc.wrap(Type: :ObjStm)]
145
+ objstms = [doc.wrap({Type: :ObjStm})]
146
146
  rev.each do |obj|
147
147
  if obj.type == :XRef
148
148
  xref_stream = true
@@ -156,7 +156,7 @@ module HexaPDF
156
156
  objstms[-1].add_object(obj)
157
157
  count += 1
158
158
  if count == 200
159
- objstms << doc.wrap(Type: :ObjStm)
159
+ objstms << doc.wrap({Type: :ObjStm})
160
160
  count = 0
161
161
  end
162
162
  end
@@ -70,6 +70,8 @@ module HexaPDF
70
70
  autoload(:FontType0, 'hexapdf/type/font_type0')
71
71
  autoload(:CIDFont, 'hexapdf/type/cid_font')
72
72
  autoload(:FontType3, 'hexapdf/type/font_type3')
73
+ autoload(:IconFit, 'hexapdf/type/icon_fit')
74
+ autoload(:AcroForm, 'hexapdf/type/acro_form')
73
75
 
74
76
  end
75
77
 
@@ -0,0 +1,51 @@
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-2019 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
+ module HexaPDF
38
+ module Type
39
+
40
+ # Namespace module for all AcroForm related dictionary types.
41
+ #
42
+ # See: PDF1.7 s12.7
43
+ module AcroForm
44
+
45
+ autoload(:Form, 'hexapdf/type/acro_form/form')
46
+ autoload(:Field, 'hexapdf/type/acro_form/field')
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,129 @@
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-2019 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/dictionary'
38
+
39
+ module HexaPDF
40
+ module Type
41
+ module AcroForm
42
+
43
+ # Field dictionaries are used to define the properties of form fields of AcroForm objects.
44
+ #
45
+ # Fields can be organized in a hierarchy using the /Kids and /Parent keys, for namespacing
46
+ # purposes and to set default values. Those fields that have other fields as children are
47
+ # called non-terminal fields, otherwise they are called terminal fields.
48
+ #
49
+ # See: PDF1.7 s12.7.3.1
50
+ class Field < Dictionary
51
+
52
+ define_type :XXAcroFormField
53
+
54
+ # List of inheritable fields.
55
+ INHERITABLE_FIELDS = [:FT, :Ff, :V, :DV]
56
+
57
+ define_field :FT, type: Symbol, allowed_values: [:Btn, :Tx, :Ch, :Sig]
58
+ define_field :Parent, type: :XXAcroFormField
59
+ define_field :Kids, type: PDFArray
60
+ define_field :T, type: String
61
+ define_field :TU, type: String, version: '1.3'
62
+ define_field :TM, type: String, version: '1.3'
63
+ define_field :Ff, type: Integer, default: 0
64
+ define_field :V, type: [Symbol, String, Stream, PDFArray, Dictionary]
65
+ define_field :DV, type: [Symbol, String, Stream, PDFArray, Dictionary]
66
+ define_field :AA, type: Dictionary, version: '1.2'
67
+
68
+ # Form fields must always be indirect objects.
69
+ def must_be_indirect?
70
+ true
71
+ end
72
+
73
+ # Returns the value for the entry +name+.
74
+ #
75
+ # If +name+ is an inheritable value and the value has not been set on this field object, its
76
+ # value is retrieved from the parent fields.
77
+ #
78
+ # See: Dictionary#[]
79
+ def [](name)
80
+ if value[name].nil? && INHERITABLE_FIELDS.include?(name)
81
+ field = self
82
+ field = field[:Parent] while field.value[name].nil? && field[:Parent]
83
+ field == self || field.value[name].nil? ? super : field[name]
84
+ else
85
+ super
86
+ end
87
+ end
88
+
89
+ # Returns the type of the field, either :Btn (pushbuttons, check boxes, radio buttons), :Tx
90
+ # (text fields), :Ch (scrollable list boxes, combo boxes) or :Sig (signature fields).
91
+ def field_type
92
+ self[:FT]
93
+ end
94
+
95
+ # Returns the full name of the field or +nil+ if no name is set.
96
+ #
97
+ # The full name of a field is constructed using the full name of the parent field, a period
98
+ # and the partial name of the field.
99
+ def full_name
100
+ if key?(:Parent)
101
+ [self[:Parent].full_name, self[:T]].compact.join('.')
102
+ else
103
+ self[:T]
104
+ end
105
+ end
106
+
107
+ # Returns +true+ if this is a terminal field.
108
+ def terminal_field?
109
+ kids = self[:Kids]
110
+ kids.nil? || kids.empty? || kids.any? {|kid| kid[:Subtype] == :Widget }
111
+ end
112
+
113
+ private
114
+
115
+ def perform_validation #:nodoc:
116
+ super
117
+ if terminal_field? && field_type.nil?
118
+ yield("/FT is required for terminal fields")
119
+ end
120
+ if key?(:T) && self[:T].include?('.')
121
+ yield("/T shall not contain a period")
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+ end
129
+ end