hexapdf 0.1.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 (346) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTERS +3 -0
  3. data/LICENSE +26 -0
  4. data/README.md +88 -0
  5. data/Rakefile +121 -0
  6. data/VERSION +1 -0
  7. data/agpl-3.0.txt +661 -0
  8. data/bin/hexapdf +6 -0
  9. data/data/hexapdf/afm/Courier-Bold.afm +342 -0
  10. data/data/hexapdf/afm/Courier-BoldOblique.afm +342 -0
  11. data/data/hexapdf/afm/Courier-Oblique.afm +342 -0
  12. data/data/hexapdf/afm/Courier.afm +342 -0
  13. data/data/hexapdf/afm/Helvetica-Bold.afm +2827 -0
  14. data/data/hexapdf/afm/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/hexapdf/afm/Helvetica-Oblique.afm +3051 -0
  16. data/data/hexapdf/afm/Helvetica.afm +3051 -0
  17. data/data/hexapdf/afm/MustRead.html +1 -0
  18. data/data/hexapdf/afm/Symbol.afm +213 -0
  19. data/data/hexapdf/afm/Times-Bold.afm +2588 -0
  20. data/data/hexapdf/afm/Times-BoldItalic.afm +2384 -0
  21. data/data/hexapdf/afm/Times-Italic.afm +2667 -0
  22. data/data/hexapdf/afm/Times-Roman.afm +2419 -0
  23. data/data/hexapdf/afm/ZapfDingbats.afm +225 -0
  24. data/data/hexapdf/encoding/glyphlist.txt +4305 -0
  25. data/data/hexapdf/encoding/zapfdingbats.txt +225 -0
  26. data/examples/arc.rb +50 -0
  27. data/examples/graphics.rb +274 -0
  28. data/examples/hello_world.rb +16 -0
  29. data/examples/machupicchu.jpg +0 -0
  30. data/examples/merging.rb +24 -0
  31. data/examples/optimizing.rb +20 -0
  32. data/examples/show_char_bboxes.rb +55 -0
  33. data/examples/standard_pdf_fonts.rb +72 -0
  34. data/examples/truetype.rb +45 -0
  35. data/lib/hexapdf/cli/extract.rb +128 -0
  36. data/lib/hexapdf/cli/info.rb +121 -0
  37. data/lib/hexapdf/cli/inspect.rb +157 -0
  38. data/lib/hexapdf/cli/modify.rb +218 -0
  39. data/lib/hexapdf/cli.rb +121 -0
  40. data/lib/hexapdf/configuration.rb +392 -0
  41. data/lib/hexapdf/content/canvas.rb +1974 -0
  42. data/lib/hexapdf/content/color_space.rb +364 -0
  43. data/lib/hexapdf/content/graphic_object/arc.rb +267 -0
  44. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +208 -0
  45. data/lib/hexapdf/content/graphic_object/solid_arc.rb +173 -0
  46. data/lib/hexapdf/content/graphic_object.rb +81 -0
  47. data/lib/hexapdf/content/graphics_state.rb +579 -0
  48. data/lib/hexapdf/content/operator.rb +1072 -0
  49. data/lib/hexapdf/content/parser.rb +204 -0
  50. data/lib/hexapdf/content/processor.rb +451 -0
  51. data/lib/hexapdf/content/transformation_matrix.rb +172 -0
  52. data/lib/hexapdf/content.rb +47 -0
  53. data/lib/hexapdf/data_dir.rb +51 -0
  54. data/lib/hexapdf/dictionary.rb +303 -0
  55. data/lib/hexapdf/dictionary_fields.rb +382 -0
  56. data/lib/hexapdf/document.rb +589 -0
  57. data/lib/hexapdf/document_utils.rb +209 -0
  58. data/lib/hexapdf/encryption/aes.rb +206 -0
  59. data/lib/hexapdf/encryption/arc4.rb +93 -0
  60. data/lib/hexapdf/encryption/fast_aes.rb +79 -0
  61. data/lib/hexapdf/encryption/fast_arc4.rb +67 -0
  62. data/lib/hexapdf/encryption/identity.rb +63 -0
  63. data/lib/hexapdf/encryption/ruby_aes.rb +447 -0
  64. data/lib/hexapdf/encryption/ruby_arc4.rb +96 -0
  65. data/lib/hexapdf/encryption/security_handler.rb +494 -0
  66. data/lib/hexapdf/encryption/standard_security_handler.rb +616 -0
  67. data/lib/hexapdf/encryption.rb +94 -0
  68. data/lib/hexapdf/error.rb +73 -0
  69. data/lib/hexapdf/filter/ascii85_decode.rb +160 -0
  70. data/lib/hexapdf/filter/ascii_hex_decode.rb +87 -0
  71. data/lib/hexapdf/filter/dct_decode.rb +57 -0
  72. data/lib/hexapdf/filter/encryption.rb +59 -0
  73. data/lib/hexapdf/filter/flate_decode.rb +93 -0
  74. data/lib/hexapdf/filter/jpx_decode.rb +56 -0
  75. data/lib/hexapdf/filter/lzw_decode.rb +191 -0
  76. data/lib/hexapdf/filter/predictor.rb +266 -0
  77. data/lib/hexapdf/filter/run_length_decode.rb +108 -0
  78. data/lib/hexapdf/filter.rb +176 -0
  79. data/lib/hexapdf/font/cmap/parser.rb +146 -0
  80. data/lib/hexapdf/font/cmap/writer.rb +176 -0
  81. data/lib/hexapdf/font/cmap.rb +90 -0
  82. data/lib/hexapdf/font/encoding/base.rb +77 -0
  83. data/lib/hexapdf/font/encoding/difference_encoding.rb +64 -0
  84. data/lib/hexapdf/font/encoding/glyph_list.rb +150 -0
  85. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +221 -0
  86. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +265 -0
  87. data/lib/hexapdf/font/encoding/standard_encoding.rb +205 -0
  88. data/lib/hexapdf/font/encoding/symbol_encoding.rb +244 -0
  89. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +280 -0
  90. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +250 -0
  91. data/lib/hexapdf/font/encoding.rb +68 -0
  92. data/lib/hexapdf/font/true_type/font.rb +179 -0
  93. data/lib/hexapdf/font/true_type/table/cmap.rb +103 -0
  94. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +384 -0
  95. data/lib/hexapdf/font/true_type/table/directory.rb +92 -0
  96. data/lib/hexapdf/font/true_type/table/glyf.rb +166 -0
  97. data/lib/hexapdf/font/true_type/table/head.rb +143 -0
  98. data/lib/hexapdf/font/true_type/table/hhea.rb +109 -0
  99. data/lib/hexapdf/font/true_type/table/hmtx.rb +79 -0
  100. data/lib/hexapdf/font/true_type/table/loca.rb +79 -0
  101. data/lib/hexapdf/font/true_type/table/maxp.rb +112 -0
  102. data/lib/hexapdf/font/true_type/table/name.rb +218 -0
  103. data/lib/hexapdf/font/true_type/table/os2.rb +200 -0
  104. data/lib/hexapdf/font/true_type/table/post.rb +230 -0
  105. data/lib/hexapdf/font/true_type/table.rb +155 -0
  106. data/lib/hexapdf/font/true_type.rb +48 -0
  107. data/lib/hexapdf/font/true_type_wrapper.rb +240 -0
  108. data/lib/hexapdf/font/type1/afm_parser.rb +230 -0
  109. data/lib/hexapdf/font/type1/character_metrics.rb +67 -0
  110. data/lib/hexapdf/font/type1/font.rb +123 -0
  111. data/lib/hexapdf/font/type1/font_metrics.rb +117 -0
  112. data/lib/hexapdf/font/type1/pfb_parser.rb +71 -0
  113. data/lib/hexapdf/font/type1.rb +52 -0
  114. data/lib/hexapdf/font/type1_wrapper.rb +193 -0
  115. data/lib/hexapdf/font_loader/from_configuration.rb +70 -0
  116. data/lib/hexapdf/font_loader/standard14.rb +98 -0
  117. data/lib/hexapdf/font_loader.rb +85 -0
  118. data/lib/hexapdf/font_utils.rb +89 -0
  119. data/lib/hexapdf/image_loader/jpeg.rb +166 -0
  120. data/lib/hexapdf/image_loader/pdf.rb +89 -0
  121. data/lib/hexapdf/image_loader/png.rb +410 -0
  122. data/lib/hexapdf/image_loader.rb +68 -0
  123. data/lib/hexapdf/importer.rb +139 -0
  124. data/lib/hexapdf/name_tree_node.rb +78 -0
  125. data/lib/hexapdf/number_tree_node.rb +67 -0
  126. data/lib/hexapdf/object.rb +363 -0
  127. data/lib/hexapdf/parser.rb +349 -0
  128. data/lib/hexapdf/rectangle.rb +99 -0
  129. data/lib/hexapdf/reference.rb +98 -0
  130. data/lib/hexapdf/revision.rb +206 -0
  131. data/lib/hexapdf/revisions.rb +194 -0
  132. data/lib/hexapdf/serializer.rb +326 -0
  133. data/lib/hexapdf/stream.rb +279 -0
  134. data/lib/hexapdf/task/dereference.rb +109 -0
  135. data/lib/hexapdf/task/optimize.rb +230 -0
  136. data/lib/hexapdf/task.rb +68 -0
  137. data/lib/hexapdf/tokenizer.rb +406 -0
  138. data/lib/hexapdf/type/catalog.rb +107 -0
  139. data/lib/hexapdf/type/embedded_file.rb +87 -0
  140. data/lib/hexapdf/type/file_specification.rb +232 -0
  141. data/lib/hexapdf/type/font.rb +81 -0
  142. data/lib/hexapdf/type/font_descriptor.rb +109 -0
  143. data/lib/hexapdf/type/font_simple.rb +190 -0
  144. data/lib/hexapdf/type/font_true_type.rb +47 -0
  145. data/lib/hexapdf/type/font_type1.rb +162 -0
  146. data/lib/hexapdf/type/form.rb +103 -0
  147. data/lib/hexapdf/type/graphics_state_parameter.rb +79 -0
  148. data/lib/hexapdf/type/image.rb +73 -0
  149. data/lib/hexapdf/type/info.rb +70 -0
  150. data/lib/hexapdf/type/names.rb +69 -0
  151. data/lib/hexapdf/type/object_stream.rb +224 -0
  152. data/lib/hexapdf/type/page.rb +355 -0
  153. data/lib/hexapdf/type/page_tree_node.rb +269 -0
  154. data/lib/hexapdf/type/resources.rb +212 -0
  155. data/lib/hexapdf/type/trailer.rb +128 -0
  156. data/lib/hexapdf/type/viewer_preferences.rb +73 -0
  157. data/lib/hexapdf/type/xref_stream.rb +204 -0
  158. data/lib/hexapdf/type.rb +67 -0
  159. data/lib/hexapdf/utils/bit_field.rb +87 -0
  160. data/lib/hexapdf/utils/bit_stream.rb +148 -0
  161. data/lib/hexapdf/utils/lru_cache.rb +65 -0
  162. data/lib/hexapdf/utils/math_helpers.rb +55 -0
  163. data/lib/hexapdf/utils/object_hash.rb +130 -0
  164. data/lib/hexapdf/utils/pdf_doc_encoding.rb +93 -0
  165. data/lib/hexapdf/utils/sorted_tree_node.rb +339 -0
  166. data/lib/hexapdf/version.rb +39 -0
  167. data/lib/hexapdf/writer.rb +199 -0
  168. data/lib/hexapdf/xref_section.rb +152 -0
  169. data/lib/hexapdf.rb +34 -0
  170. data/man/man1/hexapdf.1 +249 -0
  171. data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
  172. data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
  173. data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
  174. data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
  175. data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
  176. data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
  177. data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
  178. data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
  179. data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
  180. data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
  181. data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
  182. data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
  183. data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
  184. data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
  185. data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
  186. data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
  187. data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
  188. data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
  189. data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
  190. data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
  191. data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
  192. data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
  193. data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
  194. data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
  195. data/test/data/fonts/Ubuntu-Title.ttf +0 -0
  196. data/test/data/images/cmyk.jpg +0 -0
  197. data/test/data/images/fillbytes.jpg +0 -0
  198. data/test/data/images/gray.jpg +0 -0
  199. data/test/data/images/greyscale-1bit.png +0 -0
  200. data/test/data/images/greyscale-2bit.png +0 -0
  201. data/test/data/images/greyscale-4bit.png +0 -0
  202. data/test/data/images/greyscale-8bit.png +0 -0
  203. data/test/data/images/greyscale-alpha-8bit.png +0 -0
  204. data/test/data/images/greyscale-trns-8bit.png +0 -0
  205. data/test/data/images/greyscale-with-gamma1.0.png +0 -0
  206. data/test/data/images/greyscale-with-gamma1.5.png +0 -0
  207. data/test/data/images/indexed-1bit.png +0 -0
  208. data/test/data/images/indexed-2bit.png +0 -0
  209. data/test/data/images/indexed-4bit.png +0 -0
  210. data/test/data/images/indexed-8bit.png +0 -0
  211. data/test/data/images/indexed-alpha-4bit.png +0 -0
  212. data/test/data/images/indexed-alpha-8bit.png +0 -0
  213. data/test/data/images/rgb.jpg +0 -0
  214. data/test/data/images/truecolour-8bit.png +0 -0
  215. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  216. data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
  217. data/test/data/images/truecolour-srgb-8bit.png +0 -0
  218. data/test/data/minimal.pdf +44 -0
  219. data/test/data/standard-security-handler/README +9 -0
  220. data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
  221. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
  222. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
  223. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
  224. data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
  225. data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
  226. data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
  227. data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
  228. data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
  229. data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
  230. data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
  231. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
  232. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
  233. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
  234. data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
  235. data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
  236. data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
  237. data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
  238. data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
  239. data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
  240. data/test/hexapdf/common_tokenizer_tests.rb +204 -0
  241. data/test/hexapdf/content/common.rb +31 -0
  242. data/test/hexapdf/content/graphic_object/test_arc.rb +93 -0
  243. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +91 -0
  244. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
  245. data/test/hexapdf/content/test_canvas.rb +1113 -0
  246. data/test/hexapdf/content/test_color_space.rb +97 -0
  247. data/test/hexapdf/content/test_graphics_state.rb +138 -0
  248. data/test/hexapdf/content/test_operator.rb +619 -0
  249. data/test/hexapdf/content/test_parser.rb +66 -0
  250. data/test/hexapdf/content/test_processor.rb +156 -0
  251. data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
  252. data/test/hexapdf/encryption/common.rb +87 -0
  253. data/test/hexapdf/encryption/test_aes.rb +121 -0
  254. data/test/hexapdf/encryption/test_arc4.rb +39 -0
  255. data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
  256. data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
  257. data/test/hexapdf/encryption/test_identity.rb +21 -0
  258. data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
  259. data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
  260. data/test/hexapdf/encryption/test_security_handler.rb +356 -0
  261. data/test/hexapdf/encryption/test_standard_security_handler.rb +274 -0
  262. data/test/hexapdf/filter/common.rb +53 -0
  263. data/test/hexapdf/filter/test_ascii85_decode.rb +60 -0
  264. data/test/hexapdf/filter/test_ascii_hex_decode.rb +33 -0
  265. data/test/hexapdf/filter/test_encryption.rb +24 -0
  266. data/test/hexapdf/filter/test_flate_decode.rb +35 -0
  267. data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
  268. data/test/hexapdf/filter/test_predictor.rb +183 -0
  269. data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
  270. data/test/hexapdf/font/cmap/test_parser.rb +67 -0
  271. data/test/hexapdf/font/cmap/test_writer.rb +58 -0
  272. data/test/hexapdf/font/encoding/test_base.rb +35 -0
  273. data/test/hexapdf/font/encoding/test_difference_encoding.rb +21 -0
  274. data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
  275. data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
  276. data/test/hexapdf/font/test_encoding.rb +27 -0
  277. data/test/hexapdf/font/test_true_type_wrapper.rb +110 -0
  278. data/test/hexapdf/font/test_type1_wrapper.rb +66 -0
  279. data/test/hexapdf/font/true_type/common.rb +19 -0
  280. data/test/hexapdf/font/true_type/table/test_cmap.rb +59 -0
  281. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +133 -0
  282. data/test/hexapdf/font/true_type/table/test_directory.rb +35 -0
  283. data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
  284. data/test/hexapdf/font/true_type/table/test_head.rb +76 -0
  285. data/test/hexapdf/font/true_type/table/test_hhea.rb +40 -0
  286. data/test/hexapdf/font/true_type/table/test_hmtx.rb +38 -0
  287. data/test/hexapdf/font/true_type/table/test_loca.rb +43 -0
  288. data/test/hexapdf/font/true_type/table/test_maxp.rb +62 -0
  289. data/test/hexapdf/font/true_type/table/test_name.rb +95 -0
  290. data/test/hexapdf/font/true_type/table/test_os2.rb +65 -0
  291. data/test/hexapdf/font/true_type/table/test_post.rb +89 -0
  292. data/test/hexapdf/font/true_type/test_font.rb +120 -0
  293. data/test/hexapdf/font/true_type/test_table.rb +41 -0
  294. data/test/hexapdf/font/type1/test_afm_parser.rb +51 -0
  295. data/test/hexapdf/font/type1/test_font.rb +68 -0
  296. data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
  297. data/test/hexapdf/font_loader/test_from_configuration.rb +28 -0
  298. data/test/hexapdf/font_loader/test_standard14.rb +22 -0
  299. data/test/hexapdf/image_loader/test_jpeg.rb +83 -0
  300. data/test/hexapdf/image_loader/test_pdf.rb +47 -0
  301. data/test/hexapdf/image_loader/test_png.rb +258 -0
  302. data/test/hexapdf/task/test_dereference.rb +46 -0
  303. data/test/hexapdf/task/test_optimize.rb +137 -0
  304. data/test/hexapdf/test_configuration.rb +82 -0
  305. data/test/hexapdf/test_data_dir.rb +32 -0
  306. data/test/hexapdf/test_dictionary.rb +284 -0
  307. data/test/hexapdf/test_dictionary_fields.rb +185 -0
  308. data/test/hexapdf/test_document.rb +574 -0
  309. data/test/hexapdf/test_document_utils.rb +144 -0
  310. data/test/hexapdf/test_filter.rb +96 -0
  311. data/test/hexapdf/test_font_utils.rb +47 -0
  312. data/test/hexapdf/test_importer.rb +78 -0
  313. data/test/hexapdf/test_object.rb +177 -0
  314. data/test/hexapdf/test_parser.rb +394 -0
  315. data/test/hexapdf/test_rectangle.rb +36 -0
  316. data/test/hexapdf/test_reference.rb +41 -0
  317. data/test/hexapdf/test_revision.rb +139 -0
  318. data/test/hexapdf/test_revisions.rb +93 -0
  319. data/test/hexapdf/test_serializer.rb +169 -0
  320. data/test/hexapdf/test_stream.rb +262 -0
  321. data/test/hexapdf/test_tokenizer.rb +30 -0
  322. data/test/hexapdf/test_writer.rb +120 -0
  323. data/test/hexapdf/test_xref_section.rb +35 -0
  324. data/test/hexapdf/type/test_catalog.rb +30 -0
  325. data/test/hexapdf/type/test_embedded_file.rb +16 -0
  326. data/test/hexapdf/type/test_file_specification.rb +148 -0
  327. data/test/hexapdf/type/test_font.rb +35 -0
  328. data/test/hexapdf/type/test_font_descriptor.rb +51 -0
  329. data/test/hexapdf/type/test_font_simple.rb +190 -0
  330. data/test/hexapdf/type/test_font_type1.rb +128 -0
  331. data/test/hexapdf/type/test_form.rb +60 -0
  332. data/test/hexapdf/type/test_info.rb +14 -0
  333. data/test/hexapdf/type/test_names.rb +9 -0
  334. data/test/hexapdf/type/test_object_stream.rb +84 -0
  335. data/test/hexapdf/type/test_page.rb +260 -0
  336. data/test/hexapdf/type/test_page_tree_node.rb +255 -0
  337. data/test/hexapdf/type/test_resources.rb +167 -0
  338. data/test/hexapdf/type/test_trailer.rb +109 -0
  339. data/test/hexapdf/type/test_xref_stream.rb +131 -0
  340. data/test/hexapdf/utils/test_bit_field.rb +47 -0
  341. data/test/hexapdf/utils/test_lru_cache.rb +22 -0
  342. data/test/hexapdf/utils/test_object_hash.rb +115 -0
  343. data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
  344. data/test/hexapdf/utils/test_sorted_tree_node.rb +232 -0
  345. data/test/test_helper.rb +56 -0
  346. metadata +427 -0
