hexapdf 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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