hexapdf 0.46.0 → 1.6.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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +342 -16
  3. data/LICENSE +1 -1
  4. data/README.md +9 -8
  5. data/examples/009-text_layouter_alignment.rb +4 -0
  6. data/examples/010-text_layouter_inline_boxes.rb +4 -0
  7. data/examples/011-text_layouter_line_wrapping.rb +4 -0
  8. data/examples/012-text_layouter_styling.rb +9 -4
  9. data/examples/013-text_layouter_shapes.rb +5 -0
  10. data/examples/015-boxes.rb +3 -0
  11. data/examples/016-frame_automatic_box_placement.rb +3 -0
  12. data/examples/017-frame_text_flow.rb +3 -0
  13. data/examples/022-outline.rb +5 -1
  14. data/examples/{028-frame_mask_mode.rb → 028-composer_mask_mode.rb} +3 -3
  15. data/lib/hexapdf/cli/batch.rb +1 -1
  16. data/lib/hexapdf/cli/command.rb +65 -65
  17. data/lib/hexapdf/cli/debug_info.rb +98 -0
  18. data/lib/hexapdf/cli/files.rb +1 -1
  19. data/lib/hexapdf/cli/fonts.rb +1 -1
  20. data/lib/hexapdf/cli/form.rb +11 -6
  21. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  22. data/lib/hexapdf/cli/images.rb +19 -4
  23. data/lib/hexapdf/cli/info.rb +1 -1
  24. data/lib/hexapdf/cli/inspect.rb +24 -8
  25. data/lib/hexapdf/cli/merge.rb +1 -1
  26. data/lib/hexapdf/cli/modify.rb +1 -2
  27. data/lib/hexapdf/cli/optimize.rb +6 -6
  28. data/lib/hexapdf/cli/split.rb +1 -1
  29. data/lib/hexapdf/cli/usage.rb +1 -1
  30. data/lib/hexapdf/cli/watermark.rb +1 -1
  31. data/lib/hexapdf/cli.rb +20 -2
  32. data/lib/hexapdf/composer.rb +22 -1
  33. data/lib/hexapdf/configuration.rb +56 -1
  34. data/lib/hexapdf/content/canvas.rb +1 -1
  35. data/lib/hexapdf/content/canvas_composer.rb +1 -1
  36. data/lib/hexapdf/content/color_space.rb +1 -1
  37. data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
  38. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  39. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  40. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  41. data/lib/hexapdf/content/graphic_object.rb +1 -1
  42. data/lib/hexapdf/content/graphics_state.rb +2 -2
  43. data/lib/hexapdf/content/operator.rb +1 -1
  44. data/lib/hexapdf/content/parser.rb +22 -23
  45. data/lib/hexapdf/content/processor.rb +1 -1
  46. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  47. data/lib/hexapdf/content.rb +1 -1
  48. data/lib/hexapdf/data_dir.rb +1 -1
  49. data/lib/hexapdf/dictionary.rb +8 -2
  50. data/lib/hexapdf/dictionary_fields.rb +2 -2
  51. data/lib/hexapdf/digital_signature/cms_handler.rb +19 -2
  52. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  53. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +1 -1
  54. data/lib/hexapdf/digital_signature/signature.rb +2 -2
  55. data/lib/hexapdf/digital_signature/signatures.rb +1 -1
  56. data/lib/hexapdf/digital_signature/signing/default_handler.rb +3 -3
  57. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -2
  58. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +25 -5
  59. data/lib/hexapdf/digital_signature/signing.rb +1 -1
  60. data/lib/hexapdf/digital_signature/verification_result.rb +1 -1
  61. data/lib/hexapdf/digital_signature.rb +1 -1
  62. data/lib/hexapdf/document/annotations.rb +220 -0
  63. data/lib/hexapdf/document/destinations.rb +1 -1
  64. data/lib/hexapdf/document/files.rb +1 -1
  65. data/lib/hexapdf/document/fonts.rb +1 -1
  66. data/lib/hexapdf/document/images.rb +1 -1
  67. data/lib/hexapdf/document/layout.rb +95 -16
  68. data/lib/hexapdf/document/metadata.rb +11 -4
  69. data/lib/hexapdf/document/pages.rb +1 -1
  70. data/lib/hexapdf/document.rb +52 -9
  71. data/lib/hexapdf/encryption/aes.rb +1 -1
  72. data/lib/hexapdf/encryption/arc4.rb +3 -3
  73. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  74. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  75. data/lib/hexapdf/encryption/identity.rb +1 -1
  76. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  77. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  78. data/lib/hexapdf/encryption/security_handler.rb +4 -2
  79. data/lib/hexapdf/encryption/standard_security_handler.rb +40 -29
  80. data/lib/hexapdf/encryption.rb +1 -1
  81. data/lib/hexapdf/error.rb +12 -4
  82. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  83. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  84. data/lib/hexapdf/filter/crypt.rb +1 -1
  85. data/lib/hexapdf/filter/encryption.rb +1 -1
  86. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  87. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  88. data/lib/hexapdf/filter/pass_through.rb +1 -1
  89. data/lib/hexapdf/filter/predictor.rb +1 -1
  90. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  91. data/lib/hexapdf/filter.rb +1 -1
  92. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  93. data/lib/hexapdf/font/cmap/writer.rb +59 -5
  94. data/lib/hexapdf/font/cmap.rb +18 -7
  95. data/lib/hexapdf/font/encoding/base.rb +28 -1
  96. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  97. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  98. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  99. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  100. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  101. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  102. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  103. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  104. data/lib/hexapdf/font/encoding.rb +1 -1
  105. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  106. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  107. data/lib/hexapdf/font/true_type/font.rb +1 -1
  108. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  109. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  114. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  115. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  116. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  117. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  118. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  119. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  120. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  121. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  122. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  123. data/lib/hexapdf/font/true_type/table.rb +7 -2
  124. data/lib/hexapdf/font/true_type.rb +1 -1
  125. data/lib/hexapdf/font/true_type_wrapper.rb +51 -16
  126. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  127. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  128. data/lib/hexapdf/font/type1/font.rb +1 -1
  129. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  130. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  131. data/lib/hexapdf/font/type1.rb +1 -1
  132. data/lib/hexapdf/font/type1_wrapper.rb +3 -4
  133. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  134. data/lib/hexapdf/font_loader/from_file.rb +1 -1
  135. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  136. data/lib/hexapdf/font_loader/variant_from_name.rb +1 -1
  137. data/lib/hexapdf/font_loader.rb +1 -1
  138. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  139. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  140. data/lib/hexapdf/image_loader/png.rb +1 -1
  141. data/lib/hexapdf/image_loader.rb +1 -1
  142. data/lib/hexapdf/importer.rb +2 -2
  143. data/lib/hexapdf/layout/box.rb +6 -1
  144. data/lib/hexapdf/layout/box_fitter.rb +1 -1
  145. data/lib/hexapdf/layout/column_box.rb +1 -1
  146. data/lib/hexapdf/layout/container_box.rb +64 -29
  147. data/lib/hexapdf/layout/frame.rb +1 -1
  148. data/lib/hexapdf/layout/image_box.rb +1 -1
  149. data/lib/hexapdf/layout/inline_box.rb +1 -1
  150. data/lib/hexapdf/layout/line.rb +1 -1
  151. data/lib/hexapdf/layout/list_box.rb +1 -1
  152. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  153. data/lib/hexapdf/layout/page_style.rb +1 -1
  154. data/lib/hexapdf/layout/style.rb +133 -22
  155. data/lib/hexapdf/layout/table_box.rb +86 -14
  156. data/lib/hexapdf/layout/text_box.rb +1 -1
  157. data/lib/hexapdf/layout/text_fragment.rb +13 -2
  158. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  159. data/lib/hexapdf/layout/text_shaper.rb +1 -1
  160. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  161. data/lib/hexapdf/layout.rb +1 -1
  162. data/lib/hexapdf/name_tree_node.rb +1 -1
  163. data/lib/hexapdf/number_tree_node.rb +1 -1
  164. data/lib/hexapdf/object.rb +4 -4
  165. data/lib/hexapdf/parser.rb +36 -7
  166. data/lib/hexapdf/pdf_array.rb +26 -4
  167. data/lib/hexapdf/rectangle.rb +1 -1
  168. data/lib/hexapdf/reference.rb +2 -2
  169. data/lib/hexapdf/revision.rb +7 -3
  170. data/lib/hexapdf/revisions.rb +1 -1
  171. data/lib/hexapdf/serializer.rb +8 -8
  172. data/lib/hexapdf/stream.rb +1 -1
  173. data/lib/hexapdf/task/dereference.rb +1 -1
  174. data/lib/hexapdf/task/merge_acro_form.rb +164 -0
  175. data/lib/hexapdf/task/optimize.rb +5 -5
  176. data/lib/hexapdf/task/pdfa.rb +1 -1
  177. data/lib/hexapdf/task.rb +2 -1
  178. data/lib/hexapdf/test_utils.rb +3 -2
  179. data/lib/hexapdf/tokenizer.rb +52 -44
  180. data/lib/hexapdf/type/acro_form/appearance_generator.rb +66 -13
  181. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  182. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  183. data/lib/hexapdf/type/acro_form/field.rb +6 -2
  184. data/lib/hexapdf/type/acro_form/form.rb +23 -32
  185. data/lib/hexapdf/type/acro_form/java_script_actions.rb +10 -3
  186. data/lib/hexapdf/type/acro_form/signature_field.rb +19 -8
  187. data/lib/hexapdf/type/acro_form/text_field.rb +10 -3
  188. data/lib/hexapdf/type/acro_form/variable_text_field.rb +13 -5
  189. data/lib/hexapdf/type/acro_form.rb +1 -1
  190. data/lib/hexapdf/type/action.rb +1 -1
  191. data/lib/hexapdf/type/actions/go_to.rb +2 -1
  192. data/lib/hexapdf/type/actions/go_to_r.rb +2 -1
  193. data/lib/hexapdf/type/actions/launch.rb +6 -2
  194. data/lib/hexapdf/type/actions/set_ocg_state.rb +1 -1
  195. data/lib/hexapdf/type/actions/uri.rb +1 -1
  196. data/lib/hexapdf/type/actions.rb +1 -1
  197. data/lib/hexapdf/type/annotation.rb +78 -3
  198. data/lib/hexapdf/type/annotations/appearance_generator.rb +426 -0
  199. data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
  200. data/lib/hexapdf/type/annotations/border_styling.rb +160 -0
  201. data/lib/hexapdf/type/annotations/circle.rb +65 -0
  202. data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
  203. data/lib/hexapdf/type/annotations/line.rb +334 -0
  204. data/lib/hexapdf/type/annotations/line_ending_styling.rb +208 -0
  205. data/lib/hexapdf/type/annotations/link.rb +1 -1
  206. data/lib/hexapdf/type/annotations/markup_annotation.rb +15 -3
  207. data/lib/hexapdf/type/annotations/polygon.rb +64 -0
  208. data/lib/hexapdf/type/annotations/polygon_polyline.rb +109 -0
  209. data/lib/hexapdf/type/annotations/polyline.rb +64 -0
  210. data/lib/hexapdf/type/annotations/square.rb +65 -0
  211. data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
  212. data/lib/hexapdf/type/annotations/text.rb +1 -1
  213. data/lib/hexapdf/type/annotations/widget.rb +56 -118
  214. data/lib/hexapdf/type/annotations.rb +13 -1
  215. data/lib/hexapdf/type/catalog.rb +5 -2
  216. data/lib/hexapdf/type/cid_font.rb +6 -3
  217. data/lib/hexapdf/type/cmap.rb +58 -0
  218. data/lib/hexapdf/type/embedded_file.rb +1 -1
  219. data/lib/hexapdf/type/file_specification.rb +18 -15
  220. data/lib/hexapdf/type/font.rb +1 -1
  221. data/lib/hexapdf/type/font_descriptor.rb +5 -4
  222. data/lib/hexapdf/type/font_simple.rb +4 -2
  223. data/lib/hexapdf/type/font_true_type.rb +3 -1
  224. data/lib/hexapdf/type/font_type0.rb +2 -2
  225. data/lib/hexapdf/type/font_type1.rb +19 -1
  226. data/lib/hexapdf/type/font_type3.rb +1 -2
  227. data/lib/hexapdf/type/form.rb +8 -5
  228. data/lib/hexapdf/type/graphics_state_parameter.rb +8 -5
  229. data/lib/hexapdf/type/icon_fit.rb +1 -1
  230. data/lib/hexapdf/type/image.rb +9 -5
  231. data/lib/hexapdf/type/info.rb +3 -3
  232. data/lib/hexapdf/type/mark_information.rb +3 -3
  233. data/lib/hexapdf/type/marked_content_reference.rb +59 -0
  234. data/lib/hexapdf/type/measure.rb +57 -0
  235. data/lib/hexapdf/type/metadata.rb +1 -1
  236. data/lib/hexapdf/type/names.rb +1 -1
  237. data/lib/hexapdf/type/namespace.rb +57 -0
  238. data/lib/hexapdf/type/object_reference.rb +57 -0
  239. data/lib/hexapdf/type/object_stream.rb +1 -1
  240. data/lib/hexapdf/type/optional_content_configuration.rb +2 -2
  241. data/lib/hexapdf/type/optional_content_group.rb +1 -1
  242. data/lib/hexapdf/type/optional_content_membership.rb +2 -2
  243. data/lib/hexapdf/type/optional_content_properties.rb +1 -1
  244. data/lib/hexapdf/type/outline.rb +1 -1
  245. data/lib/hexapdf/type/outline_item.rb +1 -1
  246. data/lib/hexapdf/type/output_intent.rb +1 -1
  247. data/lib/hexapdf/type/page.rb +6 -4
  248. data/lib/hexapdf/type/page_label.rb +1 -1
  249. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  250. data/lib/hexapdf/type/resources.rb +13 -9
  251. data/lib/hexapdf/type/struct_elem.rb +72 -0
  252. data/lib/hexapdf/type/struct_tree_root.rb +64 -0
  253. data/lib/hexapdf/type/trailer.rb +1 -1
  254. data/lib/hexapdf/type/viewer_preferences.rb +5 -4
  255. data/lib/hexapdf/type/xref_stream.rb +1 -1
  256. data/lib/hexapdf/type.rb +8 -1
  257. data/lib/hexapdf/utils/bit_field.rb +1 -1
  258. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  259. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  260. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  261. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  262. data/lib/hexapdf/utils/object_hash.rb +1 -1
  263. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  264. data/lib/hexapdf/utils/sorted_tree_node.rb +17 -4
  265. data/lib/hexapdf/utils.rb +1 -1
  266. data/lib/hexapdf/version.rb +2 -2
  267. data/lib/hexapdf/writer.rb +3 -2
  268. data/lib/hexapdf/xref_section.rb +25 -6
  269. data/lib/hexapdf.rb +1 -1
  270. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf +43 -0
  271. data/test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf +44 -0
  272. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf +43 -0
  273. data/test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf +0 -0
  274. data/test/hexapdf/common_tokenizer_tests.rb +7 -7
  275. data/test/hexapdf/content/test_graphics_state.rb +2 -3
  276. data/test/hexapdf/content/test_operator.rb +4 -5
  277. data/test/hexapdf/digital_signature/common.rb +6 -1
  278. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +6 -1
  279. data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +12 -0
  280. data/test/hexapdf/digital_signature/test_cms_handler.rb +25 -15
  281. data/test/hexapdf/digital_signature/test_handler.rb +2 -3
  282. data/test/hexapdf/digital_signature/test_pkcs1_handler.rb +1 -2
  283. data/test/hexapdf/digital_signature/test_signature.rb +7 -0
  284. data/test/hexapdf/digital_signature/test_signatures.rb +12 -7
  285. data/test/hexapdf/document/test_annotations.rb +75 -0
  286. data/test/hexapdf/document/test_layout.rb +38 -10
  287. data/test/hexapdf/document/test_metadata.rb +13 -1
  288. data/test/hexapdf/encryption/common.rb +1 -1
  289. data/test/hexapdf/encryption/test_aes.rb +1 -1
  290. data/test/hexapdf/encryption/test_arc4.rb +2 -2
  291. data/test/hexapdf/encryption/test_security_handler.rb +8 -6
  292. data/test/hexapdf/encryption/test_standard_security_handler.rb +7 -3
  293. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  294. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  295. data/test/hexapdf/filter/test_flate_decode.rb +2 -3
  296. data/test/hexapdf/font/cmap/test_writer.rb +73 -16
  297. data/test/hexapdf/font/encoding/test_base.rb +20 -0
  298. data/test/hexapdf/font/encoding/test_glyph_list.rb +1 -1
  299. data/test/hexapdf/font/test_true_type_wrapper.rb +31 -5
  300. data/test/hexapdf/font/test_type1_wrapper.rb +8 -1
  301. data/test/hexapdf/font/true_type/test_table.rb +12 -0
  302. data/test/hexapdf/layout/test_box.rb +8 -2
  303. data/test/hexapdf/layout/test_container_box.rb +34 -6
  304. data/test/hexapdf/layout/test_list_box.rb +7 -7
  305. data/test/hexapdf/layout/test_page_style.rb +1 -1
  306. data/test/hexapdf/layout/test_style.rb +46 -12
  307. data/test/hexapdf/layout/test_table_box.rb +66 -16
  308. data/test/hexapdf/layout/test_text_box.rb +0 -6
  309. data/test/hexapdf/layout/test_text_fragment.rb +3 -3
  310. data/test/hexapdf/layout/test_text_layouter.rb +4 -2
  311. data/test/hexapdf/task/test_merge_acro_form.rb +104 -0
  312. data/test/hexapdf/task/test_optimize.rb +3 -1
  313. data/test/hexapdf/test_composer.rb +15 -0
  314. data/test/hexapdf/test_dictionary.rb +15 -0
  315. data/test/hexapdf/test_dictionary_fields.rb +1 -0
  316. data/test/hexapdf/test_document.rb +26 -8
  317. data/test/hexapdf/test_filter.rb +1 -1
  318. data/test/hexapdf/test_importer.rb +7 -0
  319. data/test/hexapdf/test_object.rb +1 -1
  320. data/test/hexapdf/test_parser.rb +87 -18
  321. data/test/hexapdf/test_pdf_array.rb +36 -3
  322. data/test/hexapdf/test_revision.rb +27 -6
  323. data/test/hexapdf/test_revisions.rb +1 -1
  324. data/test/hexapdf/test_serializer.rb +4 -4
  325. data/test/hexapdf/test_stream.rb +1 -2
  326. data/test/hexapdf/test_tokenizer.rb +1 -1
  327. data/test/hexapdf/test_writer.rb +22 -8
  328. data/test/hexapdf/test_xref_section.rb +15 -0
  329. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +118 -26
  330. data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
  331. data/test/hexapdf/type/acro_form/test_field.rb +10 -0
  332. data/test/hexapdf/type/acro_form/test_form.rb +32 -9
  333. data/test/hexapdf/type/acro_form/test_java_script_actions.rb +21 -0
  334. data/test/hexapdf/type/acro_form/test_signature_field.rb +3 -1
  335. data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
  336. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +14 -1
  337. data/test/hexapdf/type/actions/test_launch.rb +6 -2
  338. data/test/hexapdf/type/annotations/test_appearance_generator.rb +608 -0
  339. data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
  340. data/test/hexapdf/type/annotations/test_border_styling.rb +114 -0
  341. data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
  342. data/test/hexapdf/type/annotations/test_line.rb +144 -0
  343. data/test/hexapdf/type/annotations/test_line_ending_styling.rb +42 -0
  344. data/test/hexapdf/type/annotations/test_polygon_polyline.rb +29 -0
  345. data/test/hexapdf/type/annotations/test_widget.rb +47 -81
  346. data/test/hexapdf/type/test_annotation.rb +58 -0
  347. data/test/hexapdf/type/test_font_type1.rb +20 -1
  348. data/test/hexapdf/type/test_form.rb +7 -1
  349. data/test/hexapdf/type/test_image.rb +1 -1
  350. data/test/hexapdf/type/test_page.rb +7 -1
  351. data/test/hexapdf/type/test_page_tree_node.rb +2 -2
  352. data/test/hexapdf/type/test_resources.rb +3 -1
  353. data/test/hexapdf/utils/test_sorted_tree_node.rb +18 -7
  354. data/test/test_helper.rb +7 -0
  355. metadata +69 -9
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -43,6 +43,8 @@ require 'hexapdf/reference'
43
43
  require 'hexapdf/object'