@@ -0,0 +1,356 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/encryption/security_handler'
5
+ require 'hexapdf/document'
6
+ require 'hexapdf/stream'
7
+
8
+ describe HexaPDF::Encryption::EncryptionDictionary do
9
+ before do
10
+ @document = HexaPDF::Document.new
11
+ @dict = HexaPDF::Encryption::EncryptionDictionary.new({}, document: @document)
12
+ @dict[:Filter] = :Standard
13
+ @dict[:V] = 1
14
+ end
15
+
16
+ it "validates the /V value" do
17
+ @dict[:V] = 1
18
+ assert(@dict.validate)
19
+ @dict[:V] = 3
20
+ refute(@dict.validate)
21
+ end
22
+
23
+ it "validates the /Length field when /V=2" do
24
+ @dict[:V] = 2
25
+ refute(@dict.validate)
26
+
27
+ @dict[:Length] = 32
28
+ refute(@dict.validate)
29
+ @dict[:Length] = 136
30
+ refute(@dict.validate)
31
+ @dict[:Length] = 55
32
+ refute(@dict.validate)
33
+
34
+ @dict[:Length] = 120
35
+ assert(@dict.validate)
36
+ end
37
+ end
38
+
39
+ describe HexaPDF::Encryption::SecurityHandler do
40
+ class TestHandler < HexaPDF::Encryption::SecurityHandler
41
+
42
+ attr_accessor :strf, :myopt
43
+ public :dict
44
+
45
+ def prepare_encryption(**_options)
46
+ dict[:Filter] = :Test
47
+ @key = "a" * key_length
48
+ @strf ||= :aes
49
+ @stmf ||= :arc4
50
+ @eff ||= :identity
51
+ [@key, @strf, @stmf, @eff]
52
+ end
53
+
54
+ def prepare_decryption(myopt: nil)
55
+ @myopt = myopt
56
+ @key = "a" * key_length
57
+ end
58
+
59
+ end
60
+
61
+
62
+ before do
63
+ @document = HexaPDF::Document.new
64
+ @obj = @document.add({})
65
+ @handler = TestHandler.new(@document)
66
+ end
67
+
68
+ describe "class methods" do
69
+ before do
70
+ HexaPDF::GlobalConfiguration['encryption.filter_map'][:Test] = TestHandler
71
+ end
72
+
73
+ after do
74
+ HexaPDF::GlobalConfiguration['encryption.filter_map'].delete(:Test)
75
+ end
76
+
77
+ describe "class set_up_encryption" do
78
+ it "fails if the requested security handler cannot be found" do
79
+ assert_raises(HexaPDF::EncryptionError) do
80
+ HexaPDF::Encryption::SecurityHandler.set_up_encryption(@document, :non_standard)
81
+ end
82
+ end
83
+
84
+ it "updates the trailer's /Encrypt entry to be wrapped by an encryption dictionary" do
85
+ HexaPDF::Encryption::SecurityHandler.set_up_encryption(@document, :Test)
86
+ assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @document.trailer[:Encrypt])
87
+ end
88
+
89
+ it "returns the frozen security handler" do
90
+ handler = HexaPDF::Encryption::SecurityHandler.set_up_encryption(@document, :Test)
91
+ assert(handler.frozen?)
92
+ end
93
+ end
94
+
95
+ describe "set_up_decryption" do
96
+ it "fails if the document has no /Encrypt dictionary" do
97
+ exp = assert_raises(HexaPDF::EncryptionError) do
98
+ HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
99
+ end
100
+ assert_match(/No \/Encrypt/i, exp.message)
101
+ end
102
+
103
+ it "fails if the requested security handler cannot be found" do
104
+ @document.trailer[:Encrypt] = {Filter: :NonStandard}
105
+ assert_raises(HexaPDF::EncryptionError) do
106
+ HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
107
+ end
108
+ end
109
+
110
+ it "updates the trailer's /Encrypt entry to be wrapped by an encryption dictionary" do
111
+ @document.trailer[:Encrypt] = {Filter: :Test, V: 1}
112
+ HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
113
+ assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @document.trailer[:Encrypt])
114
+ end
115
+
116
+ it "returns the frozen security handler" do
117
+ @document.trailer[:Encrypt] = {Filter: :Test, V: 1}
118
+ handler = HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
119
+ assert(handler.frozen?)
120
+ end
121
+ end
122
+ end
123
+
124
+ it "doesn't have a valid encryption key directly after creation" do
125
+ refute(@handler.encryption_key_valid?)
126
+ end
127
+
128
+ describe "set_up_encryption" do
129
+ it "sets the correct /V value for the given key length and algorithm" do
130
+ [[40, :arc4, 1], [128, :arc4, 2], [128, :arc4, 4],
131
+ [128, :aes, 4], [256, :aes, 5]].each do |length, algorithm, version|
132
+ @handler.set_up_encryption(key_length: length, algorithm: algorithm, force_V4: version == 4)
133
+ assert_equal(version, @handler.dict[:V])
134
+ end
135
+ end
136
+
137
+ it "sets the correct /Length value for the given key length" do
138
+ [[40, nil], [48, 48], [128, 128], [256, nil]].each do |key_length, result|
139
+ algorithm = (key_length == 256 ? :aes : :arc4)
140
+ @handler.set_up_encryption(key_length: key_length, algorithm: algorithm)
141
+ assert_equal(result, @handler.dict[:Length])
142
+ end
143
+ end
144
+
145
+ it "calls the prepare_encryption method" do
146
+ @handler.set_up_encryption
147
+ assert_equal(:Test, @handler.dict[:Filter])
148
+ end
149
+
150
+ it "returns the generated encryption dictionary wrapped in an encryption class" do
151
+ dict = @handler.set_up_encryption
152
+ assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, dict)
153
+ end
154
+
155
+ it "set's up the handler for encryption" do
156
+ [:arc4, :aes].each do |algorithm|
157
+ @handler.set_up_encryption(key_length: 128, algorithm: algorithm)
158
+ @obj[:X] = @handler.encrypt_string('data', @obj)
159
+ assert_equal('data', @handler.decrypt(@obj)[:X])
160
+ end
161
+ end
162
+
163
+ it "generates a valid encryption key" do
164
+ @document.trailer[:Encrypt] = @handler.set_up_encryption
165
+ assert(@handler.encryption_key_valid?)
166
+ end
167
+
168
+ it "provides correct encryption details" do
169
+ @handler.set_up_encryption
170
+ assert_equal({version: 4, string_algorithm: :aes, stream_algorithm: :arc4,
171
+ embedded_file_algorithm: :identity, key_length: 128},
172
+ @handler.encryption_details)
173
+ assert_equal(HexaPDF::Encryption::Identity, @handler.send(:embedded_file_algorithm))
174
+ assert_equal(HexaPDF::Encryption::FastAES, @handler.send(:string_algorithm))
175
+ assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:stream_algorithm))
176
+ end
177
+
178
+ it "fails for unsupported encryption key lengths" do
179
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
180
+ @handler.set_up_encryption(key_length: 43)
181
+ end
182
+ assert_match(/Invalid key length/i, exp.message)
183
+ end
184
+
185
+ it "fails for unsupported encryption algorithms" do
186
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
187
+ @handler.set_up_encryption(algorithm: :test)
188
+ end
189
+ assert_match(/Unsupported encryption algorithm/i, exp.message)
190
+ end
191
+
192
+ it "fails for the aes algorithm with key lengths != 128 or 256" do
193
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
194
+ @handler.set_up_encryption(algorithm: :aes, key_length: 40)
195
+ end
196
+ assert_match(/AES algorithm.*key length/i, exp.message)
197
+ end
198
+
199
+ it "fails for the arc4 algorithm with a key length of 256" do
200
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
201
+ @handler.set_up_encryption(algorithm: :arc4, key_length: 256)
202
+ end
203
+ assert_match(/ARC4 algorithm.*key length/i, exp.message)
204
+ end
205
+ end
206
+
207
+
208
+ describe "set_up_decryption" do
209
+ it "sets the handlers's dictionary to the encryption dictionary wrapped in a custom class and returns it" do
210
+ dict = @handler.set_up_decryption(Filter: :test, V: 1)
211
+ assert_equal(dict, @handler.dict)
212
+ assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @handler.dict)
213
+ assert_equal({Filter: :test, V: 1}, @handler.dict.value)
214
+ end
215
+
216
+ it "doesn't modify the trailer's /Encrypt dictionary" do
217
+ @handler.set_up_decryption(Filter: :test, V: 4, Length: 128)
218
+ assert_nil(@document.trailer[:Encrypt])
219
+ end
220
+
221
+ it "calls prepare_decryption" do
222
+ @handler.set_up_decryption({Filter: :test, V: 4, Length: 128}, myopt: 5)
223
+ assert_equal(5, @handler.myopt)
224
+ end
225
+
226
+ it "selects the correct algorithm based on the /V and /CF values" do
227
+ @enc = @handler.dup
228
+
229
+ [[:arc4, 40, {V: 1}],
230
+ [:arc4, 80, {V: 2, Length: 80}],
231
+ [:arc4, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :V2}}}],
232
+ [:aes, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :AESV2}}}],
233
+ [:aes, 256, {V: 5, StrF: :Mine, CF: {Mine: {CFM: :AESV3}}}],
234
+ [:identity, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :None}}}],
235
+ [:identity, 128, {V: 4, CF: {Mine: {CFM: :AESV2}}}],
236
+ ].each do |alg, length, dict|
237
+ @enc.strf = alg
238
+ @enc.set_up_encryption(key_length: length, algorithm: (alg == :identity ? :aes : alg))
239
+ @obj[:X] = @enc.encrypt_string('data', @obj)
240
+ @handler.set_up_decryption(dict)
241
+ assert_equal('data', @handler.decrypt(@obj)[:X])
242
+ end
243
+ end
244
+
245
+ it "selects the correct algorithm for string, stream and embedded file decryption" do
246
+ @handler.set_up_decryption({V: 4, StrF: :Mine, StmF: :Mine, EFF: :Mine,
247
+ CF: {Mine: {CFM: :V2}}})
248
+ assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:embedded_file_algorithm))
249
+ assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:string_algorithm))
250
+ assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:stream_algorithm))
251
+ end
252
+
253
+ it "provides correct encryption details" do
254
+ @handler.set_up_decryption({Filter: :test, V: 2, Length: 128}, myopt: 5)
255
+ assert_equal({version: 2, string_algorithm: :arc4, stream_algorithm: :arc4,
256
+ embedded_file_algorithm: :arc4, key_length: 128},
257
+ @handler.encryption_details)
258
+ end
259
+
260
+ it "fails for unsupported /V values in the dict" do
261
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
262
+ @handler.set_up_decryption(V: 3)
263
+ end
264
+ assert_match(/Unsupported encryption version/i, exp.message)
265
+ end
266
+
267
+ it "fails for unsupported crypt filter encryption methods" do
268
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
269
+ @handler.set_up_decryption(V: 4, StrF: :Mine, CF: {Mine: {CFM: :Unknown}})
270
+ end
271
+ assert_match(/Unsupported encryption method/i, exp.message)
272
+ end
273
+ end
274
+
275
+
276
+ describe "decrypt" do
277
+ before do
278
+ @handler.set_up_decryption(V: 1)
279
+ @encrypted = @handler.encrypt_string('string', @obj)
280
+ @obj.value = {Key: @encrypted.dup, Array: [@encrypted.dup], Hash: {Another: @encrypted.dup}}
281
+ end
282
+
283
+ it "decrypts all strings in an object" do
284
+ @handler.decrypt(@obj)
285
+ assert_equal('string', @obj[:Key])
286
+ assert_equal('string', @obj[:Array][0])
287
+ assert_equal('string', @obj[:Hash][:Another])
288
+ end
289
+
290
+ it "decrypts the content of a stream object" do
291
+ data = HexaPDF::StreamData.new(proc { @encrypted })
292
+ obj = @document.wrap({}, oid: @obj.oid, stream: data)
293
+ @handler.decrypt(obj)
294
+ assert_equal('string', obj.stream)
295
+ end
296
+
297
+ it "doesn't decrypt a document's Encrypt dictionary" do
298
+ @document.trailer[:Encrypt] = @obj
299
+ assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
300
+ end
301
+
302
+ it "doesn't decrypt XRef streams" do
303
+ @obj[:Type] = :XRef
304
+ assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
305
+ end
306
+
307
+ it "fails if V < 5 and the object number changes" do
308
+ @obj.oid = 55
309
+ @handler.decrypt(@obj)
310
+ refute_equal('string', @obj[:Key])
311
+ end
312
+ end
313
+
314
+
315
+ describe "encryption" do
316
+ before do
317
+ @handler.set_up_encryption(key_length: 128, algorithm: :arc4)
318
+ @stream = @document.wrap({}, oid: 1, stream: HexaPDF::StreamData.new(proc { "string" }))
319
+ end
320
+
321
+ it "encrypts strings of indirect objects" do
322
+ @obj[:Key] = @handler.encrypt_string('string', @obj)
323
+ assert_equal('string', @handler.decrypt(@obj)[:Key])
324
+ end
325
+
326
+ it "encrypts streams" do
327
+ result = TestHelper.collector(@handler.encrypt_stream(@stream))
328
+ @stream.stream = HexaPDF::StreamData.new(proc { result })
329
+ assert_equal('string', @handler.decrypt(@stream).stream)
330
+ end
331
+
332
+ it "doesn't encrypt strings in a document's Encrypt dictionary" do
333
+ @document.trailer[:Encrypt] = @handler.dict
334
+ @document.trailer[:Encrypt][:Mine] = 'string'
335
+ assert_equal('string', @handler.encrypt_string('string', @document.trailer[:Encrypt]))
336
+ end
337
+
338
+ it "doesn't encrypt XRef streams" do
339
+ @stream[:Type] = :XRef
340
+ assert_equal('string', @handler.encrypt_stream(@stream).resume)
341
+ end
342
+ end
343
+
344
+
345
+ it "works correctly with different decryption and encryption handlers" do
346
+ test_file = File.join(TEST_DATA_DIR, 'standard-security-handler', 'nopwd-arc4-40bit-V1.pdf')
347
+ doc = HexaPDF::Document.new(io: StringIO.new(File.read(test_file)))
348
+ doc.encrypt(algorithm: :aes, password: 'test')
349
+ out = StringIO.new(''.b)
350
+ doc.write(out, update_fields: false)
351
+
352
+ assert_raises(HexaPDF::EncryptionError) { HexaPDF::Document.new(io: out) }
353
+ doc = HexaPDF::Document.new(io: out, decryption_opts: {password: 'test'})
354
+ assert_equal('D:20150409164600', doc.trailer[:Info].value[:ModDate])
355
+ end
356
+ end
@@ -0,0 +1,274 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/encryption/standard_security_handler'
5
+ require 'hexapdf/document'
6
+ require 'hexapdf/writer'
7
+ require 'stringio'
8
+
9
+ describe HexaPDF::Encryption::StandardEncryptionDictionary do
10
+ before do
11
+ @document = HexaPDF::Document.new
12
+ @dict = HexaPDF::Encryption::StandardEncryptionDictionary.new({}, document: @document)
13
+ @dict[:Filter] = :Standard
14
+ @dict[:V] = 1
15
+ @dict[:R] = 2
16
+ @dict[:U] = 'test' * 8
17
+ @dict[:O] = 'test' * 8
18
+ @dict[:P] = -5
19
+ @dict[:UE] = 'test' * 8
20
+ @dict[:OE] = 'test' * 8
21
+ @dict[:Perms] = 'test' * 8
22
+ end
23
+
24
+ it "validates the /R value" do
25
+ @dict[:R] = 2
26
+ assert(@dict.validate)
27
+ @dict[:R] = 5
28
+ refute(@dict.validate)
29
+ end
30
+
31
+ [:U, :O].each do |field|
32
+ it "validates the length of /#{field} field for R <= 4" do
33
+ @dict[field] = 'test'
34
+ refute(@dict.validate)
35
+ end
36
+ end
37
+
38
+ [:U, :O, :UE, :OE, :Perms].each do |field|
39
+ it "validates the length of /#{field} field for R=6" do
40
+ @dict[:R] = 6
41
+ @dict[field] = 'test'
42
+ refute(@dict.validate)
43
+ end
44
+ end
45
+
46
+ [:UE, :OE, :Perms].each do |field|
47
+ it "validates the existence of the /#{field} field for R=6" do
48
+ @dict[:R] = 6
49
+ @dict.delete(field)
50
+ refute(@dict.validate)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe HexaPDF::Encryption::StandardSecurityHandler do
56
+ TEST_FILES = Dir[File.join(TEST_DATA_DIR, 'standard-security-handler', '*.pdf')].sort
57
+ USER_PASSWORD = 'uhexapdf'
58
+ OWNER_PASSWORD = 'ohexapdf'
59
+
60
+ MINIMAL_DOC = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
61
+
62
+ TEST_FILES.each do |file|
63
+ basename = File.basename(file)
64
+ it "can decrypt, encrypt and decrypt the encrypted file #{basename} with the user password" do
65
+ begin
66
+ doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
67
+ decryption_opts: {password: USER_PASSWORD})
68
+ assert_equal(MINIMAL_DOC.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
69
+
70
+ out = StringIO.new(''.b)
71
+ HexaPDF::Writer.new(doc, out).write
72
+ doc = HexaPDF::Document.new(io: out, decryption_opts: {password: USER_PASSWORD})
73
+ assert_equal(MINIMAL_DOC.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
74
+ rescue HexaPDF::EncryptionError => e
75
+ flunk("Error processing #{basename}: #{e}")
76
+ end
77
+ end
78
+
79
+ if basename !~ /\Auserpwd/
80
+ it "can decrypt the encrypted file #{basename} with the owner password" do
81
+ begin
82
+ doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
83
+ decryption_opts: {password: OWNER_PASSWORD})
84
+ assert_equal(MINIMAL_DOC.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
85
+ rescue HexaPDF::EncryptionError => e
86
+ flunk("Error processing #{basename}: #{e}")
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+
93
+ before do
94
+ @document = HexaPDF::Document.new
95
+ @handler = HexaPDF::Encryption::StandardSecurityHandler.new(@document)
96
+ end
97
+
98
+ describe "prepare_encryption" do
99
+ it "returns the encryption dictionary wrapped with a custom class" do
100
+ dict = @handler.set_up_encryption
101
+ assert_kind_of(HexaPDF::Encryption::StandardEncryptionDictionary, dict)
102
+ end
103
+
104
+ it "sets the correct revision independent /Filter value" do
105
+ dict = @handler.set_up_encryption
106
+ assert_equal(:Standard, dict[:Filter])
107
+ end
108
+
109
+ it "sets the correct revision independent /P value" do
110
+ dict = @handler.set_up_encryption
111
+ assert_equal(@handler.class::Permissions::ALL | @handler.class::Permissions::RESERVED,
112
+ dict[:P])
113
+ dict = @handler.set_up_encryption(permissions: @handler.class::Permissions::COPY_CONTENT)
114
+ assert_equal(@handler.class::Permissions::COPY_CONTENT | @handler.class::Permissions::RESERVED,
115
+ dict[:P])
116
+ dict = @handler.set_up_encryption(permissions: [:modify_content, :modify_annotation])
117
+ assert_equal(@handler.class::Permissions::MODIFY_CONTENT | @handler.class::Permissions::MODIFY_ANNOTATION | @handler.class::Permissions::RESERVED,
118
+ dict[:P])
119
+ end
120
+
121
+ it "sets the correct revision independent /EncryptMetadata value" do
122
+ dict = @handler.set_up_encryption
123
+ assert(dict[:EncryptMetadata])
124
+ dict = @handler.set_up_encryption(encrypt_metadata: false)
125
+ refute(dict[:EncryptMetadata])
126
+ end
127
+
128
+ it "sets the correct encryption dictionary values for revision 2 and 3" do
129
+ arc4_assertions = lambda do |d|
130
+ assert_equal(32, d[:U].length)
131
+ assert_equal(32, d[:O].length)
132
+ refute(d.value.key?(:UE))
133
+ refute(d.value.key?(:OE))
134
+ refute(d.value.key?(:Perms))
135
+ end
136
+ dict = @handler.set_up_encryption(key_length: 40, algorithm: :arc4)
137
+ assert_equal(2, dict[:R])
138
+ arc4_assertions.call(dict)
139
+
140
+ dict = @handler.set_up_encryption(key_length: 128, algorithm: :arc4)
141
+ assert_equal(3, dict[:R])
142
+ arc4_assertions.call(dict)
143
+ end
144
+
145
+ it "sets the correct encryption dictionary values for revisions 4 and 6" do
146
+ crypt_filter = lambda do |d, r, alg, length|
147
+ assert_equal(r, d[:R])
148
+ assert_equal(alg == :AESV3 ? 48 : 32, d[:U].length)
149
+ assert_equal(alg == :AESV3 ? 48 : 32, d[:O].length)
150
+ assert_equal({CFM: alg, Length: length, AuthEvent: :DocOpen}, d[:CF][:StdCF])
151
+ assert_equal(:StdCF, d[:StrF])
152
+ assert_equal(:StdCF, d[:StmF])
153
+ assert_equal(:StdCF, d[:EFF])
154
+ end
155
+
156
+ dict = @handler.set_up_encryption(key_length: 128, algorithm: :arc4, force_V4: true)
157
+ refute(dict.value.key?(:UE))
158
+ refute(dict.value.key?(:OE))
159
+ refute(dict.value.key?(:Perms))
160
+ crypt_filter.call(dict, 4, :V2, 16)
161
+
162
+ dict = @handler.set_up_encryption(key_length: 128, algorithm: :aes)
163
+ refute(dict.value.key?(:UE))
164
+ refute(dict.value.key?(:OE))
165
+ refute(dict.value.key?(:Perms))
166
+ crypt_filter.call(dict, 4, :AESV2, 16)
167
+
168
+ dict = @handler.set_up_encryption(key_length: 256, algorithm: :aes)
169
+ assert_equal(32, dict[:UE].length)
170
+ assert_equal(32, dict[:OE].length)
171
+ assert_equal(16, dict[:Perms].length)
172
+ crypt_filter.call(dict, 6, :AESV3, 32)
173
+ end
174
+
175
+ it "uses the password keyword as fallback for the user and owner passwords" do
176
+ dict1 = @document.unwrap(@handler.set_up_encryption(password: 'user', owner_password: 'owner'))
177
+ dict2 = @document.unwrap(@handler.set_up_encryption(password: 'owner', user_password: 'user'))
178
+ dict3 = @document.unwrap(@handler.set_up_encryption(user_password: 'user', owner_password: 'owner'))
179
+
180
+ assert_equal(dict1[:U], dict2[:U])
181
+ assert_equal(dict2[:U], dict3[:U])
182
+ assert_equal(dict1[:O], dict2[:O])
183
+ assert_equal(dict2[:O], dict3[:O])
184
+ end
185
+
186
+ it "fails if the password contains invalid characters" do
187
+ assert_raises(HexaPDF::EncryptionError) { @handler.set_up_encryption(password: 'œ test') }
188
+ end
189
+
190
+ it "fails for unknown keywords" do
191
+ assert_raises(ArgumentError) { @handler.set_up_encryption(unknown: 'test') }
192
+ end
193
+ end
194
+
195
+
196
+ describe "prepare_decryption" do
197
+ it "fails if the /Filter value is incorrect" do
198
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
199
+ @handler.set_up_decryption(Filter: :NonStandard, V: 2)
200
+ end
201
+ assert_match(/Invalid \/Filter/i, exp.message)
202
+ end
203
+
204
+ it "fails if the /R value is incorrect" do
205
+ exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
206
+ @handler.set_up_decryption(Filter: :Standard, V: 2, R: 5)
207
+ end
208
+ assert_match(/Invalid \/R/i, exp.message)
209
+ end
210
+
211
+ it "fails if the ID in the document's trailer is missing although it is needed" do
212
+ exp = assert_raises(HexaPDF::EncryptionError) do
213
+ @handler.set_up_decryption(Filter: :Standard, V: 2, R: 2)
214
+ end
215
+ assert_match(/Document ID/i, exp.message)
216
+ end
217
+
218
+ it "fails if the supplied password is invalid" do
219
+ exp = assert_raises(HexaPDF::EncryptionError) do
220
+ @handler.set_up_decryption(Filter: :Standard, V: 2, R: 6, U: 'a' * 48, O: 'a' * 48,
221
+ UE: 'a' * 32, OE: 'a' * 32)
222
+ end
223
+ assert_match(/Invalid password/i, exp.message)
224
+ end
225
+
226
+ describe "/Perms field checking" do
227
+ before do
228
+ @dict = @handler.set_up_encryption(key_length: 256, algorithm: :aes)
229
+ end
230
+
231
+ it "fails if the field cannot be decrypted" do
232
+ @dict[:Perms].succ!
233
+ exp = assert_raises(HexaPDF::EncryptionError) { @handler.set_up_decryption(@dict) }
234
+ assert_match(/cannot be decrypted/, exp.message)
235
+ end
236
+
237
+ it "fails if the /P field doesn't match" do
238
+ @dict[:P] = 500
239
+ exp = assert_raises(HexaPDF::EncryptionError) { @handler.set_up_decryption(@dict) }
240
+ assert_match(/\/P/, exp.message)
241
+ end
242
+
243
+ it "fails if the /EncryptMetadata field doesn't match" do
244
+ @dict[:EncryptMetadata] = false
245
+ exp = assert_raises(HexaPDF::EncryptionError) { @handler.set_up_decryption(@dict) }
246
+ assert_match(/\/EncryptMetadata/, exp.message)
247
+ end
248
+
249
+ it "ignores the /Perms when requested" do
250
+ obj = HexaPDF::Object.new(nil, oid: 1)
251
+ obj.value = @handler.encrypt_string('test', obj)
252
+
253
+ @dict[:P] = 500
254
+ @handler.set_up_decryption(@dict, check_permissions: false)
255
+ assert_equal('test', @handler.decrypt(obj).value)
256
+ end
257
+ end
258
+ end
259
+
260
+ it "encryption key stays valid even if default dictionary values are set while setting up decryption" do
261
+ @document.encrypt(key_length: 128, algorithm: :aes)
262
+ assert(@document.security_handler.encryption_key_valid?)
263
+
264
+ @document.trailer[:Encrypt].delete(:EncryptMetadata)
265
+ handler = HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
266
+ assert(handler.encryption_key_valid?)
267
+ end
268
+
269
+ it "returns an array of permission symbols" do
270
+ perms = @handler.class::Permissions::MODIFY_CONTENT | @handler.class::Permissions::COPY_CONTENT
271
+ @handler.set_up_encryption(permissions: perms)
272
+ assert_equal([:copy_content, :modify_content], @handler.permissions.sort)
273
+ end
274
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ # Provides common tests for all filter implementations.
6
+ #
7
+ # The filter object needs to be available in the @obj variable and the @all_test_cases variable
8
+ # needs to hold an array of test cases, i.e. [decoded, encoded] objects.
9
+ module CommonFilterTests
10
+ include TestHelper
11
+
12
+ TEST_BIG_STR = ''.b
13
+ TEST_BIG_STR << [rand(2**32)].pack('N') while TEST_BIG_STR.length < 2**16
14
+ TEST_BIG_STR.freeze
15
+
16
+ def test_decodes_correctly
17
+ @all_test_cases.each_with_index do |(result, str), index|
18
+ assert_equal(result, collector(@obj.decoder(feeder(str.dup))), "testcase #{index}")
19
+ end
20
+ end
21
+
22
+ def test_encodes_correctly
23
+ @all_test_cases.each_with_index do |(str, result), index|
24
+ assert_equal(result, collector(@obj.encoder(feeder(str.dup))), "testcase #{index}")
25
+ end
26
+ end
27
+
28
+ def test_works_with_big_data
29
+ assert_equal(TEST_BIG_STR, collector(@obj.decoder(@obj.encoder(feeder(TEST_BIG_STR.dup)))))
30
+ end
31
+
32
+ def test_decoder_returns_strings_in_binary_encoding
33
+ assert_encodings(@obj.decoder(@obj.encoder(feeder('some test data', 1))), "decoder")
34
+ end
35
+
36
+ def test_encoder_returns_strings_in_binary_encoding
37
+ assert_encodings(@obj.encoder(feeder('some test data', 1)), "encoder")
38
+ end
39
+
40
+ def assert_encodings(source, type)
41
+ while source.alive? && (data = source.resume)
42
+ assert_equal(Encoding::BINARY, data.encoding, "encoding problem in #{type}")
43
+ end
44
+ end
45
+
46
+ def test_decoder_works_with_single_byte_input
47
+ assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded.dup, 1))))
48
+ end
49
+
50
+ def test_encoder_works_with_single_byte_input
51
+ assert_equal(@encoded, collector(@obj.encoder(feeder(@decoded.dup, 1))))
52
+ end
53
+ end