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,84 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/type/object_stream'
5
+
6
+ describe HexaPDF::Type::ObjectStream::Data do
7
+ before do
8
+ @data = HexaPDF::Type::ObjectStream::Data.new("5 [1 2]", [1, 5], [0, 2])
9
+ end
10
+
11
+ it "returns the correct [object, oid] pair for a given index" do
12
+ assert_equal([5, 1], @data.object_by_index(0))
13
+ assert_equal([[1, 2], 5], @data.object_by_index(1))
14
+ end
15
+
16
+ it "fails if the index is out of bounds" do
17
+ assert_raises(ArgumentError) { @data.object_by_index(5) }
18
+ assert_raises(ArgumentError) { @data.object_by_index(-1) }
19
+ end
20
+ end
21
+
22
+
23
+ describe HexaPDF::Type::ObjectStream do
24
+ before do
25
+ @doc = Object.new
26
+ @doc.instance_variable_set(:@version, '1.5')
27
+ def (@doc).trailer
28
+ @trailer ||= {Encrypt: HexaPDF::Object.new({}, oid: 9)}
29
+ end
30
+ @obj = HexaPDF::Type::ObjectStream.new({}, oid: 1, document: @doc)
31
+ end
32
+
33
+ it "correctly parses stream data" do
34
+ @obj.value = {N: 2, First: 8}
35
+ @obj.stream = "1 0 5 2 5 [1 2]"
36
+ data = @obj.parse_stream
37
+ assert_equal([5, 1], data.object_by_index(0))
38
+ assert_equal([[1, 2], 5], data.object_by_index(1))
39
+ end
40
+
41
+ it "allows adding and deleting object as well as determining their index" do
42
+ @obj.add_object(5)
43
+ @obj.add_object(7)
44
+ @obj.add_object(9)
45
+ assert_equal(0, @obj.object_index(5))
46
+ assert_equal(1, @obj.object_index(7))
47
+ assert_equal(2, @obj.object_index(9))
48
+
49
+ @obj.delete_object(5)
50
+ assert_equal(0, @obj.object_index(9))
51
+ assert_equal(1, @obj.object_index(7))
52
+ assert_equal(nil, @obj.object_index(5))
53
+
54
+ @obj.delete_object(7)
55
+ @obj.delete_object(9)
56
+ assert_equal(nil, @obj.object_index(5))
57
+ end
58
+
59
+ it "allows writing the objects to the stream" do
60
+ @obj.stream = 'something'
61
+ @obj.add_object(HexaPDF::Object.new(5, oid: 1))
62
+ @obj.add_object(HexaPDF::Object.new(:will_be_deleted, oid: 3, gen: 1))
63
+ @obj.add_object(HexaPDF::Object.new([1, 2], oid: 5))
64
+ @obj.add_object(HexaPDF::Object.new(nil, oid: 7))
65
+ @obj.add_object(@doc.trailer[:Encrypt])
66
+
67
+ revision = Object.new
68
+ def revision.object(obj); obj; end
69
+ @obj.write_objects(revision)
70
+
71
+ assert_equal(2, @obj.value[:N])
72
+ assert_equal(8, @obj.value[:First])
73
+ assert_equal("1 0 5 2 5 [1 2] ", @obj.stream)
74
+ end
75
+
76
+ it "fails validation if gen != 0" do
77
+ assert(@obj.validate(auto_correct: false))
78
+ @obj.gen = 1
79
+ refute(@obj.validate(auto_correct: false) do |msg, correctable|
80
+ assert_match(/invalid generation/, msg)
81
+ refute(correctable)
82
+ end)
83
+ end
84
+ end
@@ -0,0 +1,260 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative '../content/common'
5
+ require 'stringio'
6
+ require 'hexapdf/document'
7
+ require 'hexapdf/type/page'
8
+
9
+ describe HexaPDF::Type::Page do
10
+ before do
11
+ @doc = HexaPDF::Document.new
12
+ end
13
+
14
+ # Asserts that the page's contents contains the operators.
15
+ def assert_operators(page, operators)
16
+ processor = TestHelper::OperatorRecorder.new
17
+ page.process_contents(processor)
18
+ assert_equal(operators, processor.recorded_ops)
19
+ end
20
+
21
+ it "must always be indirect" do
22
+ page = @doc.add(Type: :Page)
23
+ page.must_be_indirect = false
24
+ assert(page.must_be_indirect?)
25
+ end
26
+
27
+ describe "[]" do
28
+ before do
29
+ @root = @doc.add(Type: :Pages)
30
+ @kid = @doc.add(Type: :Pages, Parent: @root)
31
+ @page = @doc.add(Type: :Page, Parent: @kid)
32
+ end
33
+
34
+ it "works normal for non-inheritable fields" do
35
+ assert_equal(:Page, @page[:Type])
36
+ assert_nil(@page[:Dur])
37
+ end
38
+
39
+ it "automatically retrieves inherited values" do
40
+ @root[:MediaBox] = :media
41
+ assert_equal(:media, @page[:MediaBox])
42
+
43
+ @root[:Resources] = :root_res
44
+ @kid[:Resources] = :res
45
+ assert_equal(:res, @page[:Resources])
46
+
47
+ @page[:CropBox] = :cropbox
48
+ assert_equal(:cropbox, @page[:CropBox])
49
+
50
+ @kid[:Rotate] = :kid_rotate
51
+ assert_equal(:kid_rotate, @page[:Rotate])
52
+ @kid.delete(:Rotate)
53
+ assert_equal(0, @page[:Rotate])
54
+ end
55
+
56
+ it "fails if no parent node is associated" do
57
+ page = @doc.add(Type: :Page)
58
+ assert_raises(HexaPDF::InvalidPDFObjectError) { page[:Resources] }
59
+ end
60
+ end
61
+
62
+ describe "validation" do
63
+ it "fails if a required inheritable field is not set" do
64
+ root = @doc.add(Type: :Pages)
65
+ page = @doc.add(Type: :Page, Parent: root)
66
+ message = ''
67
+ refute(page.validate {|m, _| message = m})
68
+ assert_match(/inheritable.*MediaBox/i, message)
69
+ end
70
+ end
71
+
72
+ describe "box" do
73
+ before do
74
+ @page = @doc.pages.add_page
75
+ end
76
+
77
+ it "returns the correct media box" do
78
+ @page[:MediaBox] = :media
79
+ assert_equal(:media, @page.box(:media))
80
+ end
81
+
82
+ it "returns the correct crop box" do
83
+ @page[:MediaBox] = :media
84
+ assert_equal(:media, @page.box(:crop))
85
+ @page[:CropBox] = :crop
86
+ assert_equal(:crop, @page.box(:crop))
87
+ end
88
+
89
+ it "returns the correct bleed, trim and art boxes" do
90
+ @page[:CropBox] = :crop
91
+ assert_equal(:crop, @page.box(:bleed))
92
+ assert_equal(:crop, @page.box(:trim))
93
+ assert_equal(:crop, @page.box(:art))
94
+ @page[:BleedBox] = :bleed
95
+ @page[:TrimBox] = :trim
96
+ @page[:ArtBox] = :art
97
+ assert_equal(:bleed, @page.box(:bleed))
98
+ assert_equal(:trim, @page.box(:trim))
99
+ assert_equal(:art, @page.box(:art))
100
+ end
101
+
102
+ it "fails if an unknown box type is supplied" do
103
+ assert_raises(ArgumentError) { @page.box(:undefined) }
104
+ end
105
+ end
106
+
107
+ describe "contents" do
108
+ it "returns the contents of a single content stream" do
109
+ page = @doc.pages.add_page
110
+ page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
111
+ assert_equal('q 10 w Q', page.contents)
112
+ end
113
+
114
+ it "returns the concatenated contents of multiple content stream" do
115
+ page = @doc.pages.add_page
116
+ page[:Contents] = [@doc.wrap({}, stream: 'q 10'), @doc.wrap({}, stream: 'w Q')]
117
+ assert_equal('q 10 w Q', page.contents)
118
+ end
119
+ end
120
+
121
+ describe "contents=" do
122
+ it "creates a content stream if none already exist" do
123
+ page = @doc.pages.add_page
124
+ page.contents = 'test'
125
+ assert_equal('test', page[:Contents].stream)
126
+ end
127
+
128
+ it "reuses an existing content stream" do
129
+ page = @doc.pages.add_page
130
+ page[:Contents] = content = @doc.wrap({}, stream: 'q 10 w Q')
131
+ page.contents = 'test'
132
+ assert_equal(content, page[:Contents])
133
+ assert_equal('test', content.stream)
134
+ end
135
+
136
+ it "reuses the first content stream and deletes the rest if more than one exist" do
137
+ page = @doc.pages.add_page
138
+ page[:Contents] = [content = @doc.add({}, stream: 'q 10 w Q'), @doc.add({}, stream: 'q Q')]
139
+ page.contents = 'test'
140
+ assert_equal(content, page[:Contents])
141
+ assert_equal('test', content.stream)
142
+ end
143
+ end
144
+
145
+ describe "resources" do
146
+ it "creates the resource dictionary if it is not found" do
147
+ page = @doc.add(Type: :Page, Parent: @doc.pages)
148
+ resources = page.resources
149
+ assert_equal(:XXResources, resources.type)
150
+ assert_equal({}, resources.value)
151
+ end
152
+
153
+ it "returns the already used resource dictionary" do
154
+ @doc.pages[:Resources] = {Font: {F1: nil}}
155
+ page = @doc.pages.add_page(@doc.add(Type: :Page))
156
+ resources = page.resources
157
+ assert_equal(:XXResources, resources.type)
158
+ assert_equal(@doc.pages[:Resources], resources)
159
+ end
160
+ end
161
+
162
+ describe "process_contents" do
163
+ it "parses the contents and processes it" do
164
+ page = @doc.pages.add_page
165
+ page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
166
+ assert_operators(page, [[:save_graphics_state], [:set_line_width, [10]],
167
+ [:restore_graphics_state]])
168
+ end
169
+ end
170
+
171
+ describe "canvas" do
172
+ before do
173
+ @page = @doc.pages.add_page
174
+ end
175
+
176
+ it "works correctly if invoked on an empty page, using type :page in first invocation" do
177
+ @page.canvas.line_width = 10
178
+ assert_operators(@page, [[:set_line_width, [10]]])
179
+
180
+ @page.canvas(type: :overlay).line_width = 5
181
+ assert_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
182
+ [:save_graphics_state], [:set_line_width, [10]],
183
+ [:restore_graphics_state], [:set_line_width, [5]]])
184
+
185
+ @page.canvas(type: :underlay).line_width = 2
186
+ assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
187
+ [:restore_graphics_state], [:save_graphics_state],
188
+ [:set_line_width, [10]],
189
+ [:restore_graphics_state], [:set_line_width, [5]]])
190
+ end
191
+
192
+ it "works correctly if invoked on an empty page, using type :underlay in first invocation" do
193
+ @page.canvas(type: :underlay).line_width = 2
194
+ assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
195
+ [:restore_graphics_state], [:save_graphics_state],
196
+ [:restore_graphics_state]])
197
+
198
+ @page.canvas.line_width = 10
199
+ assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
200
+ [:restore_graphics_state], [:save_graphics_state],
201
+ [:set_line_width, [10]], [:restore_graphics_state]])
202
+
203
+ @page.canvas(type: :overlay).line_width = 5
204
+ assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
205
+ [:restore_graphics_state], [:save_graphics_state],
206
+ [:set_line_width, [10]],
207
+ [:restore_graphics_state], [:set_line_width, [5]]])
208
+ end
209
+
210
+ it "works correctly if invoked on a page with existing contents" do
211
+ @page.contents = "10 w"
212
+
213
+ @page.canvas(type: :overlay).line_width = 5
214
+ assert_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
215
+ [:save_graphics_state], [:set_line_width, [10]],
216
+ [:restore_graphics_state], [:set_line_width, [5]]])
217
+
218
+ @page.canvas(type: :underlay).line_width = 2
219
+ assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
220
+ [:restore_graphics_state], [:save_graphics_state],
221
+ [:set_line_width, [10]],
222
+ [:restore_graphics_state], [:set_line_width, [5]]])
223
+ end
224
+
225
+ it "fails if the page canvas is requested for a page with existing contents" do
226
+ @page.contents = "q Q"
227
+ assert_raises(HexaPDF::Error) { @page.canvas }
228
+ end
229
+
230
+ it "fails if called with an incorrect type argument" do
231
+ assert_raises(ArgumentError) { @page.canvas(type: :something) }
232
+ end
233
+ end
234
+
235
+ describe "to_form_xobject" do
236
+ it "creates an independent form xobject" do
237
+ page = @doc.pages.add_page
238
+ page.contents = "test"
239
+ form = page.to_form_xobject
240
+ refute(form.indirect?)
241
+ assert_equal(form.box.value, page.box.value)
242
+ end
243
+
244
+ it "works for pages without content" do
245
+ page = @doc.pages.add_page
246
+ form = page.to_form_xobject
247
+ assert_equal('', form.stream)
248
+ end
249
+
250
+ it "uses the raw stream data if possible to avoid unnecessary work" do
251
+ page = @doc.pages.add_page
252
+ page.contents = HexaPDF::StreamData.new(StringIO.new("test"))
253
+ form = page.to_form_xobject
254
+ assert_same(form.raw_stream, page[:Contents].raw_stream)
255
+
256
+ form = page.to_form_xobject(reference: false)
257
+ refute_same(form.raw_stream, page[:Contents].raw_stream)
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,255 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/page_tree_node'
6
+
7
+ describe HexaPDF::Type::PageTreeNode do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @root = @doc.add(Type: :Pages)
11
+ end
12
+
13
+ # Defines the following page tree:
14
+ #
15
+ # @root
16
+ # @kid1
17
+ # @kid11
18
+ # @pages[0]
19
+ # @pages[1]
20
+ # @kid12
21
+ # @pages[2]
22
+ # @pages[3]
23
+ # @pages[4]
24
+ # @pages[5]
25
+ # @kid2
26
+ # @pages[6]
27
+ # @pages[7]
28
+ def define_multilevel_page_tree
29
+ @pages = 8.times.map { @doc.add(Type: :Page) }
30
+ @kid1 = @doc.add(Type: :Pages, Parent: @root, Count: 5)
31
+ @kid11 = @doc.add(Type: :Pages, Parent: @kid1)
32
+ @kid11.add_page(@pages[0])
33
+ @kid11.add_page(@pages[1])
34
+ @kid12 = @doc.add(Type: :Pages, Parent: @kid1)
35
+ @kid12.add_page(@pages[2])
36
+ @kid12.add_page(@pages[3])
37
+ @kid12.add_page(@pages[4])
38
+ @kid1[:Kids] << @kid11 << @kid12
39
+ @root[:Kids] << @kid1
40
+
41
+ @pages[5][:Parent] = @root
42
+ @root[:Kids] << @pages[5]
43
+
44
+ @kid2 = @doc.add(Type: :Pages, Parent: @root)
45
+ @kid2.add_page(@pages[6])
46
+ @kid2.add_page(@pages[7])
47
+ @root[:Kids] << @kid2
48
+ @root[:Count] = 8
49
+ end
50
+
51
+ it "must always be indirect" do
52
+ pages = @doc.add(Type: :Pages)
53
+ pages.must_be_indirect = false
54
+ assert(pages.must_be_indirect?)
55
+ end
56
+
57
+ describe "page" do
58
+ before do
59
+ define_multilevel_page_tree
60
+ end
61
+
62
+ it "returns the page for a given index" do
63
+ assert_equal(@pages[0], @root.page(0))
64
+ assert_equal(@pages[3], @root.page(3))
65
+ assert_equal(@pages[5], @root.page(5))
66
+ assert_equal(@pages[7], @root.page(7))
67
+ end
68
+
69
+ it "works with negative indicies counting backwards from the end" do
70
+ assert_equal(@pages[0], @root.page(-8))
71
+ assert_equal(@pages[3], @root.page(-5))
72
+ assert_equal(@pages[5], @root.page(-3))
73
+ assert_equal(@pages[7], @root.page(-1))
74
+ end
75
+
76
+ it "returns nil for bad indices" do
77
+ assert_nil(@root.page(20))
78
+ assert_nil(@root.page(-20))
79
+ end
80
+ end
81
+
82
+ describe "insert_page" do
83
+ it "uses an empty new page when none is provided" do
84
+ page = @root.insert_page(3)
85
+ assert_equal([page], @root[:Kids])
86
+ assert_equal(1, @root.page_count)
87
+ assert_equal(:Page, page[:Type])
88
+ assert_equal(@root, page[:Parent])
89
+ assert_kind_of(HexaPDF::Rectangle, page[:MediaBox])
90
+ assert_equal({}, page[:Resources].value)
91
+ refute(@root.value.key?(:Parent))
92
+ end
93
+
94
+ it "doesn't create a /Resources entry if an inherited one exists" do
95
+ @root[:Resources] = {Font: {F1: nil}}
96
+ page = @root.insert_page(3)
97
+ assert_equal(@root[:Resources], page[:Resources])
98
+ end
99
+
100
+ it "inserts the provided page at the given index" do
101
+ page = @doc.wrap(Type: :Page)
102
+ assert_equal(page, @root.insert_page(3, page))
103
+ assert_equal([page], @root[:Kids])
104
+ assert_equal(@root, page[:Parent])
105
+ refute(@root.value.key?(:Parent))
106
+ end
107
+
108
+ it "inserts multiple pages correctly in an empty root node" do
109
+ page3 = @root.insert_page(5)
110
+ page1 = @root.insert_page(0)
111
+ page2 = @root.insert_page(1)
112
+ assert_equal([page1, page2, page3], @root[:Kids])
113
+ assert_equal(3, @root.page_count)
114
+ end
115
+
116
+ it "inserts multiple pages correctly in a multilevel page tree" do
117
+ define_multilevel_page_tree
118
+ page = @root.insert_page(2)
119
+ assert_equal([@pages[0], @pages[1], page], @kid11[:Kids])
120
+ assert_equal(3, @kid11.page_count)
121
+ assert_equal(6, @kid1.page_count)
122
+ assert_equal(9, @root.page_count)
123
+
124
+ page = @root.insert_page(4)
125
+ assert_equal([@pages[2], page, @pages[3], @pages[4]], @kid12[:Kids])
126
+ assert_equal(4, @kid12.page_count)
127
+ assert_equal(7, @kid1.page_count)
128
+ assert_equal(10, @root.page_count)
129
+
130
+ page = @root.insert_page(8)
131
+ assert_equal([@kid1, @pages[5], page, @kid2], @root[:Kids])
132
+ assert_equal(11, @root.page_count)
133
+
134
+ page = @root.insert_page(100)
135
+ assert_equal([@kid1, @pages[5], @root[:Kids][2], @kid2, page], @root[:Kids])
136
+ assert_equal(12, @root.page_count)
137
+ end
138
+
139
+ it "allows negative indices to be specified" do
140
+ define_multilevel_page_tree
141
+ page = @root.insert_page(-1)
142
+ assert_equal(page, @root[:Kids].last)
143
+
144
+ page = @root.insert_page(-4)
145
+ assert_equal(page, @root[:Kids][2])
146
+ end
147
+ end
148
+
149
+ describe "delete_page" do
150
+ before do
151
+ define_multilevel_page_tree
152
+ end
153
+
154
+ it "does nothing if the page index is not valid" do
155
+ assert_nil(@root.delete_page(20))
156
+ assert_nil(@root.delete_page(-20))
157
+ assert_equal(8, @root.page_count)
158
+ end
159
+
160
+ it "deletes the correct page" do
161
+ assert_equal(@pages[2], @root.delete_page(2))
162
+ assert_equal(2, @kid12.page_count)
163
+ assert_equal(4, @kid1.page_count)
164
+ assert_equal(7, @root.page_count)
165
+
166
+ assert_equal(@pages[5], @root.delete_page(4))
167
+ assert_equal(6, @root.page_count)
168
+ end
169
+
170
+ it "deletes intermediate page tree nodes if they contain only one child after deletion" do
171
+ assert_equal(@pages[0], @root.delete_page(0))
172
+ assert_equal(4, @kid1.page_count)
173
+ assert_equal(7, @root.page_count)
174
+ assert_nil(@doc.object(@kid11).value)
175
+ assert_equal(@pages[1], @kid1[:Kids][0])
176
+ end
177
+
178
+ it "deletes intermediate page tree nodes if they don't have any children after deletion" do
179
+ node = @doc.add(Type: :Pages, Parent: @root)
180
+ page = node.add_page
181
+ @root[:Kids] << node
182
+ @root[:Count] += 1
183
+
184
+ assert_equal(page, @root.delete_page(-1))
185
+ assert_nil(@doc.object(node).value)
186
+ refute_equal(node, @root[:Kids].last)
187
+ assert(8, @root.page_count)
188
+ end
189
+ end
190
+
191
+ describe "each_page" do
192
+ before do
193
+ define_multilevel_page_tree
194
+ end
195
+
196
+ it "iterates over a simple, one-level page tree" do
197
+ assert_equal([@pages[2], @pages[3], @pages[4]], @kid12.each_page.to_a)
198
+ end
199
+
200
+ it "iterates over a multilevel page tree" do
201
+ assert_equal(@pages, @root.each_page.to_a)
202
+ end
203
+ end
204
+
205
+ describe "validation" do
206
+ it "corrects faulty /Count entries" do
207
+ define_multilevel_page_tree
208
+ root_count = @root.page_count
209
+ @root[:Count] = -5
210
+ kid_count = @kid12.page_count
211
+ @kid12[:Count] = 100
212
+
213
+ called_msg = ''
214
+ refute(@root.validate(auto_correct: false) {|msg, _| called_msg = msg})
215
+ assert_match(/Count.*invalid/, called_msg)
216
+
217
+ assert(@root.validate)
218
+ assert_equal(root_count, @root.page_count)
219
+ assert_equal(kid_count, @kid12.page_count)
220
+ end
221
+
222
+ it "corrects faulty /Parent entries" do
223
+ define_multilevel_page_tree
224
+ @kid12.delete(:Parent)
225
+ @kid2.delete(:Parent)
226
+
227
+ called_msg = ''
228
+ refute(@root.validate(auto_correct: false) {|msg, _| called_msg = msg})
229
+ assert_match(/Parent.*invalid/, called_msg)
230
+
231
+ assert(@root.validate)
232
+ assert_equal(@kid1, @kid12[:Parent])
233
+ assert_equal(@root, @kid2[:Parent])
234
+ end
235
+
236
+ it "removes invalid objects from the page tree (like null objects)" do
237
+ define_multilevel_page_tree
238
+ assert(@root.validate(auto_correct: false) {|m, _| p m})
239
+
240
+ @doc.delete(@pages[3])
241
+ refute(@root.validate(auto_correct: false)) do |msg, _|
242
+ assert_match(/invalid object/i, msg)
243
+ end
244
+ assert(@root.validate)
245
+ assert_equal(2, @kid12[:Count])
246
+ assert_equal([@pages[2], @pages[4]], @kid12[:Kids])
247
+ end
248
+
249
+ it "needs at least one page node" do
250
+ refute(@root.validate(auto_correct: false))
251
+ assert(@root.validate)
252
+ assert_equal(1, @root.page_count)
253
+ end
254
+ end
255
+ end