origami 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -1,433 +1,457 @@
1
1
  =begin
2
2
 
3
- = File
4
- string.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- Origami is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU Lesser General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- Origami is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- GNU Lesser General Public License for more details.
20
-
21
- You should have received a copy of the GNU Lesser General Public License
22
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
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/>.
23
18
 
24
19
  =end
25
20
 
21
+ require 'date'
22
+
26
23
  module Origami
27
24
 
28
- #
29
- # Module common to String objects.
30
- #
31
- module String
32
-
33
- module Encoding
34
- class EncodingError < Exception #:nodoc:
35
- end
36
-
37
- module PDFDocEncoding
38
-
39
- CHARMAP =
40
- [
41
- "\x00\x00", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd",
42
- "\xff\xfd", "\x00\x09", "\x00\x0a", "\xff\xfd", "\x00\x0c", "\x00\x0d", "\xff\xfd", "\xff\xfd",
43
- "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd",
44
- "\x02\xd8", "\x02\xc7", "\x02\xc6", "\x02\xd9", "\x02\xdd", "\x02\xdb", "\x02\xda", "\x02\xdc",
45
- "\x00\x20", "\x00\x21", "\x00\x22", "\x00\x23", "\x00\x24", "\x00\x25", "\x00\x26", "\x00\x27",
46
- "\x00\x28", "\x00\x29", "\x00\x2a", "\x00\x2b", "\x00\x2c", "\x00\x2d", "\x00\x2e", "\x00\x2f",
47
- "\x00\x30", "\x00\x31", "\x00\x32", "\x00\x33", "\x00\x34", "\x00\x35", "\x00\x36", "\x00\x37",
48
- "\x00\x38", "\x00\x39", "\x00\x3a", "\x00\x3b", "\x00\x3c", "\x00\x3d", "\x00\x3e", "\x00\x3f",
49
- "\x00\x40", "\x00\x41", "\x00\x42", "\x00\x43", "\x00\x44", "\x00\x45", "\x00\x46", "\x00\x47",
50
- "\x00\x48", "\x00\x49", "\x00\x4a", "\x00\x4b", "\x00\x4c", "\x00\x4d", "\x00\x4e", "\x00\x4f",
51
- "\x00\x50", "\x00\x51", "\x00\x52", "\x00\x53", "\x00\x54", "\x00\x55", "\x00\x56", "\x00\x57",
52
- "\x00\x58", "\x00\x59", "\x00\x5a", "\x00\x5b", "\x00\x5c", "\x00\x5d", "\x00\x5e", "\x00\x5f",
53
- "\x00\x60", "\x00\x61", "\x00\x62", "\x00\x63", "\x00\x64", "\x00\x65", "\x00\x66", "\x00\x67",
54
- "\x00\x68", "\x00\x69", "\x00\x6a", "\x00\x6b", "\x00\x6c", "\x00\x6d", "\x00\x6e", "\x00\x6f",
55
- "\x00\x70", "\x00\x71", "\x00\x72", "\x00\x73", "\x00\x74", "\x00\x75", "\x00\x76", "\x00\x77",
56
- "\x00\x78", "\x00\x79", "\x00\x7a", "\x00\x7b", "\x00\x7c", "\x00\x7d", "\x00\x7e", "\xff\xfd",
57
- "\x20\x22", "\x20\x20", "\x20\x21", "\x20\x26", "\x20\x14", "\x20\x13", "\x01\x92", "\x20\x44",
58
- "\x20\x39", "\x20\x3a", "\x22\x12", "\x20\x30", "\x20\x1e", "\x20\x1c", "\x20\x1d", "\x20\x18",
59
- "\x20\x19", "\x20\x1a", "\x21\x22", "\xfb\x01", "\xfb\x02", "\x01\x41", "\x01\x52", "\x01\x60",
60
- "\x01\x78", "\x01\x7d", "\x01\x31", "\x01\x42", "\x01\x53", "\x01\x61", "\x01\x7e", "\xff\xfd",
61
- "\x20\xac", "\x00\xa1", "\x00\xa2", "\x00\xa3", "\x00\xa4", "\x00\xa5", "\x00\xa6", "\x00\xa7",
62
- "\x00\xa8", "\x00\xa9", "\x00\xaa", "\x00\xab", "\x00\xac", "\xff\xfd", "\x00\xae", "\x00\xaf",
63
- "\x00\xb0", "\x00\xb1", "\x00\xb2", "\x00\xb3", "\x00\xb4", "\x00\xb5", "\x00\xb6", "\x00\xb7",
64
- "\x00\xb8", "\x00\xb9", "\x00\xba", "\x00\xbb", "\x00\xbc", "\x00\xbd", "\x00\xbe", "\x00\xbf",
65
- "\x00\xc0", "\x00\xc1", "\x00\xc2", "\x00\xc3", "\x00\xc4", "\x00\xc5", "\x00\xc6", "\x00\xc7",
66
- "\x00\xc8", "\x00\xc9", "\x00\xca", "\x00\xcb", "\x00\xcc", "\x00\xcd", "\x00\xce", "\x00\xcf",
67
- "\x00\xd0", "\x00\xd1", "\x00\xd2", "\x00\xd3", "\x00\xd4", "\x00\xd5", "\x00\xd6", "\x00\xd7",
68
- "\x00\xd8", "\x00\xd9", "\x00\xda", "\x00\xdb", "\x00\xdc", "\x00\xdd", "\x00\xde", "\x00\xdf",
69
- "\x00\xe0", "\x00\xe1", "\x00\xe2", "\x00\xe3", "\x00\xe4", "\x00\xe5", "\x00\xe6", "\x00\xe7",
70
- "\x00\xe8", "\x00\xe9", "\x00\xea", "\x00\xeb", "\x00\xec", "\x00\xed", "\x00\xee", "\x00\xef",
71
- "\x00\xf0", "\x00\xf1", "\x00\xf2", "\x00\xf3", "\x00\xf4", "\x00\xf5", "\x00\xf6", "\x00\xf7",
72
- "\x00\xf8", "\x00\xf9", "\x00\xfa", "\x00\xfb", "\x00\xfc", "\x00\xfd", "\x00\xfe", "\x00\xff"
73
- ]
74
-
75
- def PDFDocEncoding.to_utf16be(pdfdocstr)
76
-
77
- utf16bestr = "#{UTF16BE::MAGIC}"
78
- pdfdocstr.each_byte do |byte|
79
- utf16bestr << CHARMAP[byte]
80
- end
81
-
82
- utf16bestr
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
83
106
  end