44
44
  require 'hexapdf/pdf_array'
45
45
  require 'hexapdf/stream'
46
+ require 'hexapdf/name_tree_node'
47
+ require 'hexapdf/number_tree_node'
46
48
  require 'hexapdf/revisions'
47
49
  require 'hexapdf/type'
48
50
  require 'hexapdf/task'
@@ -121,6 +123,7 @@ module HexaPDF
121
123
  autoload(:Destinations, 'hexapdf/document/destinations')
122
124
  autoload(:Layout, 'hexapdf/document/layout')
123
125
  autoload(:Metadata, 'hexapdf/document/metadata')
126
+ autoload(:Annotations, 'hexapdf/document/annotations')
124
127
 
125
128
  # :call-seq:
126
129
  # Document.open(filename, **docargs) -> doc
@@ -537,6 +540,12 @@ module HexaPDF
537
540
  @destinations ||= Destinations.new(self)
538
541
  end
539
542
 
543
+ # Returns the Annotations object that provides convenience methods for working with annotation
544
+ # objects.
545
+ def annotations
546
+ @annotations ||= Annotations.new(self)
547
+ end
548
+
540
549
  # Returns the Layout object that provides convenience methods for working with the
541
550
  # HexaPDF::Layout classes for document layout.
542
551
  def layout
