hexapdf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,574 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'tempfile'
4
+ require 'test_helper'
5
+ require 'hexapdf/document'
6
+ require 'stringio'
7
+
8
+ describe HexaPDF::Document do
9
+ before do
10
+ @io = StringIO.new(<<EOF)
11
+ %PDF-1.7
12
+ 1 0 obj
13
+ 10
14
+ endobj
15
+
16
+ 2 0 obj
17
+ 20
18
+ endobj
19
+
20
+ 3 0 obj
21
+ 30
22
+ endobj
23
+
24
+ xref
25
+ 0 4
26
+ 0000000000 65535 f
27
+ 0000000009 00000 n
28
+ 0000000028 00000 n
29
+ 0000000047 00000 n
30
+ trailer
31
+ << /Size 4 >>
32
+ startxref
33
+ 66
34
+ %%EOF
35
+
36
+ 2 0 obj
37
+ 200
38
+ endobj
39
+
40
+ xref
41
+ 2 2
42
+ 0000000197 00000 n
43
+ 0000000000 00001 f
44
+ trailer
45
+ << /Size 4 /Prev 66 >>
46
+ startxref
47
+ 217
48
+ %%EOF
49
+ EOF
50
+ @io_doc = HexaPDF::Document.new(io: @io)
51
+ @doc = HexaPDF::Document.new
52
+ end
53
+
54
+ describe "::open" do
55
+ before do
56
+ @file = Tempfile.new('hexapdf-document')
57
+ @io_doc.write(@file)
58
+ @file.close
59
+ end
60
+
61
+ after do
62
+ @file.unlink
63
+ end
64
+
65
+ it "works without block" do
66
+ doc = HexaPDF::Document.open(@file.path)
67
+ assert_equal(200, doc.object(2).value)
68
+ end
69
+
70
+ it "works with a block" do
71
+ HexaPDF::Document.open(@file.path) do |doc|
72
+ assert_equal(200, doc.object(2).value)
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "initialize" do
78
+ it "doesn't need any arguments" do
79
+ doc = HexaPDF::Document.new
80
+ assert_equal(:A4, doc.config['page.default_media_box'])
81
+ end
82
+
83
+ it "takes a configuration hash as option" do
84
+ doc = HexaPDF::Document.new(config: {'page.default_media_box' => :A5})
85
+ assert_equal(:A5, doc.config['page.default_media_box'])
86
+ end
87
+
88
+ it "takes an IO object as option" do
89
+ doc = HexaPDF::Document.new(io: @io)
90
+ assert_equal(10, doc.object(1).value)
91
+ end
92
+ end
93
+
94
+ describe "object" do
95
+ it "accepts a Reference object as argument" do
96
+ assert_equal(10, @io_doc.object(HexaPDF::Reference.new(1, 0)).value)
97
+ end
98
+
99
+ it "accepts an object number as arguments" do
100
+ assert_equal(10, @io_doc.object(1).value)
101
+ end
102
+
103
+ it "returns added objects" do
104
+ obj = @io_doc.add(@io_doc.wrap({Type: :Test}, oid: 100))
105
+ assert_equal(obj, @io_doc.object(100))
106
+ end
107
+
108
+ it "returns nil for unknown object references" do
109
+ assert_nil(@io_doc.object(100))
110
+ end
111
+
112
+ it "returns only the newest version of an object" do
113
+ assert_equal(200, @io_doc.object(2).value)
114
+ assert_equal(200, @io_doc.object(HexaPDF::Reference.new(2, 0)).value)
115
+ assert_nil(@io_doc.object(3).value)
116
+ assert_nil(@io_doc.object(HexaPDF::Reference.new(3, 1)).value)
117
+ assert_equal(30, @io_doc.object(HexaPDF::Reference.new(3, 0)).value)
118
+ end
119
+ end
120
+
121
+ describe "object?" do
122
+ it "works with a Reference object as argument" do
123
+ assert(@io_doc.object?(HexaPDF::Reference.new(1, 0)))
124
+ end
125
+
126
+ it "works with an object number as arguments" do
127
+ assert(@io_doc.object?(1))
128
+ end
129
+ end
130
+
131
+ describe "deref" do
132
+ it "returns a dereferenced object when given a Reference object" do
133
+ assert_equal(@io_doc.object(1), @io_doc.deref(HexaPDF::Reference.new(1, 0)))
134
+ end
135
+
136
+ it "returns the given object when it is not a Reference object" do
137
+ assert_equal(5, @io_doc.deref(5))
138
+ end
139
+ end
140
+
141
+ describe "add" do
142
+ it "automatically assigns free object numbers" do
143
+ assert_equal(1, @doc.add(5).oid)
144
+ assert_equal(2, @doc.add(5).oid)
145
+ @doc.revisions.add
146
+ assert_equal(3, @doc.add(5).oid)
147
+ end
148
+
149
+ it "assigns the object's document" do
150
+ obj = @doc.add(5)
151
+ assert_equal(@doc, obj.document)
152
+ end
153
+
154
+ it "allows adding a native ruby object" do
155
+ obj = @doc.add(5)
156
+ assert_equal(5, obj.value)
157
+ end
158
+
159
+ it "allows passing arguments to the wrap call" do
160
+ obj = @doc.add({}, type: HexaPDF::Dictionary)
161
+ assert_equal(HexaPDF::Dictionary, obj.class)
162
+ end
163
+
164
+ it "allows adding a HexaPDF::Object" do
165
+ obj = @doc.add(HexaPDF::Object.new(5))
166
+ assert_equal(5, obj.value)
167
+ end
168
+
169
+ it "returns the given object if it is already stored in the document" do
170
+ obj = @doc.add(5)
171
+ assert_same(obj, @doc.add(obj))
172
+ end
173
+
174
+ it "allows specifying a revision to which the object should be added" do
175
+ @doc.revisions.add
176
+ @doc.revisions.add
177
+
178
+ @doc.add(@doc.wrap(5, oid: 1), revision: 0)
179
+ assert_equal(5, @doc.object(1).value)
180
+
181
+ @doc.add(@doc.wrap(10, oid: 1), revision: 2)
182
+ assert_equal(10, @doc.object(1).value)
183
+
184
+ @doc.add(@doc.wrap(7.5, oid: 1), revision: 1)
185
+ assert_equal(10, @doc.object(1).value)
186
+ end
187
+
188
+ it "fails if the specified revision index is invalid" do
189
+ assert_raises(ArgumentError) { @doc.add(5, revision: 5) }
190
+ end
191
+
192
+ it "fails if the object to be added is associated with another document" do
193
+ doc = HexaPDF::Document.new
194
+ obj = doc.add(5)
195
+ assert_raises(HexaPDF::Error) { @doc.add(obj) }
196
+ end
197
+
198
+ it "fails if the object number of the object to be added is already associated with another object" do
199
+ obj = @doc.add(5)
200
+ assert_raises(HexaPDF::Error) { @doc.add(@doc.wrap(5, oid: obj.oid, gen: 1)) }
201
+ end
202
+ end
203
+
204
+ describe "delete" do
205
+ it "works with a Reference object as argument" do
206
+ obj = @doc.add(5)
207
+ @doc.delete(obj, mark_as_free: false)
208
+ refute(@doc.object?(obj))
209
+ end
210
+
211
+ it "works with an object number as arguments" do
212
+ @doc.add(5)
213
+ @doc.delete(1, mark_as_free: false)
214
+ refute(@doc.object?(1))
215
+ end
216
+
217
+ describe "with an object in multiple revisions" do
218
+ before do
219
+ @ref = HexaPDF::Reference.new(2, 3)
220
+ obj = @doc.wrap(5, oid: @ref.oid, gen: @ref.gen)
221
+ @doc.revisions.add
222
+ @doc.add(obj, revision: 0)
223
+ @doc.add(obj, revision: 1)
224
+ end
225
+
226
+ it "deletes an object for all revisions when revision = :all" do
227
+ @doc.delete(@ref, revision: :all, mark_as_free: false)
228
+ refute(@doc.object?(@ref))
229
+ end
230
+
231
+ it "deletes an object only in the current revision when revision = :current" do
232
+ @doc.delete(@ref, revision: :current, mark_as_free: false)
233
+ assert(@doc.object?(@ref))
234
+ end
235
+
236
+ it "marks the object as PDF null object when using mark_as_free=true" do
237
+ @doc.delete(@ref, revision: :current)
238
+ assert(@doc.object(@ref).null?)
239
+ end
240
+ end
241
+
242
+ it "fails if the revision argument is invalid" do
243
+ assert_raises(ArgumentError) { @doc.delete(1, revision: :invalid) }
244
+ end
245
+ end
246
+
247
+ describe "import" do
248
+ it "allows importing objects from another document" do
249
+ obj = @doc.import(@io_doc.object(2))
250
+ assert_equal(200, obj.value)
251
+ refute_equal(0, obj.oid)
252
+ end
253
+
254
+ it "fails if the given object is not a PDF object" do
255
+ assert_raises(ArgumentError) { @doc.import(5) }
256
+ end
257
+
258
+ it "fails if the given object is associated with no or the destination document" do
259
+ assert_raises(ArgumentError) { @doc.import(HexaPDF::Object.new(5)) }
260
+ obj = @doc.add(5)
261
+ assert_raises(ArgumentError) { @doc.import(obj) }
262
+ end
263
+ end
264
+
265
+ describe "wrap" do
266
+ before do
267
+ @myclass = Class.new(HexaPDF::Object)
268
+ @myclass2 = Class.new(HexaPDF::Object)
269
+ HexaPDF::GlobalConfiguration['object.type_map'][:MyClass] = @myclass
270
+ HexaPDF::GlobalConfiguration['object.subtype_map'][:TheSecond] = @myclass2
271
+ end
272
+
273
+ after do
274
+ HexaPDF::GlobalConfiguration['object.type_map'].delete(:MyClass)
275
+ HexaPDF::GlobalConfiguration['object.subtype_map'].delete(:TheSecond)
276
+ end
277
+
278
+ it "uses a suitable default type if no special type is specified" do
279
+ assert_instance_of(HexaPDF::Object, @doc.wrap(5))
280
+ assert_instance_of(HexaPDF::Stream, @doc.wrap({a: 5}, stream: ''))
281
+ assert_instance_of(HexaPDF::Dictionary, @doc.wrap(a: 5))
282
+ end
283
+
284
+ it "returns an object of type HexaPDF::Object" do
285
+ assert_kind_of(HexaPDF::Object, @doc.wrap(5))
286
+ assert_kind_of(HexaPDF::Object, @doc.wrap({}, stream: ''))
287
+ end
288
+
289
+ it "associates the returned object with the document" do
290
+ assert_equal(@doc, @doc.wrap(5).document)
291
+ end
292
+
293
+ it "sets the given object (not === HexaPDF::Object) as value for the PDF object" do
294
+ assert_equal(5, @doc.wrap(5).value)
295
+ end
296
+
297
+ it "uses the data of the given PDF object for re-wrapping" do
298
+ obj = @doc.wrap({a: :b}, oid: 10, gen: 20, stream: 'hallo')
299
+ new_obj = @doc.wrap(obj)
300
+ assert_equal({a: :b}, new_obj.value)
301
+ assert_equal('hallo', new_obj.raw_stream)
302
+ assert_equal(10, new_obj.oid)
303
+ assert_equal(20, new_obj.gen)
304
+ refute_same(obj, new_obj)
305
+
306
+ obj = @doc.wrap({a: :b}, oid: 10, gen: 20)
307
+ new_obj = @doc.wrap(obj)
308
+ refute_same(obj, new_obj)
309
+ end
310
+
311
+ it "allows overrding the data of the given PDF object" do
312
+ obj = @doc.wrap({a: :b}, oid: 10, gen: 20, stream: 'hallo')
313
+ new_obj = @doc.wrap(obj, oid: 15, gen: 25, stream: 'not')
314
+ assert_equal('not', new_obj.raw_stream)
315
+ assert_equal(15, new_obj.oid)
316
+ assert_equal(25, new_obj.gen)
317
+ end
318
+
319
+ it "sets the given oid/gen values on the returned object" do
320
+ obj = @doc.wrap(5, oid: 10, gen: 20)
321
+ assert_equal(10, obj.oid)
322
+ assert_equal(20, obj.gen)
323
+ end
324
+
325
+ it "uses the type/subtype information in the hash that should be wrapped" do
326
+ assert_kind_of(@myclass, @doc.wrap(Type: :MyClass))
327
+ assert_kind_of(@myclass2, @doc.wrap(Subtype: :TheSecond))
328
+ assert_kind_of(@myclass2, @doc.wrap(Type: :MyClass, Subtype: :TheSecond))
329
+ assert_kind_of(@myclass, @doc.wrap(Type: :MyClass, Subtype: :TheThird))
330
+ end
331
+
332
+ it "respects the given type/subtype arguments" do
333
+ assert_kind_of(@myclass, @doc.wrap({Type: :Other}, type: :MyClass))
334
+ assert_kind_of(@myclass2, @doc.wrap({Subtype: :Other}, subtype: :TheSecond))
335
+ assert_kind_of(@myclass2, @doc.wrap({Type: :Other, Subtype: :Other},
336
+ type: :MyClass, subtype: :TheSecond))
337
+ end
338
+
339
+ it "directly uses a class given via the type argument" do
340
+ obj = @doc.wrap({a: :b}, type: @myclass, oid: 5)
341
+ assert_kind_of(@myclass, obj)
342
+ obj = @doc.wrap(obj, type: @myclass2)
343
+ assert_kind_of(@myclass2, obj)
344
+ assert_equal(:b, obj.value[:a])
345
+ assert_equal(5, obj.oid)
346
+ end
347
+ end
348
+
349
+ describe "unwrap" do
350
+ it "returns a simple native ruby type" do
351
+ assert_equal(5, @doc.unwrap(5))
352
+ end
353
+
354
+ it "recursively unwraps arrays" do
355
+ assert_equal([5, 10, [200], [200]],
356
+ @io_doc.unwrap([5, HexaPDF::Reference.new(1, 0), [HexaPDF::Reference.new(2, 0)],
357
+ [HexaPDF::Reference.new(2, 0)]]))
358
+ end
359
+
360
+ it "recursively unwraps hashes" do
361
+ assert_equal({a: 5, b: 10, c: [200], d: [200]},
362
+ @io_doc.unwrap(a: 5, b: HexaPDF::Reference.new(1, 0),
363
+ c: [HexaPDF::Reference.new(2, 0)],
364
+ d: [HexaPDF::Reference.new(2, 0)]))
365
+ end
366
+
367
+ it "recursively unwraps PDF objects" do
368
+ assert_equal({a: 10}, @io_doc.unwrap(@io_doc.wrap(a: HexaPDF::Reference.new(1, 0))))
369
+ value = {a: HexaPDF::Object.new(b: HexaPDF::Object.new(10))}
370
+ assert_equal({a: {b: 10}}, @doc.unwrap(value))
371
+ end
372
+
373
+ it "fails to unwrap recursive structures" do
374
+ obj1 = @doc.add({})
375
+ obj2 = @doc.add({})
376
+ obj1.value[2] = obj2
377
+ obj2.value[1] = obj1
378
+ assert_raises(HexaPDF::Error) { @doc.unwrap(a: obj1) }
379
+ end
380
+ end
381
+
382
+ describe "each" do
383
+ it "iterates over the current objects" do
384
+ assert_equal([10, 200, nil], @io_doc.each(current: true).sort.map(&:value))
385
+ end
386
+
387
+ it "iterates over all objects" do
388
+ assert_equal([10, 200, 20, 30, nil], @io_doc.each(current: false).sort.map(&:value))
389
+ end
390
+
391
+ it "yields the revision as second argument if the block accepts exactly two arguments" do
392
+ objs = [[10, 20, 30], [200, nil]]
393
+ data = @io_doc.revisions.map.with_index {|rev, i| objs[i].map {|o| [o, rev]}}.reverse.flatten
394
+ @io_doc.each(current: false) do |obj, rev|
395
+ assert_equal(data.shift, obj.value)
396
+ assert_equal(data.shift, rev)
397
+ end
398
+ end
399
+ end
400
+
401
+ describe "encryption" do
402
+ it "checks for encryption based on the existence of the trailer's /Encrypt dictionary" do
403
+ refute(@doc.encrypted?)
404
+ @doc.trailer[:Encrypt] = {Filter: :Standard}
405
+ assert(@doc.encrypted?)
406
+ end
407
+
408
+ it "can set or delete a security handler via #encrypt" do
409
+ @doc.encrypt
410
+ refute_nil(@doc.security_handler)
411
+ assert(@doc.encrypted?)
412
+
413
+ @doc.encrypt(name: nil)
414
+ assert_nil(@doc.security_handler)
415
+ refute(@doc.encrypted?)
416
+ end
417
+
418
+ it "doesn't decrypt the document if document.auto_encrypt=false" do
419
+ test_file = File.join(TEST_DATA_DIR, 'standard-security-handler', 'nopwd-arc4-40bit-V1.pdf')
420
+ doc = HexaPDF::Document.new(io: StringIO.new(File.read(test_file)),
421
+ config: {'document.auto_decrypt' => false})
422
+ assert_kind_of(String, doc.trailer[:Info][:ModDate])
423
+ handler = HexaPDF::Encryption::SecurityHandler.set_up_decryption(doc)
424
+ assert_kind_of(Time, handler.decrypt(doc.trailer[:Info])[:ModDate])
425
+ end
426
+ end
427
+
428
+ describe "validate" do
429
+ before do
430
+ @doc.trailer.validate # to create a valid document
431
+ end
432
+
433
+ it "validates indirect objects" do
434
+ @doc.add(Type: :Catalog)
435
+ refute(@doc.validate(auto_correct: false))
436
+
437
+ called = false
438
+ assert(@doc.validate { called = true })
439
+ assert(called)
440
+ end
441
+
442
+ it "validates the trailer object" do
443
+ @doc.trailer[:ID] = :Symbol
444
+ refute(@doc.validate)
445
+ end
446
+ end
447
+
448
+
449
+ describe "write" do
450
+ it "writes the document to a file" do
451
+ begin
452
+ file = Tempfile.new('hexapdf-write')
453
+ file.close
454
+ @io_doc.write(file.path)
455
+ HexaPDF::Document.open(file.path) do |doc|
456
+ assert_equal(200, doc.object(2).value)
457
+ end
458
+ ensure
459
+ file.unlink
460
+ end
461
+ end
462
+
463
+ it "writes the document to an IO object" do
464
+ io = StringIO.new(''.b)
465
+ @doc.write(io)
466
+ refute(io.string.empty?)
467
+ end
468
+
469
+ it "fails if the document is not valid" do
470
+ @doc.trailer[:Size] = :Symbol
471
+ assert_raises(HexaPDF::Error) { @doc.write(StringIO.new(''.b)) }
472
+ end
473
+
474
+ it "update the ID and the Info's ModDate field" do
475
+ _, id1 = @doc.trailer.set_random_id
476
+
477
+ @doc.write(StringIO.new(''.b), update_fields: false)
478
+ assert_same(id1, @doc.trailer[:ID][1])
479
+ refute(@doc.trailer.info.key?(:ModDate))
480
+
481
+ @doc.write(StringIO.new(''.b))
482
+ refute_same(id1, (id2 = @doc.trailer[:ID][1]))
483
+ assert(@doc.trailer.info.key?(:ModDate))
484
+
485
+ @doc.trailer.info[:Author] = 'Me'
486
+ @doc.write(StringIO.new(''.b))
487
+ refute_same(id2, @doc.trailer[:ID][1])
488
+ assert(@doc.trailer.info.key?(:ModDate))
489
+ assert(@doc.trailer.info.key?(:Author))
490
+ end
491
+
492
+ it "it doesn't optimize the file by default" do
493
+ io = StringIO.new(''.b)
494
+ @io_doc.write(io)
495
+ doc = HexaPDF::Document.new(io: io)
496
+ assert_equal(0, doc.each.count {|o| o.type == :ObjStm})
497
+ end
498
+
499
+ it "allows optimizing the file by using object streams" do
500
+ io = StringIO.new(''.b)
501
+ @io_doc.write(io, optimize: true)
502
+ doc = HexaPDF::Document.new(io: io)
503
+ assert_equal(2, doc.each.count {|o| o.type == :ObjStm})
504
+ end
505
+ end
506
+
507
+ describe "version" do
508
+ it "uses the file header version of a loaded document" do
509
+ assert_equal('1.7', @io_doc.version)
510
+ end
511
+
512
+ it "uses the default version for a new document" do
513
+ assert_equal('1.2', @doc.version)
514
+ end
515
+
516
+ it "uses the catalog's /Version entry if it points to a later version" do
517
+ (@doc.trailer[:Root] ||= {})[:Version] = '1.4'
518
+ assert_equal('1.4', @doc.version)
519
+ end
520
+
521
+ it "allows setting the version" do
522
+ @doc.version = '1.4'
523
+ assert_equal('1.4', @doc.version)
524
+ end
525
+
526
+ it "fails setting a version with an invalid format" do
527
+ assert_raises(ArgumentError) { @doc.version = 'bla' }
528
+ end
529
+ end
530
+
531
+ describe "task" do
532
+ after do
533
+ HexaPDF::GlobalConfiguration['task.map'].delete(:test)
534
+ end
535
+
536
+ it "executes the given task with options" do
537
+ HexaPDF::GlobalConfiguration['task.map'][:test] = lambda do |doc, arg1:|
538
+ assert_equal(doc, @doc)
539
+ assert_equal(:arg1, arg1)
540
+ end
541
+ @doc.task(:test, arg1: :arg1)
542
+ end
543
+
544
+ it "executes the given task with a block" do
545
+ HexaPDF::GlobalConfiguration['task.map'][:test] = lambda do |doc, **, &block|
546
+ assert_equal(doc, @doc)
547
+ block.call('inside')
548
+ end
549
+ assert_equal(:done, @doc.task(:test) {|msg| assert_equal('inside', msg); :done})
550
+ end
551
+
552
+ it "fails if the given task is not available" do
553
+ assert_raises(HexaPDF::Error) { @doc.task(:unknown) }
554
+ end
555
+ end
556
+
557
+ describe "pages" do
558
+ it "returns the root node of the page tree" do
559
+ pages = @doc.pages
560
+ assert_equal(:Pages, pages.type)
561
+ end
562
+ end
563
+
564
+ describe "listener interface" do
565
+ it "allows registering and dispatching messages" do
566
+ args = []
567
+ callable = lambda {|*a| args << [:callable, a]}
568
+ @doc.register_listener(:something, callable)
569
+ @doc.register_listener(:something) {|*a| args << [:block, a]}
570
+ @doc.dispatch_message(:something, :arg)
571
+ assert_equal([[:callable, [:arg]], [:block, [:arg]]], args)
572
+ end
573
+ end
574
+ end
@@ -0,0 +1,144 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'tempfile'
6
+ require 'hexapdf/document'
7
+
8
+ describe HexaPDF::DocumentUtils::Images do
9
+ before do
10
+ @doc = HexaPDF::Document.new
11
+ end
12
+
13
+ describe "add_image" do
14
+ describe "using a custom image loader" do
15
+ before do
16
+ @loader = Object.new
17
+ @loader.define_singleton_method(:handles?) {|*| true}
18
+ @loader.define_singleton_method(:load) do |doc, s|
19
+ s = HexaPDF::StreamData.new(s) if s.kind_of?(IO)
20
+ doc.add({Subtype: :Image}, stream: s)
21
+ end
22
+ HexaPDF::GlobalConfiguration['image_loader'].unshift(@loader)
23
+ end
24
+
25
+ after do
26
+ HexaPDF::GlobalConfiguration['image_loader'].delete(@loader)
27
+ end
28
+
29
+ it "adds an image using a filename" do
30
+ data = 'test'
31
+ image = @doc.utils.add_image(data)
32
+ assert_equal(data, image.stream)
33
+ assert_equal(File.absolute_path(data), image.source_path)
34
+ end
35
+
36
+ it "adds an image using an IO" do
37
+ File.open(__FILE__, 'rb') do |file|
38
+ image = @doc.utils.add_image(file)
39
+ assert_equal(File.read(__FILE__), image.stream)
40
+ assert_equal(File.absolute_path(__FILE__), image.source_path)
41
+ end
42
+ end
43
+
44
+ it "doesn't add an image twice" do
45
+ data = 'test'
46
+ image = @doc.utils.add_image(data)
47
+ image1 = @doc.utils.add_image(data)
48
+ assert_same(image, image1)
49
+ end
50
+ end
51
+
52
+ it "fails if the needed image loader can't be resolved" do
53
+ begin
54
+ HexaPDF::GlobalConfiguration['image_loader'].unshift('SomeUnknownConstantHere')
55
+ exp = assert_raises(HexaPDF::Error) { @doc.utils.add_image(StringIO.new('test')) }
56
+ assert_match(/image loader from configuration/, exp.message)
57
+ ensure
58
+ HexaPDF::GlobalConfiguration['image_loader'].shift
59
+ end
60
+ end
61
+
62
+ it "fails if no image loader is found" do
63
+ exp = assert_raises(HexaPDF::Error) { @doc.utils.add_image(StringIO.new('test')) }
64
+ assert_match(/suitable image loader/, exp.message)
65
+ end
66
+ end
67
+
68
+ describe "each" do
69
+ it "iterates over all non-mask images" do
70
+ images = []
71
+ images << @doc.add(Subtype: :Image)
72
+ images << @doc.add(Subtype: :Image, Mask: [5, 6])
73
+ images << @doc.add(Subtype: :Image, Mask: @doc.add(Subtype: :Image))
74
+ images << @doc.add(Subtype: :Image, SMask: @doc.add(Subtype: :Image))
75
+ assert_equal(images.sort, @doc.utils.each_image.to_a.sort)
76
+ end
77
+ end
78
+ end
79
+
80
+
81
+ describe HexaPDF::DocumentUtils::Files do
82
+ before do
83
+ @doc = HexaPDF::Document.new
84
+ @data = "embed-test"
85
+ @file = Tempfile.new('file-embed-test')
86
+ @file.write(@data)
87
+ @file.close
88
+ end
89
+
90
+ after do
91
+ @file.unlink
92
+ end
93
+
94
+ describe "add_file" do
95
+ it "adds a file using a filename and embeds it" do
96
+ spec = @doc.utils.add_file(@file.path)
97
+ assert_equal(File.basename(@file.path), spec.path)
98
+ assert_equal(@data, spec.embedded_file_stream.stream)
99
+ end
100
+
101
+ it "adds a reference to a file" do
102
+ spec = @doc.utils.add_file(@file.path, embed: false)
103
+ assert_equal(File.basename(@file.path), spec.path)
104
+ refute(spec.embedded_file?)
105
+ end
106
+
107
+ it "adds a file using an IO" do
108
+ @file.open
109
+ spec = @doc.utils.add_file(@file, name: 'test', embed: false)
110
+ assert_equal('test', spec.path)
111
+ assert_equal(@data, spec.embedded_file_stream.stream)
112
+ end
113
+
114
+ it "optionally sets the description of the file" do
115
+ spec = @doc.utils.add_file(@file.path, description: 'Some file')
116
+ assert_equal('Some file', spec[:Desc])
117
+ end
118
+
119
+ it "requires the name argument when given an IO object" do
120
+ assert_raises(ArgumentError) { @doc.utils.add_file(StringIO.new) }
121
+ end
122
+ end
123
+
124
+ describe "each_file" do
125
+ it "iterates only over named embedded files and file annotations if search=false" do
126
+ @doc.add(Type: :Filespec)
127
+ spec1 = @doc.utils.add_file(__FILE__)
128
+ spec2 = @doc.add(Type: :Filespec)
129
+ @doc.pages.add_page[:Annots] = [
130
+ {Subtype: :FileAttachment, FS: HexaPDF::Reference.new(spec1.oid, spec1.gen)},
131
+ {Subtype: :FileAttachment, FS: spec2},
132
+ {},
133
+ ]
134
+ assert_equal([spec1, spec2], @doc.utils.each_file.to_a)
135
+ end
136
+
137
+ it "iterates over all file specifications of the document if search=true" do
138
+ specs = []
139
+ specs << @doc.add(Type: :Filespec)
140
+ specs << @doc.add(Type: :Filespec)
141
+ assert_equal(specs, @doc.utils.each_file(search: true).to_a)
142
+ end
143
+ end
144
+ end