84
107
 
85
- def PDFDocEncoding.to_pdfdoc(str)
86
- str
108
+ module ClassMethods #:nodoc:all
109
+ def native_type; Origami::String end
87
110
  end
88
111
 
89
- end
112
+ def self.included(receiver) #:nodoc:
113
+ receiver.extend(ClassMethods)
114
+ end
115
+
116
+ def self.native_type; Origami::String end #:nodoc:
117
+
118
+ include Origami::Object
90
119
 
91
- module UTF16BE
120
+ attr_accessor :encoding
92
121
 
93
- MAGIC = "\xFE\xFF"
122
+ def initialize(str) #:nodoc:
123
+ super(str.force_encoding('binary'))
94
124
 
95
- def UTF16BE.to_utf16be(str)
96
- str
125
+ detect_encoding
97
126
  end
98
127
 
99
- def UTF16BE.to_pdfdoc(str)
100
- pdfdoc = []
101
- i = 2
128
+ #
129
+ # Convert String object to an UTF8 encoded Ruby string.
130
+ #
131
+ def to_utf8
132
+ detect_encoding
102
133
 
103
- while i < str.size
104
- char = PDFDocEncoding::CHARMAP.index(str[i,2])
105
- raise EncodingError, "Can't convert UTF16-BE character to PDFDocEncoding" if char.nil?
106
- pdfdoc << char
107
- i = i + 2
108
- end
134
+ utf16 = self.encoding.to_utf16be(self.value)
135
+ utf16.slice!(0, Encoding::UTF16BE::BOM.size)
109
136
 
110
- pdfdoc.pack("C*")
137
+ utf16.encode("utf-8", "utf-16be")
111
138
  end