@@ -724,14 +733,25 @@ module HexaPDF
724
733
  end
725
734
 
726
735
  # :call-seq:
727
- # doc.write(filename, incremental: false, validate: true, update_fields: true, optimize: false)
728
- # doc.write(io, incremental: false, validate: true, update_fields: true, optimize: false)
736
+ # doc.write(filename, incremental: false, validate: true, update_fields: true, optimize: false, compact: true) -> [start_xref, section]
737
+ # doc.write(io, incremental: false, validate: true, update_fields: true, optimize: false, compact: true) -> [start_xref, section]
729
738
  #
730
- # Writes the document to the given file (in case +io+ is a String) or IO stream.
739
+ # Writes the document to the given file (in case +io+ is a String) or IO stream. Returns the
740
+ # file position of the start of the last cross-reference section and the last XRefSection object
741
+ # written.
731
742
  #
732
743
  # Before the document is written, it is validated using #validate and an error is raised if the
733
744
  # document is not valid. However, this step can be skipped if needed.
734
745
  #
746
+ # The method dispatches two messages:
747
+ #
748
+ # :complete_objects::
749
+ # This message is dispatched before anything is done and should be used to finalize objects.
750
+ #
751
+ # :before_write::
752
+ # This message is dispatched directly before the document gets serialized and allows, for
753
+ # example, overriding automatic HexaPDF changes (e.g. forcefully setting a document version).
754
+ #
735
755
  # Options:
