origamindee 3.0.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 +7 -0
  2. data/CHANGELOG.md +89 -0
  3. data/COPYING.LESSER +165 -0
  4. data/README.md +131 -0
  5. data/bin/config/pdfcop.conf.yml +236 -0
  6. data/bin/pdf2pdfa +87 -0
  7. data/bin/pdf2ruby +333 -0
  8. data/bin/pdfcop +476 -0
  9. data/bin/pdfdecompress +97 -0
  10. data/bin/pdfdecrypt +91 -0
  11. data/bin/pdfencrypt +113 -0
  12. data/bin/pdfexplode +223 -0
  13. data/bin/pdfextract +277 -0
  14. data/bin/pdfmetadata +143 -0
  15. data/bin/pdfsh +12 -0
  16. data/bin/shell/console.rb +128 -0
  17. data/bin/shell/hexdump.rb +59 -0
  18. data/bin/shell/irbrc +69 -0
  19. data/examples/README.md +34 -0
  20. data/examples/attachments/attachment.rb +38 -0
  21. data/examples/attachments/nested_document.rb +51 -0
  22. data/examples/encryption/encryption.rb +28 -0
  23. data/examples/events/events.rb +72 -0
  24. data/examples/flash/flash.rb +37 -0
  25. data/examples/flash/helloworld.swf +0 -0
  26. data/examples/forms/javascript.rb +54 -0
  27. data/examples/forms/xfa.rb +115 -0
  28. data/examples/javascript/hello_world.rb +22 -0
  29. data/examples/javascript/js_emulation.rb +54 -0
  30. data/examples/loop/goto.rb +32 -0
  31. data/examples/loop/named.rb +33 -0
  32. data/examples/signature/signature.rb +65 -0
  33. data/examples/uri/javascript.rb +56 -0
  34. data/examples/uri/open-uri.rb +21 -0
  35. data/examples/uri/submitform.rb +47 -0
  36. data/lib/origami/3d.rb +364 -0
  37. data/lib/origami/acroform.rb +321 -0
  38. data/lib/origami/actions.rb +318 -0
  39. data/lib/origami/annotations.rb +711 -0
  40. data/lib/origami/array.rb +242 -0
  41. data/lib/origami/boolean.rb +90 -0
  42. data/lib/origami/catalog.rb +418 -0
  43. data/lib/origami/collections.rb +144 -0
  44. data/lib/origami/compound.rb +161 -0
  45. data/lib/origami/destinations.rb +252 -0
  46. data/lib/origami/dictionary.rb +192 -0
  47. data/lib/origami/encryption.rb +1084 -0
  48. data/lib/origami/extensions/fdf.rb +347 -0
  49. data/lib/origami/extensions/ppklite.rb +422 -0
  50. data/lib/origami/filespec.rb +197 -0
  51. data/lib/origami/filters/ascii.rb +211 -0
  52. data/lib/origami/filters/ccitt/tables.rb +267 -0
  53. data/lib/origami/filters/ccitt.rb +357 -0
  54. data/lib/origami/filters/crypt.rb +38 -0
  55. data/lib/origami/filters/dct.rb +54 -0
  56. data/lib/origami/filters/flate.rb +69 -0
  57. data/lib/origami/filters/jbig2.rb +57 -0
  58. data/lib/origami/filters/jpx.rb +47 -0
  59. data/lib/origami/filters/lzw.rb +170 -0
  60. data/lib/origami/filters/predictors.rb +292 -0
  61. data/lib/origami/filters/runlength.rb +129 -0
  62. data/lib/origami/filters.rb +364 -0
  63. data/lib/origami/font.rb +196 -0
  64. data/lib/origami/functions.rb +79 -0
  65. data/lib/origami/graphics/colors.rb +230 -0
  66. data/lib/origami/graphics/instruction.rb +98 -0
  67. data/lib/origami/graphics/path.rb +182 -0
  68. data/lib/origami/graphics/patterns.rb +174 -0
  69. data/lib/origami/graphics/render.rb +62 -0
  70. data/lib/origami/graphics/state.rb +149 -0
  71. data/lib/origami/graphics/text.rb +225 -0
  72. data/lib/origami/graphics/xobject.rb +918 -0
  73. data/lib/origami/graphics.rb +38 -0
  74. data/lib/origami/header.rb +75 -0
  75. data/lib/origami/javascript.rb +713 -0
  76. data/lib/origami/linearization.rb +330 -0
  77. data/lib/origami/metadata.rb +172 -0
  78. data/lib/origami/name.rb +135 -0
  79. data/lib/origami/null.rb +65 -0
  80. data/lib/origami/numeric.rb +181 -0
  81. data/lib/origami/obfuscation.rb +245 -0
  82. data/lib/origami/object.rb +760 -0
  83. data/lib/origami/optionalcontent.rb +183 -0
  84. data/lib/origami/outline.rb +54 -0
  85. data/lib/origami/outputintents.rb +85 -0
  86. data/lib/origami/page.rb +722 -0
  87. data/lib/origami/parser.rb +269 -0
  88. data/lib/origami/parsers/fdf.rb +56 -0
  89. data/lib/origami/parsers/pdf/lazy.rb +176 -0
  90. data/lib/origami/parsers/pdf/linear.rb +122 -0
  91. data/lib/origami/parsers/pdf.rb +118 -0
  92. data/lib/origami/parsers/ppklite.rb +57 -0
  93. data/lib/origami/pdf.rb +1108 -0
  94. data/lib/origami/reference.rb +134 -0
  95. data/lib/origami/signature.rb +702 -0
  96. data/lib/origami/stream.rb +705 -0
  97. data/lib/origami/string.rb +444 -0
  98. data/lib/origami/template/patterns.rb +56 -0
  99. data/lib/origami/template/widgets.rb +151 -0
  100. data/lib/origami/trailer.rb +190 -0
  101. data/lib/origami/tree.rb +62 -0
  102. data/lib/origami/version.rb +23 -0
  103. data/lib/origami/webcapture.rb +100 -0
  104. data/lib/origami/xfa/config.rb +453 -0
  105. data/lib/origami/xfa/connectionset.rb +146 -0
  106. data/lib/origami/xfa/datasets.rb +49 -0
  107. data/lib/origami/xfa/localeset.rb +42 -0
  108. data/lib/origami/xfa/package.rb +59 -0
  109. data/lib/origami/xfa/pdf.rb +73 -0
  110. data/lib/origami/xfa/signature.rb +42 -0
  111. data/lib/origami/xfa/sourceset.rb +43 -0
  112. data/lib/origami/xfa/stylesheet.rb +44 -0
  113. data/lib/origami/xfa/template.rb +1691 -0
  114. data/lib/origami/xfa/xdc.rb +42 -0
  115. data/lib/origami/xfa/xfa.rb +146 -0
  116. data/lib/origami/xfa/xfdf.rb +43 -0
  117. data/lib/origami/xfa/xmpmeta.rb +43 -0
  118. data/lib/origami/xfa.rb +62 -0
  119. data/lib/origami/xreftable.rb +557 -0
  120. data/lib/origami.rb +47 -0
  121. data/test/dataset/calc.pdf +85 -0
  122. data/test/dataset/crypto.pdf +36 -0
  123. data/test/dataset/empty.pdf +49 -0
  124. data/test/test_actions.rb +27 -0
  125. data/test/test_annotations.rb +68 -0
  126. data/test/test_forms.rb +30 -0
  127. data/test/test_native_types.rb +83 -0
  128. data/test/test_object_tree.rb +33 -0
  129. data/test/test_pages.rb +60 -0
  130. data/test/test_pdf.rb +20 -0
  131. data/test/test_pdf_attachment.rb +34 -0
  132. data/test/test_pdf_create.rb +24 -0
  133. data/test/test_pdf_encrypt.rb +102 -0
  134. data/test/test_pdf_parse.rb +134 -0
  135. data/test/test_pdf_parse_lazy.rb +69 -0
  136. data/test/test_pdf_sign.rb +97 -0
  137. data/test/test_streams.rb +184 -0
  138. data/test/test_xrefs.rb +67 -0
  139. metadata +280 -0