112
139
 
113
- end
140
+ #
141
+ # Convert String object to an UTF16-BE encoded binary Ruby string.
142
+ #
143
+ def to_utf16be
144
+ detect_encoding
145
+ self.encoding.to_utf16be(self.value)
146
+ end
114
147
 
115
- end
148
+ #
149
+ # Convert String object to a PDFDocEncoding encoded binary Ruby string.
150
+ #
151
+ def to_pdfdoc
152
+ detect_encoding
153
+ self.encoding.to_pdfdoc(self.value)
154
+ end
116
155
 
117
- module ClassMethods #:nodoc:all
118
- def native_type; Origami::String end
156
+ def detect_encoding #:nodoc:
157
+ if self.value[0,2] == Encoding::UTF16BE::BOM
158
+ @encoding = Encoding::UTF16BE
159
+ else
160
+ @encoding = Encoding::PDFDocEncoding
161
+ end
162
+ end
119
163
  end
120
164
 
121
- def self.included(receiver) #:nodoc:
122
- receiver.extend(ClassMethods)
123
- end
124
-
125
- def self.native_type; Origami::String end #:nodoc:
126
-
127
- include Origami::Object
128
-
129
- attr_accessor :encoding
130
-
131
- def initialize(str) #:nodoc:
132
- infer_encoding
133
- super(str)
165
+ class InvalidHexaStringObjectError < InvalidObjectError #:nodoc:
134
166
  end
135
167
 
136
168
  #
137
- # Convert String object to an UTF8 encoded Ruby string.
169
+ # Class representing an hexadecimal-writen String Object.
138
170
  #
139
- def to_utf8
140
- infer_encoding
141
-
142
- if RUBY_VERSION < '1.9'
143
- require 'iconv'
144
- i = Iconv.new("UTF-8", "UTF-16")
145
- utf8str = i.iconv(self.encoding.to_utf16be(self.value))
146
- i.close
147
- else
148
- utf8str = self.encoding.to_utf16be(self.value).encode("utf-8", "utf-16")
149
- end
150
-
151
- utf8str
152
- end
171
+ class HexaString < ::String
172
+ include String
153
173
 
154
- #
155
- # Convert String object to an UTF16-BE encoded Ruby string.
156
- #
157
- def to_utf16be
158
- infer_encoding
159
- self.encoding.to_utf16be(self.value)
160
- end
174
+ TOKENS = %w{ < > } #:nodoc:
161
175
 
162
- #
163
- # Convert String object to a PDFDocEncoding encoded Ruby string.
164
- #
165
- def to_pdfdoc
166
- infer_encoding
167
- self.encoding.to_pdfdoc(self.value)
168
- end
176
+ @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
177
+ @@regexp_close = Regexp.new(TOKENS.last)
169
178
 
170
- def infer_encoding #:nodoc:
171
- @encoding =
172
- if self.value[0,2] == Encoding::UTF16BE::MAGIC
173
- Encoding::UTF16BE
174
- else
175
- Encoding::PDFDocEncoding
176
- end
177
- end
178
- end
179
-
180
- class InvalidHexaStringObjectError < InvalidObjectError #:nodoc:
181
- end
182
-
183
- #
184
- # Class representing an hexadecimal-writen String Object.
185
- #
186
- class HexaString < ::String
187
- include String
188
-
189
- TOKENS = %w{ < > } #:nodoc:
190
-
191
- @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
192
- @@regexp_close = Regexp.new(TOKENS.last)
179
+ #
180
+ # Creates a new PDF hexadecimal String.
181
+ # _str_:: The string value.
182
+ #
183
+ def initialize(str = "")
184
+ unless str.is_a?(::String)
185
+ raise TypeError, "Expected type String, received #{str.class}."
186
+ end
193
187
 