736
756
  #
737
757
  # incremental::
@@ -751,7 +771,20 @@ module HexaPDF
751
771
  # optimize::
752
772
  # Optimize the file size by using object and cross-reference streams. This will raise the PDF
753
773
  # version to at least 1.5.
754
- def write(file_or_io, incremental: false, validate: true, update_fields: true, optimize: false)
774
+ #
775
+ # compact::
776
+ # Compact the document by reducing it to a single revision and removing null and unused
777
+ # objects.
778
+ #
779
+ # The initial revision of a document has to contain objects with continuous numbering. If some
780
+ # object numbers refer to free entries, other PDF libraries/viewers might not work
781
+ # correctly. So continuous object numbers are assigned to stay compliant with the
782
+ # specification.
783
+ #
784
+ # Only change this argument to +false+ if you run the optimization task with 'compact: true'
785
+ # beforehand or if you know exactly what you do and what not compacting implies.
786
+ def write(file_or_io, incremental: false, validate: true, update_fields: true, optimize: false,
787
+ compact: true)
755
788
  if update_fields
756
789
  trailer.update_id
757
790
  if @metadata
@@ -770,10 +803,11 @@ module HexaPDF
770
803
  end
771
804
  end
772
805
 
773
- if optimize
774
- task(:optimize, object_streams: :generate)
775
- self.version = '1.5' if version < '1.5'
776
- end
806
+ optimize_opts = {}
807
+ optimize_opts[:object_streams] = :generate if optimize
808
+ optimize_opts[:compact] = true if compact && !incremental
809
+ task(:optimize, **optimize_opts) unless optimize_opts.empty?
810
+ self.version = '1.5' if version < '1.5' if optimize
777
811
 