@@ -0,0 +1,444 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ require 'date'
22
+
23
+ module Origami
24
+
25
+ #
26
+ # Module common to String objects.
27
+ #
28
+ module String
29
+
30
+ module Encoding
31
+ class EncodingError < Error #:nodoc:
32
+ end
33
+
34
+ module PDFDocEncoding
35
+ CHARMAP =
36
+ [
37
+ "\x00\x00", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd",
38
+ "\xff\xfd", "\x00\x09", "\x00\x0a", "\xff\xfd", "\x00\x0c", "\x00\x0d", "\xff\xfd", "\xff\xfd",
39
+ "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd",
40
+ "\x02\xd8", "\x02\xc7", "\x02\xc6", "\x02\xd9", "\x02\xdd", "\x02\xdb", "\x02\xda", "\x02\xdc",
41
+ "\x00\x20", "\x00\x21", "\x00\x22", "\x00\x23", "\x00\x24", "\x00\x25", "\x00\x26", "\x00\x27",
42
+ "\x00\x28", "\x00\x29", "\x00\x2a", "\x00\x2b", "\x00\x2c", "\x00\x2d", "\x00\x2e", "\x00\x2f",
43
+ "\x00\x30", "\x00\x31", "\x00\x32", "\x00\x33", "\x00\x34", "\x00\x35", "\x00\x36", "\x00\x37",
44
+ "\x00\x38", "\x00\x39", "\x00\x3a", "\x00\x3b", "\x00\x3c", "\x00\x3d", "\x00\x3e", "\x00\x3f",
45
+ "\x00\x40", "\x00\x41", "\x00\x42", "\x00\x43", "\x00\x44", "\x00\x45", "\x00\x46", "\x00\x47",
46
+ "\x00\x48", "\x00\x49", "\x00\x4a", "\x00\x4b", "\x00\x4c", "\x00\x4d", "\x00\x4e", "\x00\x4f",
47
+ "\x00\x50", "\x00\x51", "\x00\x52", "\x00\x53", "\x00\x54", "\x00\x55", "\x00\x56", "\x00\x57",
48
+ "\x00\x58", "\x00\x59", "\x00\x5a", "\x00\x5b", "\x00\x5c", "\x00\x5d", "\x00\x5e", "\x00\x5f",
49
+ "\x00\x60", "\x00\x61", "\x00\x62", "\x00\x63", "\x00\x64", "\x00\x65", "\x00\x66", "\x00\x67",
50
+ "\x00\x68", "\x00\x69", "\x00\x6a", "\x00\x6b", "\x00\x6c", "\x00\x6d", "\x00\x6e", "\x00\x6f",
51
+ "\x00\x70", "\x00\x71", "\x00\x72", "\x00\x73", "\x00\x74", "\x00\x75", "\x00\x76", "\x00\x77",
52
+ "\x00\x78", "\x00\x79", "\x00\x7a", "\x00\x7b", "\x00\x7c", "\x00\x7d", "\x00\x7e", "\xff\xfd",
53
+ "\x20\x22", "\x20\x20", "\x20\x21", "\x20\x26", "\x20\x14", "\x20\x13", "\x01\x92", "\x20\x44",
54
+ "\x20\x39", "\x20\x3a", "\x22\x12", "\x20\x30", "\x20\x1e", "\x20\x1c", "\x20\x1d", "\x20\x18",
55
+ "\x20\x19", "\x20\x1a", "\x21\x22", "\xfb\x01", "\xfb\x02", "\x01\x41", "\x01\x52", "\x01\x60",
56
+ "\x01\x78", "\x01\x7d", "\x01\x31", "\x01\x42", "\x01\x53", "\x01\x61", "\x01\x7e", "\xff\xfd",
57
+ "\x20\xac", "\x00\xa1", "\x00\xa2", "\x00\xa3", "\x00\xa4", "\x00\xa5", "\x00\xa6", "\x00\xa7",
58
+ "\x00\xa8", "\x00\xa9", "\x00\xaa", "\x00\xab", "\x00\xac", "\xff\xfd", "\x00\xae", "\x00\xaf",
59
+ "\x00\xb0", "\x00\xb1", "\x00\xb2", "\x00\xb3", "\x00\xb4", "\x00\xb5", "\x00\xb6", "\x00\xb7",
60
+ "\x00\xb8", "\x00\xb9", "\x00\xba", "\x00\xbb", "\x00\xbc", "\x00\xbd", "\x00\xbe", "\x00\xbf",
61
+ "\x00\xc0", "\x00\xc1", "\x00\xc2", "\x00\xc3", "\x00\xc4", "\x00\xc5", "\x00\xc6", "\x00\xc7",
62
+ "\x00\xc8", "\x00\xc9", "\x00\xca", "\x00\xcb", "\x00\xcc", "\x00\xcd", "\x00\xce", "\x00\xcf",
63
+ "\x00\xd0", "\x00\xd1", "\x00\xd2", "\x00\xd3", "\x00\xd4", "\x00\xd5", "\x00\xd6", "\x00\xd7",
64
+ "\x00\xd8", "\x00\xd9", "\x00\xda", "\x00\xdb", "\x00\xdc", "\x00\xdd", "\x00\xde", "\x00\xdf",
65
+ "\x00\xe0", "\x00\xe1", "\x00\xe2", "\x00\xe3", "\x00\xe4", "\x00\xe5", "\x00\xe6", "\x00\xe7",
66
+ "\x00\xe8", "\x00\xe9", "\x00\xea", "\x00\xeb", "\x00\xec", "\x00\xed", "\x00\xee", "\x00\xef",
67
+ "\x00\xf0", "\x00\xf1", "\x00\xf2", "\x00\xf3", "\x00\xf4", "\x00\xf5", "\x00\xf6", "\x00\xf7",
68
+ "\x00\xf8", "\x00\xf9", "\x00\xfa", "\x00\xfb", "\x00\xfc", "\x00\xfd", "\x00\xfe", "\x00\xff"
69
+ ].map(&:b)
70
+
71
+ def PDFDocEncoding.to_utf16be(pdfdocstr)
72
+ utf16bestr = UTF16BE::BOM.dup
73
+ pdfdocstr.each_byte do |byte|
74
+ utf16bestr << CHARMAP[byte]
75
+ end
76
+
77
+ utf16bestr.force_encoding('binary')
78
+ end
79
+
80
+ def PDFDocEncoding.to_pdfdoc(str)
81
+ str
82
+ end
83
+ end
84
+
85
+ module UTF16BE
86
+ BOM = "\xFE\xFF".b
87
+
88
+ def UTF16BE.to_utf16be(str)
89
+ str
90
+ end
91
+
92
+ def UTF16BE.to_pdfdoc(str)
93
+ pdfdoc = []
94
+ i = 2
95
+
96
+ while i < str.size
97
+ char = PDFDocEncoding::CHARMAP.index(str[i,2])
98
+ raise EncodingError, "Can't convert UTF16-BE character to PDFDocEncoding" if char.nil?
99
+ pdfdoc << char
100
+ i = i + 2
101
+ end
102
+
103
+ pdfdoc.pack("C*")
104
+ end
105
+ end
106
+ end
107
+
108
+ include Origami::Object
109
+
110
+ attr_accessor :encoding
111
+
112
+ def initialize(str) #:nodoc:
113
+ super(str.force_encoding('binary'))
114
+
115
+ detect_encoding
116
+ end
117
+
118
+ #
119
+ # Convert String object to an UTF8 encoded Ruby string.
120
+ #
121
+ def to_utf8
122
+ detect_encoding
123
+
124
+ utf16 = self.encoding.to_utf16be(self.value)
125
+ utf16.slice!(0, Encoding::UTF16BE::BOM.size)
126
+
127
+ utf16.encode("utf-8", "utf-16be")
128
+ end
129
+
130
+ #
131
+ # Convert String object to an UTF16-BE encoded binary Ruby string.
132
+ #
133
+ def to_utf16be
134
+ detect_encoding
135
+ self.encoding.to_utf16be(self.value)
136
+ end
137
+
138
+ #
139
+ # Convert String object to a PDFDocEncoding encoded binary Ruby string.
140
+ #
141
+ def to_pdfdoc
142
+ detect_encoding
143
+ self.encoding.to_pdfdoc(self.value)
144
+ end
145
+
146
+ def detect_encoding #:nodoc:
147
+ if self.value[0,2] == Encoding::UTF16BE::BOM
148
+ @encoding = Encoding::UTF16BE
149
+ else
150
+ @encoding = Encoding::PDFDocEncoding
151
+ end
152
+ end
153
+ end
154
+
155
+ class InvalidHexaStringObjectError < InvalidObjectError #:nodoc:
156
+ end
157
+
158
+ #
159
+ # Class representing an hexadecimal-writen String Object.
160
+ #
161
+ class HexaString < ::String
162
+ include String
163
+
164
+ TOKENS = %w{ < > } #:nodoc:
165
+
166
+ @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
167
+ @@regexp_close = Regexp.new(TOKENS.last)
168
+
169
+ #
170
+ # Creates a new PDF hexadecimal String.
171
+ # _str_:: The string value.
172
+ #
173
+ def initialize(str = "")
174
+ unless str.is_a?(::String)
175
+ raise TypeError, "Expected type String, received #{str.class}."
176
+ end
177
+
178
+ super(str)
179
+ end
180
+
181
+ def self.parse(stream, _parser = nil) #:nodoc:
182
+ scanner = Parser.init_scanner(stream)
183
+ offset = scanner.pos
184
+
185
+ if scanner.skip(@@regexp_open).nil?
186
+ raise InvalidHexaStringObjectError, "Hexadecimal string shall start with a '#{TOKENS.first}' token"
187
+ end
188
+
189
+ hexa = scanner.scan_until(@@regexp_close)
190
+ if hexa.nil?
191
+ raise InvalidHexaStringObjectError, "Hexadecimal string shall end with a '#{TOKENS.last}' token"
192
+ end
193
+
194
+ begin
195
+ decoded = Filter::ASCIIHex.decode(hexa.chomp!(TOKENS.last))
196
+ rescue Filter::InvalidASCIIHexStringError => e
197
+ raise InvalidHexaStringObjectError, e.message
198
+ end
199
+
200
+ hexastr = HexaString.new(decoded)
201
+ hexastr.file_offset = offset
202
+
203
+ hexastr
204
+ end
205
+
206
+ def to_s(eol: $/) #:nodoc:
207
+ super(TOKENS.first + Filter::ASCIIHex.encode(to_str) + TOKENS.last, eol: eol)
208
+ end
209
+
210
+ #
211
+ # Converts self to a literal String.
212
+ #
213
+ def to_literal
214
+ LiteralString.new(self.value)
215
+ end
216
+
217
+ def value
218
+ self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
219
+
220
+ to_str
221
+ end
222
+ end
223
+
224
+ class InvalidLiteralStringObjectError < InvalidObjectError #:nodoc:
225
+ end
226
+
227
+ #
228
+ # Class representing a literal String Object.
229
+ #
230
+ class LiteralString < ::String
231
+ include String
232
+
233
+ TOKENS = %w{ ( ) } #:nodoc:
234
+
235
+ @@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first))
236
+ @@regexp_close = Regexp.new(Regexp.escape(TOKENS.last))
237
+
238
+ #
239
+ # Creates a new PDF String.
240
+ # _str_:: The string value.
241
+ #
242
+ def initialize(str = "")
243
+ unless str.is_a?(::String)
244
+ raise TypeError, "Expected type String, received #{str.class}."
245
+ end
246
+
247
+ super(str)
248
+ end
249
+
250
+ def self.parse(stream, _parser = nil) #:nodoc:
251
+ scanner = Parser.init_scanner(stream)
252
+ offset = scanner.pos
253
+
254
+ unless scanner.skip(@@regexp_open)
255
+ raise InvalidLiteralStringObjectError, "No literal string start token found"
256
+ end
257
+
258
+ result = ""
259
+ depth = 0
260
+ while depth != 0 or scanner.peek(1) != TOKENS.last do
261
+ raise InvalidLiteralStringObjectError, "Non-terminated string" if scanner.eos?
262
+
263
+ c = scanner.get_byte
264
+ case c
265
+ when "\\"
266
+ if scanner.match?(/\d{1,3}/)
267
+ oct = scanner.peek(3).oct.chr
268
+ scanner.pos += 3
269
+ result << oct
270
+ elsif scanner.match?(/((\r?\n)|(\r\n?))/)
271
+ scanner.skip(/((\r?\n)|(\r\n?))/)
272
+ next
273
+ else
274
+ flag = scanner.get_byte
275
+ case flag
276
+ when "n" then result << "\n"
277
+ when "r" then result << "\r"
278
+ when "t" then result << "\t"
279
+ when "b" then result << "\b"
280
+ when "f" then result << "\f"
281
+ else
282
+ result << flag
283
+ end
284
+ end
285
+
286
+ when "(" then
287
+ depth = depth + 1
288
+ result << c
289
+ when ")" then
290
+ depth = depth - 1
291
+ result << c
292
+ else
293
+ result << c
294
+ end
295
+ end
296
+
297
+ unless scanner.skip(@@regexp_close)
298
+ raise InvalidLiteralStringObjectError, "Byte string shall be terminated with '#{TOKENS.last}'"
299
+ end
300
+
301
+ # Try to cast as a Date object if possible.
302
+ if result[0, 2] == 'D:'
303
+ begin
304
+ date = Date.parse(result)
305
+ date.file_offset = offset
306
+ return date
307
+ rescue InvalidDateError
308
+ end
309
+ end
310
+
311
+ bytestr = self.new(result)
312
+ bytestr.file_offset = offset
313
+
314
+ bytestr
315
+ end
316
+
317
+ def to_s(eol: $/) #:nodoc:
318
+ super(TOKENS.first + expand + TOKENS.last, eol: eol)
319
+ end
320
+
321
+ #
322
+ # Converts self to HexaString
323
+ #
324
+ def to_hex
325
+ HexaString.new(self.value)
326
+ end
327
+
328
+ #
329
+ # Returns a standard String representation.
330
+ #
331
+ def value
332
+ self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
333
+
334
+ to_str
335
+ end
336
+
337
+ private
338
+
339
+ def expand #:nodoc:
340
+ self.gsub(/[\n\r\t\b\f()\\]/,
341
+ "\n" => "\\n",
342
+ "\r" => "\\r",
343
+ "\t" => "\\t",
344
+ "\b" => "\\b",
345
+ "\f" => "\\f",
346
+ "\\" => "\\\\",
347
+ "(" => "\\(",
348
+ ")" => "\\)")
349
+ end
350
+ end
351
+
352
+ class InvalidDateError < Error #:nodoc:
353
+ end
354
+
355
+ #
356
+ # Class representing a Date string.
357
+ #
358
+ class Date < LiteralString #:nodoc:
359
+
360
+ REGEXP_TOKEN =
361
+ /D: # Date header
362
+ (?<year>\d{4}) # Year
363
+ (?<month>\d{2})? # Month
364
+ (?<day>\d{2})? # Day
365
+ (?<hour>\d{2})? # Hour
366
+ (?<min>\d{2})? # Minute
367
+ (?<sec>\d{2})? # Second
368
+ (?:
369
+ (?<ut>[\+\-Z]) # UT relationship
370
+ (?<ut_hour_off>\d{2}) # UT hour offset
371
+ ('(?<ut_min_off>\d{2}))? # UT minute offset
372
+ )?
373
+ /x
374
+
375
+ attr_reader :year, :month, :day, :hour, :min, :sec, :utc_offset
376
+
377
+ def initialize(year:, month: 1, day: 1, hour: 0, min: 0, sec: 0, utc_offset: 0)
378
+ @year, @month, @day, @hour, @min, @sec = year, month, day, hour, min, sec
379
+ @utc_offset = utc_offset
380
+
381
+ date = "D:%04d%02d%02d%02d%02d%02d" % [year, month, day, hour, min, sec ]
382
+
383
+ if utc_offset == 0
384
+ date << "Z00'00"
385
+ else
386
+ date << (if utc_offset < 0 then '-' else '+' end)
387
+ off_hours, off_secs = utc_offset.abs.divmod(3600)
388
+ off_mins = off_secs / 60
389
+ date << "%02d'%02d" % [ off_hours, off_mins ]
390
+ end
391
+
392
+ super(date)
393
+ end
394
+
395
+ def to_datetime
396
+ ::DateTime.new(@year, @month, @day, @hour, @min, @sec, (@utc_offset / 3600).to_s)
397
+ end
398
+
399
+ def self.parse(str) #:nodoc:
400
+ raise InvalidDateError, "Not a valid Date string" unless str =~ REGEXP_TOKEN
401
+
402
+ date =
403
+ {
404
+ year: $~['year'].to_i
405
+ }
406
+
407
+ date[:month] = $~['month'].to_i if $~['month']
408
+ date[:day] = $~['day'].to_i if $~['day']
409
+ date[:hour] = $~['hour'].to_i if $~['hour']
410
+ date[:min] = $~['min'].to_i if $~['min']
411
+ date[:sec] = $~['sec'].to_i if $~['sec']
412
+
413
+ if %w[+ -].include?($~['ut'])
414
+ utc_offset = $~['ut_hour_off'].to_i * 3600 + $~['ut_min_off'].to_i * 60
415
+ utc_offset = -utc_offset if $~['ut'] == '-'
416
+
417
+ date[:utc_offset] = utc_offset
418
+ end
419
+
420
+ Origami::Date.new(**date)
421
+ end
422
+
423
+ #
424
+ # Returns current Date String in UTC time.
425
+ #
426
+ def self.now
427
+ now = Time.now.utc
428
+
429
+ date =
430
+ {
431
+ year: now.strftime("%Y").to_i,
432
+ month: now.strftime("%m").to_i,
433
+ day: now.strftime("%d").to_i,
434
+ hour: now.strftime("%H").to_i,
435
+ min: now.strftime("%M").to_i,
436
+ sec: now.strftime("%S").to_i,
437
+ utc_offset: now.utc_offset
438
+ }
439
+
440
+ Origami::Date.new(**date)
441
+ end
442
+ end
443
+
444
+ end
@@ -0,0 +1,56 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ module Origami
22
+
23
+ module Template
24
+
25
+ class AxialGradient < Graphics::Pattern::Shading::Axial
26
+ def initialize(from, to, color0, color1, coeff = 1)
27
+ super()
28
+
29
+ set_indirect(true)
30
+
31
+ x, y = from
32
+ tx, ty = to
33
+
34
+ c0 = Graphics::Color.to_a(color0)
35
+ c1 = Graphics::Color.to_a(color1)
36
+
37
+ space =
38
+ case c0.size
39
+ when 1 then Graphics::Color::Space::DEVICE_GRAY
40
+ when 3 then Graphics::Color::Space::DEVICE_RGB
41
+ when 4 then Graphics::Color::Space::DEVICE_CMYK
42
+ end
43
+
44
+ f = Function::Exponential.new
45
+ f.Domain = [ 0.0, 1.0 ]
46
+ f.N = coeff
47
+ f.C0, f.C1 = c0, c1
48
+
49
+ self.ColorSpace = space
50
+ self.Coords = [ x, y, tx, ty ]
51
+ self.Function = f
52
+ self.Extend = [ true, true ]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,151 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ module Origami
22
+
23
+ module Template
24
+
25
+ class Button < Annotation::Widget::PushButton
26
+ def initialize(caption, id: nil, x:, y:, width:, height:)
27
+ super()
28
+
29
+ set_indirect(true)
30
+
31
+ self.set_name(id)
32
+ self.H = Annotation::Widget::Highlight::INVERT
33
+ self.Rect = [ x, y, x + width, y + height ]
34
+ self.F = Annotation::Flags::PRINT
35
+
36
+ appstm = Annotation::AppearanceStream.new.setFilter(:FlateDecode)
37
+ appstm.BBox = [ 0, 0, width, height ]
38
+ appstm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
39
+
40
+ appstm.draw_rectangle(0, 0, width, height,
41
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(0xE6, 0xE6, 0xFA))
42
+
43
+ appstm.draw_polygon([[1,1],[1,height-1],[width-1,height-1],[width-2,height-2],[2,height-2],[2,2]],
44
+ fill: true, stroke: false, fill_color: Graphics::Color::GrayScale.new(1.0))
45
+
46
+ appstm.draw_polygon([[width-1,height-1],[width-1,1],[1,1],[2,2],[width-2,2],[width-2,height-2]],
47
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(130, 130, 130))
48
+
49
+ appstm.draw_rectangle(0.5, 0.5, width-1, height-1,
50
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
51
+
52
+ text_width = 4.75 * caption.length
53
+ appstm.write(caption,
54
+ x: (width - text_width)/2, y: height/2-5, size: 10)
55
+
56
+ appstm.Resources = Resources.new
57
+ set_normal_appearance(appstm)
58
+ end
59
+ end
60
+
61
+ class Edit < Annotation::Widget::Text
62
+ def initialize(id, x:, y:, width:, height:)
63
+ super()
64
+
65
+ set_indirect(true)
66
+
67
+ self.set_name(id)
68
+ self.Rect = [ x, y, x+width, y+height ]
69
+ self.F = Annotation::Flags::PRINT
70
+ self.DA = '/F1 12 Tf 0 g'
71
+
72
+ appstm = Annotation::AppearanceStream.new.setFilter(:FlateDecode)
73
+ appstm.BBox = [ 0, 0, width, height ]
74
+ appstm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
75
+
76
+ appstm.draw_rectangle(0, 0, width, height,
77
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
78
+
79
+ appstm.draw_polygon([[1,1],[1,height-1],[width-1,height-1],[width-2,height-2],[2,height-2],[2,2]],
80
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(130, 130, 130))
81
+
82
+ appstm.draw_polygon([[width-1,height-1],[width-1,1],[1,1],[2,2],[width-2,2],[width-2,height-2]],
83
+ fill: true, stroke: false, fill_color: Graphics::Color::GrayScale.new(1.0))
84
+
85
+ appstm.draw_rectangle(0.5, 0.5, width-1, height-1,
86
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
87
+
88
+ set_normal_appearance(appstm)
89
+ end
90
+ end
91
+
92
+ class MultiLineEdit < Edit
93
+ def initialize(id, x:, y:, width:, height:)
94
+ super(id, x: x, y: y, width: width, height: height)
95
+
96
+ self.Ff ||= 0
97
+ self.Ff |= Annotation::Widget::Text::Flags::MULTILINE
98
+ end
99
+ end
100
+
101
+ class RichTextEdit < MultiLineEdit
102
+ def initialize(id, x: , y:, width:, height:)
103
+ super(id, x: x, y: y, width: width, height: height)
104
+
105
+ self.F |= Annotation::Flags::READONLY
106
+ self.Ff |= (Annotation::Widget::Text::Flags::RICHTEXT | Field::Flags::READONLY)
107
+ end
108
+ end
109
+
110
+ class PasswordEdit < Edit
111
+ def initialize(id, x:, y:, width:, height:)
112
+ super(id, x: x, y: y, width: width, height: height)
113
+
114
+ self.Ff ||= 0
115
+ self.Ff |= Annotation::Widget::Text::Flags::PASSWORD
116
+ end
117
+ end
118
+
119
+ class TextPanel < Annotation::FreeText
120
+ def initialize(id, x:, y:, width:, height:)
121
+ super()
122
+
123
+ set_indirect(true)
124
+
125
+ self.Rect = [ x, y, x + width, y + height ]
126
+ self.F = Annotation::Flags::PRINT
127
+ self.NM = id
128
+ self.DA = '/F1 12 Tf 0 g'
129
+
130
+ appstm = Annotation::AppearanceStream.new.setFilter(:FlateDecode)
131
+ appstm.BBox = [ 0, 0, width, height ]
132
+ appstm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
133
+
134
+ appstm.draw_rectangle(0, 0, width, height,
135
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
136
+
137
+ appstm.draw_polygon([[1,1],[1,height-1],[width-1,height-1],[width-2,height-2],[2,height-2],[2,2]],
138
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(130, 130, 130))
139
+
140
+ appstm.draw_polygon([[width-1,height-1],[width-1,1],[1,1],[2,2],[width-2,2],[width-2,height-2]],
141
+ fill: true, stroke: false, fill_color: Graphics::Color::GrayScale.new(1.0))
142
+
143
+ appstm.draw_rectangle(0.5, 0.5, width-1, height-1,
144
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
145
+
146
+ set_normal_appearance(appstm)
147
+ end
148
+ end
149
+ end
150
+
151
+ end