194
- #
195
- # Creates a new PDF hexadecimal String.
196
- # _str_:: The string value.
197
- #
198
- def initialize(str = "")
199
-
200
- unless str.is_a?(::String)
201
- raise TypeError, "Expected type String, received #{str.class}."
202
- end
203
-
204
- super(str)
205
- end
206
-
207
- def self.parse(stream, parser = nil) #:nodoc:
208
-
209
- offset = stream.pos
210
-
211
- if stream.skip(@@regexp_open).nil?
212
- raise InvalidHexaStringObjectError, "Hexadecimal string shall start with a '#{TOKENS.first}' token"
213
- end
214
-
215
- hexa = stream.scan_until(@@regexp_close)
216
- if hexa.nil?
217
- raise InvalidHexaStringObjectError, "Hexadecimal string shall end with a '#{TOKENS.last}' token"
218
- end
219
-
220
- decoded = Filter::ASCIIHex.decode(hexa.chomp!(TOKENS.last))
221
-
222
- hexastr = HexaString.new(decoded)
223
- hexastr.file_offset = offset
224
-
225
- hexastr
226
- end
227
-
228
- def to_s #:nodoc:
229
- super(TOKENS.first + Filter::ASCIIHex.encode(to_str) + TOKENS.last)
230
- end
188
+ super(str)
189
+ end
231
190
 
232
- #
233
- # Converts self to ByteString
234
- #
235
- def to_raw
236
- ByteString.new(self.value)
237
- end
191
+ def self.parse(stream, parser = nil) #:nodoc:
192
+ offset = stream.pos
193
+
194
+ if stream.skip(@@regexp_open).nil?
195
+ raise InvalidHexaStringObjectError, "Hexadecimal string shall start with a '#{TOKENS.first}' token"
196
+ end
197
+
198
+ hexa = stream.scan_until(@@regexp_close)
199
+ if hexa.nil?
200
+ raise InvalidHexaStringObjectError, "Hexadecimal string shall end with a '#{TOKENS.last}' token"
201
+ end
202
+
203
+ decoded = Filter::ASCIIHex.decode(hexa.chomp!(TOKENS.last))
204
+
205
+ hexastr = HexaString.new(decoded)
206
+ hexastr.file_offset = offset
238
207
 
239
- def value
240
- self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
208
+ hexastr
209
+ end
210
+
211
+ def to_s #:nodoc:
212
+ super(TOKENS.first + Filter::ASCIIHex.encode(to_str) + TOKENS.last)
213
+ end
241
214
 
242
- to_str
215
+ #
216
+ # Converts self to a literal String.
217
+ #
218
+ def to_literal
219
+ LiteralString.new(self.value)
220
+ end
221
+
222
+ def value
223
+ self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
224
+
225
+ to_str
226
+ end
227
+ end
228
+
229
+ class InvalidLiteralStringObjectError < InvalidObjectError #:nodoc:
243
230
  end
244
231
 
245
- end
246
-
247
- class InvalidByteStringObjectError < InvalidObjectError #:nodoc:
248
- end
249
-
250
- #
251
- # Class representing an ASCII String Object.
252
- #
253
- class ByteString < ::String
254
-
255
- include String
256
-
257
- TOKENS = %w{ ( ) } #:nodoc:
258
-
259
- @@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first))
260
- @@regexp_close = Regexp.new(Regexp.escape(TOKENS.last))
261
-
262
232
  #
263
- # Creates a new PDF String.
264
- # _str_:: The string value.
233
+ # Class representing a literal String Object.
265
234
  #
266
- def initialize(str = "")
267
-
268
- unless str.is_a?(::String)
269
- raise TypeError, "Expected type String, received #{str.class}."
270
- end
271
-
272
- super(str)
273
- end
235
+ class LiteralString < ::String
236
+ include String
274
237
 
275
- def self.parse(stream, parser = nil) #:nodoc:
238
+ TOKENS = %w{ ( ) } #:nodoc:
276
239
 
277
- offset = stream.pos
278
-
279
- if not stream.skip(@@regexp_open)
280
- raise InvalidByteStringObjectError, "No literal string start token found"
281
- end
282
-
283
- result = ""
284
- depth = 0
285
- while depth != 0 or stream.peek(1) != TOKENS.last do
240
+ @@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first))
241
+ @@regexp_close = Regexp.new(Regexp.escape(TOKENS.last))
286
242
 