778
812
  dispatch_message(:before_write)
779
813
 
@@ -784,6 +818,15 @@ module HexaPDF
784
818
  end
785
819
  end
786
820
 
821
+ # Writes the document to a string and returns the string.
822
+ #
823
+ # See #write for further information and details on the available arguments.
824
+ def write_to_string(**args)
825
+ io = StringIO.new(''.b)
826
+ write(io, **args)
827
+ io.string
828
+ end
829
+
787
830
  def inspect #:nodoc:
788
831
  "<#{self.class.name}:#{object_id}>"
789
832
  end
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -66,14 +66,14 @@ module HexaPDF
66
66
  # Encrypts the given +data+ with the +key+.
67
67
  #
68
68
  # See: PDF2.0 s7.6.3
69
- def encrypt(key, data)
69
+ def encrypt(key, data, &_block)
70
70
  new(key).process(data)
71
71
  end
72
72
  alias decrypt encrypt
73
73
 
74
74
  # Returns a Fiber object that encrypts the data from the given source fiber with the
75
75
  # +key+.
76
- def encryption_fiber(key, source)
76
+ def encryption_fiber(key, source, &_block)
77
77
  Fiber.new do
78
78
  algorithm = new(key)
79
79
  while source.alive? && (data = source.resume)
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -363,7 +363,9 @@ module HexaPDF
363
363
  raise(HexaPDF::UnsupportedEncryptionError,
364
364
  "Invalid key length #{key_length} specified")