287
- if stream.eos?
288
- raise InvalidByteStringObjectError, "Non-terminated string"
243
+ #
244
+ # Creates a new PDF String.
245
+ # _str_:: The string value.
246
+ #
247
+ def initialize(str = "")
248
+ unless str.is_a?(::String)
249
+ raise TypeError, "Expected type String, received #{str.class}."
250
+ end
251
+
252
+ super(str)
289
253
  end
290
254
 
291
- c = stream.get_byte
292
- case c
293
- when "\\"
294
- if stream.match?(/\d{1,3}/)
295
- oct = stream.peek(3).oct.chr
296
- stream.pos += 3
297
- result << oct
298
- elsif stream.match?(/((\r?\n)|(\r\n?))/)
299
-
300
- stream.skip(/((\r?\n)|(\r\n?))/)
301
- next
302
-
303
- else
304
- flag = stream.get_byte
305
- case flag
306
- when "n" then result << "\n"
307
- when "r" then result << "\r"
308
- when "t" then result << "\t"
309
- when "b" then result << "\b"
310
- when "f" then result << "\f"
311
- when "(" then result << "("
312
- when ")" then result << ")"
313
- when "\\" then result << "\\"
314
- when "\r"
315
- if str.peek(1) == "\n" then stream.pos += 1 end
316
- when "\n"
317
- else
318
- result << flag
255
+ def self.parse(stream, parser = nil) #:nodoc:
256
+ offset = stream.pos
257
+
258
+ unless stream.skip(@@regexp_open)
259
+ raise InvalidLiteralStringObjectError, "No literal string start token found"
260
+ end
261
+
262
+ result = ""
263
+ depth = 0
264
+ while depth != 0 or stream.peek(1) != TOKENS.last do
265
+ raise InvalidLiteralStringObjectError, "Non-terminated string" if stream.eos?
266
+
267
+ c = stream.get_byte
268
+ case c
269
+ when "\\"
270
+ if stream.match?(/\d{1,3}/)
271
+ oct = stream.peek(3).oct.chr
272
+ stream.pos += 3
273
+ result << oct
274
+ elsif stream.match?(/((\r?\n)|(\r\n?))/)
275
+ stream.skip(/((\r?\n)|(\r\n?))/)
276
+ next
277
+ else
278
+ flag = stream.get_byte
279
+ case flag
280
+ when "n" then result << "\n"
281
+ when "r" then result << "\r"
282
+ when "t" then result << "\t"
283
+ when "b" then result << "\b"
284
+ when "f" then result << "\f"
285
+ when "(" then result << "("
286
+ when ")" then result << ")"
287
+ when "\\" then result << "\\"
288
+ when "\r"
289
+ stream.pos += 1 if stream.peek(1) == "\n"
290
+ when "\n"
291
+ else
292
+ result << flag
293
+ end
294
+ end
295
+
296
+ when "(" then
297
+ depth = depth + 1
298
+ result << c
299
+ when ")" then
300
+ depth = depth - 1
301
+ result << c
302
+ else
303
+ result << c
304
+ end
305
+ end
306
+
307
+ unless stream.skip(@@regexp_close)
308
+ raise InvalidLiteralStringObjectError, "Byte string shall be terminated with '#{TOKENS.last}'"
309
+ end
310
+
311
+ # Try to cast as a Date object if possible.
312
+ if result[0, 2] == 'D:'
313
+ begin
314
+ date = Date.parse(result)
315
+ date.file_offset = offset
316
+ return date
317
+ rescue InvalidDateError
318
+ end
319
319
  end
320
- end
321
-
322
- when "(" then
323
- depth = depth + 1
324
- result << c
325
- when ")" then
326
- depth = depth - 1
327
- result << c
328
- else
329
- result << c
320
+
321
+ bytestr = self.new(result)
322
+ bytestr.file_offset = offset
323
+
324
+ bytestr
330
325
  end
331
326
 