365
365
  end
366
- dict[:Length] = key_length if dict[:V] == 4 || dict[:V] == 2
366
+ # /Length should only be set for V=2 as per the spec. However, software like Adobe Reader
367
+ # fails if this is not set for V=5 or V=4.
368
+ dict[:Length] = key_length if dict[:V] == 5 || dict[:V] == 4 || dict[:V] == 2
367
369
 
368
370
  if ![:aes, :arc4].include?(algorithm)
369
371
  raise(HexaPDF::UnsupportedEncryptionError,
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -106,6 +106,10 @@ module HexaPDF
106
106
  # password is supplied. To open such an encrypted PDF file, the +decryption_opts+ provided to
107
107
  # HexaPDF::Document.new needs to contain a :password key with the password.
108
108
  #
109
+ # **Note**: While HexaPDF supports reading files encrypted with revision 5, it doesn't support
110
+ # writing such files. This is no problem in practice since revision 5 was an inofficial Adobe
111
+ # extension to PDF 1.7 and revision 6 specified in PDF 2.0 is practically the same.
112
+ #
109
113
  # See: PDF2.0 s7.6.4
110
114
  class StandardSecurityHandler < SecurityHandler
111
115
 
@@ -321,8 +325,13 @@ module HexaPDF
321
325
  options.user_password = prepare_password(options.user_password)
322
326
  options.owner_password = prepare_password(options.owner_password)
323
327
 
324
- dict[:O] = compute_o_field(options.owner_password, options.user_password)
325
- dict[:U] = compute_u_field(options.user_password)
328
+ if dict[:R] <= 4
329
+ dict[:O] = compute_o_field(options.owner_password, options.user_password)
330
+ dict[:U] = compute_u_field(options.user_password)
331
+ else
332
+ dict[:U] = compute_u_field(options.user_password)
333
+ dict[:O] = compute_o_field(options.owner_password, options.user_password)
334
+ end
326
335
 
327
336
  if dict[:R] <= 4
328
337
  encryption_key = compute_user_encryption_key(options.user_password)
@@ -340,13 +349,13 @@ module HexaPDF
340
349
  # Uses the given password (or the default password if none given) to retrieve the encryption
341
350
  # key.
342
351
  #
343
- # If the optional +check_permissions+ argument is +true+, the permissions for files
344
- # encrypted with revision 6 are checked. Otherwise, permission changes are ignored.
352
+ # If the optional +check_permissions+ argument is +true+, the permissions for files encrypted
353
+ # with revision 5 or 6 are checked. Otherwise, permission changes are ignored.
345
354
  def prepare_decryption(password: '', check_permissions: true)
346
355
  if dict[:Filter] != :Standard
347
356
  raise(HexaPDF::UnsupportedEncryptionError,
348
357
  "Invalid /Filter value #{dict[:Filter]} for standard security handler")
349
- elsif ![2, 3, 4, 6].include?(dict[:R])
358
+ elsif ![2, 3, 4, 5, 6].include?(dict[:R])
350
359
  raise(HexaPDF::UnsupportedEncryptionError,
351
360
  "Invalid /R value #{dict[:R]} for standard security handler")
352
361
  elsif dict[:R] <= 4 && !document.trailer[:ID].kind_of?(PDFArray)
@@ -369,7 +378,7 @@ module HexaPDF
369
378
  raise HexaPDF::EncryptionError, "Invalid password specified"
370
379
  end
371
380
 
372
- check_perms_field(encryption_key) if check_permissions && dict[:R] == 6
381
+ check_perms_field(encryption_key) if check_permissions && dict[:R] >= 5
373
382
 
374
383
  encryption_key
375
384
  end
@@ -396,8 +405,8 @@ module HexaPDF
396
405
  # For revisions <= 4 this is the *only* way for generating the encryption key needed to
397
406
  # encrypt or decrypt a file.
398
407
  #
399
- # For revision 6 the file encryption key is a string of random bytes that has been encrypted
400
- # with the user password. If the password is the owner password,
408
+ # For revision 5 and 6 the file encryption key is a string of random bytes that has been
409
+ # encrypted with the user password. If the password is the owner password,
401
410
  # #compute_owner_encryption_key has to be used instead.
402
411
  #
403
412
  # See: PDF2.0 s7.6.4.3.2 (algorithm 2), PDF2.0 s7.6.4.3.3 (algorithm 2.A (a)-(b),(e))
@@ -416,7 +425,7 @@ module HexaPDF
416
425
  end
417
426
 
418
427
  data[0, n]
419
- elsif dict[:R] == 6
428
+ elsif dict[:R] <= 6
420
429
  key = compute_hash(password, dict[:U][40, 8])
421
430
  aes_algorithm.new(key, "\0" * 16, :decrypt).process(dict[:UE])
422
431
  end
@@ -427,15 +436,15 @@ module HexaPDF
427
436
  # For revisions <= 4 this is done by first retrieving the user password through the use of
428
437
  # the owner password and then using the #compute_user_encryption_key method.
429
438
  #
430
- # For revision 6 the file encryption key is a string of random bytes that has been encrypted
431
- # with the owner password. If the password is the user password, #compute_user_encryption_key
432
- # has to be used.
439
+ # For revisions 5 and 6 the file encryption key is a string of random bytes that has been
440
+ # encrypted with the owner password. If the password is the user password,
441
+ # #compute_user_encryption_key has to be used.
433
442
  #
434
443
  # See: PDF2.0 s7.6.4.3.2 (algorithm 2.A (a)-(d))
435
444
  def compute_owner_encryption_key(password)
436
445
  if dict[:R] <= 4
437
446
  compute_user_encryption_key(user_password_from_owner_password(password))
438
- elsif dict[:R] == 6
447
+ elsif dict[:R] <= 6
439
448
  key = compute_hash(password, dict[:O][40, 8], dict[:U])
440
449
  aes_algorithm.new(key, "\0" * 16, :decrypt).process(dict[:OE])
441
450
  end
@@ -447,7 +456,7 @@ module HexaPDF
447
456
  # the owner password. For revision 6 the /O value is a hash computed from the password and
448
457
  # the /U value with added validation and key salts.
449
458
  #
450
- # *Attention*: If revision 6 is used, the /U value has to be computed and set before this
459
+ # *Attention*: If revision 5 or 6 is used, the /U value has to be computed and set before this
451
460
  # method is used, otherwise the return value is incorrect!
452
461
  #
453
462
  # See: PDF2.0 s7.6.4.4.2 (algorithm 3), PDF2.0 s7.6.4.4.8 (algorithm 9 (a))
@@ -465,14 +474,14 @@ module HexaPDF
465
474
  end
466
475
 
467
476
  data
468
- elsif dict[:R] == 6
477
+ elsif dict[:R] <= 6
469
478
  validation_salt = random_bytes(8)
470
479
  key_salt = random_bytes(8)
471
480
  compute_hash(owner_password, validation_salt, dict[:U]) << validation_salt << key_salt
472
481
  end
473
482
  end
474
483
 
475
- # Computes the encryption dictionary's /OE (owner encryption key) value (for revision 6
484
+ # Computes the encryption dictionary's /OE (owner encryption key) value (for revisions 5 and 6
476
485
  # only).
477
486
  #
478
487
  # Short explanation: Encrypts the file encryption key with a key based on the password and
@@ -487,7 +496,7 @@ module HexaPDF
487
496
  # Computes the encryption dictionary's /U (user password) value.
488
497
  #
489
498
  # Short explanation: For revisions <= 4, the password padding string is encrypted with a key
490
- # based on the user password. For revision 6 the /U value is a hash computed from the
499
+ # based on the user password. For revisions 5 and 6 the /U value is a hash computed from the
491
500
  # password with added validation and key salts.
492
501
  #
493
502
  # See: PDF2.0 s7.6.4.4.3 (algorithm 4 for R=2), PDF s7.6.4.4.4 (algorithm 5 for R=3 and R=4)
@@ -502,14 +511,14 @@ module HexaPDF
502
511
  data = arc4_algorithm.encrypt(key, data)
503
512
  19.times {|i| data = arc4_algorithm.encrypt(xor_key(key, i + 1), data) }
504
513
  data << "hexapdfhexapdfhe"
505
- elsif dict[:R] == 6
514
+ elsif dict[:R] <= 6
506
515
  validation_salt = random_bytes(8)
507
516
  key_salt = random_bytes(8)
508
517
  compute_hash(password, validation_salt) << validation_salt << key_salt
509
518
  end
510
519
  end
511
520
 
512
- # Computes the encryption dictionary's /UE (user encryption key) value (for revision 6
521
+ # Computes the encryption dictionary's /UE (user encryption key) value (for revision 5 and 6
513
522
  # only).
514
523
  #
515
524
  # Short explanation: Encrypts the file encryption key with a key based on the password and
@@ -521,7 +530,8 @@ module HexaPDF
521
530
  aes_algorithm.new(key, "\0" * 16, :encrypt).process(file_encryption_key)
522
531
  end
523
532
 
524
- # Computes the encryption dictionary's /Perms (permissions) value (for revision 6 only).
533
+ # Computes the encryption dictionary's /Perms (permissions) value (for revisions 5 and 6
534
+ # only).
525
535
  #
526
536
  # Uses /P and /EncryptMetadata values, so these have to be set beforehand.
527
537
  #
@@ -543,7 +553,7 @@ module HexaPDF
543
553
  compute_u_field(password) == dict[:U]
544
554
  elsif dict[:R] <= 4
545
555
  compute_u_field(password)[0, 16] == dict[:U][0, 16]
546
- elsif dict[:R] == 6
556
+ elsif dict[:R] <= 6
547
557
  compute_hash(password, dict[:U][32, 8]) == dict[:U][0, 32]
548
558
  end
549
559
  end
@@ -554,14 +564,14 @@ module HexaPDF
554
564
  def owner_password_valid?(password)
555
565
  if dict[:R] <= 4
556
566
  user_password_valid?(user_password_from_owner_password(password))
557
- elsif dict[:R] == 6
567
+ elsif dict[:R] <= 6
558
568
  compute_hash(password, dict[:O][32, 8], dict[:U]) == dict[:O][0, 32]
559
569
  end
560
570
  end
561
571
 
562
572
  # Checks if the decrypted /Perms entry matches the /P and /EncryptMetadata entries.
563
573
  #
564
- # This method can only be used for revision 6.
574
+ # This method can only be used for revisions 5 and 6.
565
575
  #
566
576
  # See: PDF2.0 s7.6.4.4.12 (algorithm 13)
567
577
  def check_perms_field(encryption_key)
@@ -596,17 +606,18 @@ module HexaPDF
596
606
  end
597
607
 
598
608
  # Computes a hash that is used extensively for all operations in security handlers of
599
- # revision 6.
609
+ # revision 5 and 6.
600
610
  #
601
611
  # Note: The original input (as defined by the spec) is calculated as
602
612
  # "#{password}#{salt}#{user_key}" where +user_key+ has to be empty when doing operations
603
613
  # with the user password.
604
614
  #
605
- # See: PDF2.0 s7.6.4.3.4 (algorithm 2.B)
615
+ # See: PDF2.0 s7.6.4.3.4 (algorithm 2.B) and ADB Extension Level 3 s3.5.2
606
616
  def compute_hash(password, salt, user_key = '')
607
617
  k = Digest::SHA256.digest("#{password}#{salt}#{user_key}")
608
- e = ''
618
+ return k if dict[:R] == 5
609
619
 
620
+ e = ''
610
621
  i = 0
611
622
  while i < 64 || e.getbyte(-1) > i - 32
612
623
  k1 = "#{password}#{k}#{user_key}" * 64
@@ -627,7 +638,7 @@ module HexaPDF
627
638
  # * For revisions <= 4, the password is converted into ISO-8859-1 encoding, padded with
628
639
  # PASSWORD_PADDING and truncated to a maximum of 32 bytes.
629
640
  #
630
- # * For revision 6 the password is converted into UTF-8 encoding that is normalized
641
+ # * For revision 5 and 6 the password is converted into UTF-8 encoding that is normalized
631
642
  # according to the PDF2.0 specification.
632
643
  #
633
644
  # See: PDF2.0 s7.6.4.3.2 (algorithm 2 step a)),
@@ -636,7 +647,7 @@ module HexaPDF
636
647
  if dict[:R] <= 4
637
648
  password.to_s[0, 32].encode(Encoding::ISO_8859_1).force_encoding(Encoding::BINARY).
638
649
  ljust(32, PASSWORD_PADDING)
639
- elsif dict[:R] == 6
650
+ elsif dict[:R] <= 6
640
651
  password.to_s.encode(Encoding::UTF_8).force_encoding(Encoding::BINARY)[0, 127]
641
652
  end
642
653
  rescue Encoding::UndefinedConversionError => e
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
data/lib/hexapdf/error.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -94,9 +94,17 @@ module HexaPDF
94
94
  end
95
95
 
96
96
  def message # :nodoc:
97
- "No glyph for #{glyph.str.inspect} in font '#{glyph.font_wrapper.wrapped_font.full_name}' " \
98
- "found. \n\n" \
99
- "Use the configuration option 'font.on_missing_glyph' to customize missing glyph handling."
97
+ str = "No glyph for #{glyph.str.inspect} in font '#{glyph.font_wrapper.wrapped_font.full_name}' " \
98
+ "found. \n\n"
99
+ str << if glyph.font_wrapper.font_type == :Type1
100
+ "The used Type1 font only contains a very limited number of glyphs. TrueType " \
101
+ "fonts usually provide a much wider array of glyphs. Use the configuration option " \
102
+ "'font.map' to register appropriate font files. Also have a look at the " \
103
+ "'font.default' and 'font.fallback' options. "
104
+ else
105
+ "Maybe register another #{glyph.font_wrapper.font_type} font that contains the " \
106
+ "needed glyph and use it as fallback via the configuration option 'font.fallback'."
107
+ end
100
108
  end
101
109
 
102
110
  end
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2024 Thomas Leitner
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as