332
- end
327
+ def expand #:nodoc:
328
+ extended = self.gsub("\\", "\\\\\\\\")
329
+ extended.gsub!(/\)/, "\\)")
330
+ extended.gsub!("\n", "\\n")
331
+ extended.gsub!("\r", "\\r")
332
+ extended.gsub!(/\(/, "\\(")
333
333
 
334
- if not stream.skip(@@regexp_close)
335
- raise InvalidByteStringObjectError, "Byte string shall be terminated with '#{TOKENS.last}'"
336
- end
334
+ extended
335
+ end
337
336
 
338
- bytestr = ByteString.new(result)
339
- bytestr.file_offset
337
+ def to_s #:nodoc:
338
+ super(TOKENS.first + self.expand + TOKENS.last)
339
+ end
340
340
 
341
- bytestr
342
- end
341
+ #
342
+ # Converts self to HexaString
343
+ #
344
+ def to_hex
345
+ HexaString.new(self.value)
346
+ end
343
347
 
344
- def expand #:nodoc:
345
-
346
- extended = self.gsub("\\", "\\\\\\\\")
347
- extended.gsub!(/\)/, "\\)")
348
- extended.gsub!("\n", "\\n")
349
- extended.gsub!("\r", "\\r")
350
- extended.gsub!(/\(/, "\\(")
351
-
352
- extended
353
- end
354
-
355
- def to_s #:nodoc:
356
- super(TOKENS.first + self.expand + TOKENS.last)
357
- end
348
+ #
349
+ # Returns a standard String representation.
350
+ #
351
+ def value
352
+ self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
358
353
 
359
- #
360
- # Converts self to HexaString
361
- #
362
- def to_hex
363
- HexaString.new(self.value)
354
+ to_str
355
+ end
364
356
  end
365
-
366
- def value
367
- self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
368
357
 
369
- to_str
370
- end
371
- end
372
-
373
- #
374
- # Class representing a Date string.
375
- # _Not used_
376
- # _Not tested_
377
- #
378
- class Date < ByteString #:nodoc:
379
-
380
- REGEXP_TOKEN = "(D:)?(\\d{4})(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?(?:([\\+-Z])(?:(\\d{2})')?(?:(\\d{2})')?)?"
381
-
382
- def initialize(year, month = nil, day = nil, hour = nil, minute = nil, second = nil, ut_sign = nil, ut_hours = nil, ut_min = nil)
383
-
384
- year_str = '%04d' % year
385
- month_str = month.nil? ? '01' : '%02d' % month
386
- day_str = day.nil? ? '01' : '%02d' % day
387
- hour_str = '%02d' % hour
388
- minute_str = '%02d' % minute
389
- second_str = '%02d' % second
390
-
391
- date_str = "D:#{year_str}#{month_str}#{day_str}#{hour_str}#{minute_str}#{second_str}"
392
- date_str << "#{ut_sign}#{'%02d' % ut_hours}'#{'%02d' % ut_min}" unless ut_sign.nil?
393
-
394
- super(date_str)
358
+ class InvalidDateError < Error #:nodoc:
395
359
  end
396
-
397
- def self.parse(stream, parser = nil) #:nodoc:
398
-
399
- dateReg = Regexp.new(REGEXP_TOKEN)
400
-
401
- raise InvalidDate if stream.scan(dateReg).nil?
402
-
403
- year = stream[2].to_i
404
- month = stream[3] and stream[3].to_i
405
- day = stream[4] and stream[4].to_i
406
- hour = stream[5] and stream[5].to_i
407
- min = stream[6] and stream[6].to_i
408
- sec = stream[7] and stream[7].to_i
409
- ut_sign = stream[8]
410
- ut_hours = stream[9] and stream[9].to_i
411
- ut_min = stream[10] and stream[10].to_i
412
-
413
- Origami::Date.new(year, month, day, hour, min, sec, ut_sign, ut_hours, ut_min)
414
- end
415
-
360
+
416
361
  #
417
- # Returns current Date String in UTC time.
362
+ # Class representing a Date string.
418
363
  #
419
- def self.now
420
- now = Time.now.getutc
421
- year = now.strftime("%Y").to_i
422
- month = now.strftime("%m").to_i
423
- day = now.strftime("%d").to_i
424
- hour = now.strftime("%H").to_i
425
- min = now.strftime("%M").to_i
426
- sec = now.strftime("%S").to_i
427
-
428
- Origami::Date.new(year, month, day, hour, min, sec, 'Z', 0, 0)
364
+ class Date < LiteralString #:nodoc:
365
+
366
+ REGEXP_TOKEN =
367
+ /D: # Date header
368
+ (?<year>\d{4}) # Year
369
+ (?<month>\d{2})? # Month
370
+ (?<day>\d{2})? # Day
371
+ (?<hour>\d{2})? # Hour
372
+ (?<min>\d{2})? # Minute
373
+ (?<sec>\d{2})? # Second
374
+ (?:
375
+ (?<ut>[\+\-Z]) # UT relationship
376
+ (?<ut_hour_off>\d{2}) # UT hour offset
377
+ ('(?<ut_min_off>\d{2}))? # UT minute offset
378
+ )?
379
+ /x
380
+
381
+ attr_reader :year, :month, :day, :hour, :min, :sec, :utc_offset
382
+
383
+ def initialize(year:, month: 1, day: 1, hour: 0, min: 0, sec: 0, utc_offset: 0)
384
+ raise InvalidDateError, "Invalid year #{year}" unless (0..9999) === year
385
+ raise InvalidDateError, "Invalid month #{month}" unless (1..12) === month
386
+ raise InvalidDateError, "Invalid day #{day}" unless (1..31) === day
387
+ raise InvalidDateError, "Invalid hour #{hour}" unless (0..23) === hour
388
+ raise InvalidDateError, "Invalid minute #{min}" unless (0..59) === min
389
+ raise InvalidDateError, "Invalid second #{sec}" unless (0..59) === sec
390
+
391
+ @year, @month, @day, @hour, @min, @sec = year, month, day, hour, min, sec
392
+ @utc_offset = utc_offset
393
+
394
+ date = "D:%04d%02d%02d%02d%02d%02d" % [year, month, day, hour, min, sec ]
395
+
396
+ if utc_offset == 0
397
+ date << "Z00'00"
398
+ else
399
+ date << (if utc_offset < 0 then '-' else '+' end)
400
+ off_hours, off_secs = utc_offset.abs.divmod(3600)
401
+ off_mins = off_secs / 60
402
+ date << "%02d'%02d" % [ off_hours, off_mins ]
403
+ end
404
+
405
+ super(date)
406
+ end
407
+
408
+ def to_datetime
409
+ ::DateTime.new(@year, @month, @day, @hour, @min, @sec, (@utc_offset / 3600).to_s)
410
+ end
411
+
412
+ def self.parse(str) #:nodoc:
413
+ raise InvalidDateError, "Not a valid Date string" unless str =~ REGEXP_TOKEN
414
+
415
+ date =
416
+ {
417
+ year: $~['year'].to_i
418
+ }
419
+
420
+ date[:month] = $~['month'].to_i if $~['month']
421
+ date[:day] = $~['day'].to_i if $~['day']
422
+ date[:hour] = $~['hour'].to_i if $~['hour']
423
+ date[:min] = $~['min'].to_i if $~['min']
424
+ date[:sec] = $~['sec'].to_i if $~['sec']
425
+
426
+ if %w[+ -].include?($~['ut'])
427
+ utc_offset = $~['ut_hour_off'].to_i * 3600 + $~['ut_min_off'].to_i * 60
428
+ utc_offset = -utc_offset if $~['ut'] == '-'
429
+
430
+ date[:utc_offset] = utc_offset
431
+ end
432
+
433
+ Origami::Date.new(date)
434
+ end
435
+
436
+ #
437
+ # Returns current Date String in UTC time.
438
+ #
439
+ def self.now
440
+ now = Time.now.utc
441
+
442
+ date =
443
+ {
444
+ year: now.strftime("%Y").to_i,
445
+ month: now.strftime("%m").to_i,
446
+ day: now.strftime("%d").to_i,
447
+ hour: now.strftime("%H").to_i,
448
+ min: now.strftime("%M").to_i,
449
+ sec: now.strftime("%S").to_i,
450
+ utc_offset: now.utc_offset
451
+ }
452
+
453
+ Origami::Date.new(date)
454
+ end
429
455
  end
430
-
431
- end
432
456
 
433
457
  end