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
@@ -53,8 +53,8 @@ module HexaPDF
53
53
  # == Usage
54
54
  #
55
55
  # It is necessary to provide at least the URL of the timestamp authority server (TSA) via
56
- # #tsa_url, everything else is optional and uses default values. The TSA server must not use
57
- # authentication to be usable.
56
+ # #tsa_url, everything else is optional and uses default values. The TSA server can optionally
57
+ # use HTTP basic authentication.
58
58
  #
59
59
  # Example:
60
60
  #
@@ -66,6 +66,18 @@ module HexaPDF
66
66
  # This value is required.
67
67
  attr_accessor :tsa_url
68
68
 
69
+ # The username for basic authentication to the TSA server.
70
+ #
71
+ # If the username is not set, no basic authentication is done.
72
+ #
73
+ # See: #tsa_password
74
+ attr_accessor :tsa_username
75
+
76
+ # The password for basic authentication to the TSA server.
77
+ #
78
+ # See: #tsa_username
79
+ attr_accessor :tsa_password
80
+
69
81
  # The hash algorithm to use for timestamping. Defaults to SHA512.
70
82
  attr_accessor :tsa_hash_algorithm
71
83
 
@@ -127,8 +139,14 @@ module HexaPDF
127
139
  req.message_imprint = digest.digest
128
140
  req.policy_id = tsa_policy_id if tsa_policy_id
129
141
 
130
- http_response = Net::HTTP.post(URI(tsa_url), req.to_der,
131
- 'content-type' => 'application/timestamp-query')
142
+ url = URI(tsa_url)
143
+ http_request = Net::HTTP::Post.new(url, 'Content-Type' => 'application/timestamp-query')
144
+ http_request.body = req.to_der
145
+ http_request.basic_auth(tsa_username, tsa_password) if tsa_username
146
+ http_response = Net::HTTP.start(url.hostname, url.port, use_ssl: (url.scheme == 'https')) do |http|
147
+ http.request(http_request)
148
+ end
149
+
132
150
  if http_response.kind_of?(Net::HTTPOK)
133
151
  response = OpenSSL::Timestamp::Response.new(http_response.body)
134
152
  if response.status == 0
@@ -136,6 +154,8 @@ module HexaPDF
136
154
  else
137
155
  raise HexaPDF::Error, "Timestamp token could not be created: #{response.failure_info}"
138
156
  end
157
+ elsif http_response.kind_of?(Net::HTTPUnauthorized)
158
+ raise HexaPDF::Error, "Basic authentication to the server failed: #{http_response.body}"
139
159
  else
140
160
  raise HexaPDF::Error, "Invalid TSA server response: #{http_response.body}"
141
161
  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
@@ -0,0 +1,220 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2025 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/dictionary'
38
+ require 'hexapdf/error'
39
+
40
+ module HexaPDF
41
+ class Document
42
+
43
+ # This class provides methods for creating and managing the annotations of a PDF file.
44
+ #
45
+ # An annotation is an object that can be added to a certain location on a page, provides a
46
+ # visual appearance and allows for interaction with the user via keyboard and mouse.
47
+ #
48
+ # == Usage
49
+ #
50
+ # To create an annotation either call the general #create method or a specific creation method
51
+ # for an annotation type. After the annotation has been created customize it using the
52
+ # convenience methods on the annotation object. The last step should be the call to
53
+ # +regenerate_appearance+ so that the appearance is generated.
54
+ #
55
+ # See: PDF2.0 s12.5
56
+ class Annotations
57
+
58
+ include Enumerable
59
+
60
+ # Creates a new Annotations object for the given PDF document.
61
+ def initialize(document)
62
+ @document = document
63
+ end
64
+
65
+ # :call-seq:
66
+ # annotations.create(type, page, **options) -> annotation
67
+ #
68
+ # Creates a new annotation object with the given +type+ and +page+ by calling the respective
69
+ # +create_type+ method.
70
+ #
71
+ # The +options+ are passed on the specific annotation creation method.
72
+ def create(type, page, *args, **options)
73
+ method_name = "create_#{type}"
74
+ unless respond_to?(method_name)
75
+ raise ArgumentError, "Invalid type specified"
76
+ end
77
+ send("create_#{type}", page, *args, **options)
78
+ end
79
+
80
+ # :call-seq:
81
+ # annotations.create_line(page, start_point:, end_point:) -> annotation
82
+ #
83
+ # Creates a line annotation from +start_point+ to +end_point+ on the given page and returns
84
+ # it.
85
+ #
86
+ # The line uses a black color and a width of 1pt. It can be further styled using the
87
+ # convenience methods on the returned annotation object.
88
+ #
89
+ # Example:
90
+ #
91
+ # doc.annotations.create_line(doc.pages[0], start_point: [100, 100], end_point: [130, 180]).
92
+ # border_style(color: "blue", width: 2).
93
+ # leader_line_length(10).
94
+ # regenerate_appearance
95
+ #
96
+ # See: Type::Annotations::Line
97
+ def create_line(page, start_point:, end_point:)
98
+ create_and_add_to_page(:Line, page).
99
+ line(*start_point, *end_point).
100
+ border_style(color: 0, width: 1)
101
+ end
102
+
103
+ # :call-seq:
104
+ # annotations.create_rectangle(page, x, y, width, height) -> annotation
105
+ #
106
+ # Creates a rectangle (called "square" in the PDF specification) annotation with the
107
+ # lower-left corner at (+x+, +y+) and the given +width+ and +height+.
108
+ #
109
+ # The rectangle uses a black stroke color, no interior color and a line width of 1pt by
110
+ # default. It can be further styled using the convenience methods on the returned annotation
111
+ # object.
112
+ #
113
+ # Example:
114
+ #
115
+ # #>pdf-small
116
+ # doc.annotations.create_rectangle(doc.pages[0], 20, 20, 20, 60).
117
+ # regenerate_appearance
118
+ #
119
+ # doc.annotations.create_rectangle(doc.pages[0], 60, 20, 20, 60).
120
+ # border_style(color: "hp-blue", width: 2).
121
+ # interior_color("hp-orange").
122
+ # regenerate_appearance
123
+ #
124
+ # See: Type::Annotations::Square
125
+ def create_rectangle(page, x, y, w, h)
126
+ annot = create_and_add_to_page(:Square, page)
127
+ annot[:Rect] = [x, y, x + w, y + h]
128
+ annot.border_style(color: 0, width: 1)
129
+ annot
130
+ end
131
+
132
+ # :call-seq:
133
+ # annotations.create_ellipse(page, cx, cy, a:, b:) -> annotation
134
+ #
135
+ # Creates an ellipse (called "circle" in the PDF specification) annotation with the center
136
+ # point at (+cx+, +cy+), the semi-major axis +a+ and the semi-minor axis +b+.
137
+ #
138
+ # The ellipse uses a black stroke color, no interior color and a line width of 1pt by
139
+ # default. It can be further styled using the convenience methods on the returned annotation
140
+ # object.
141
+ #
142
+ # Example:
143
+ #
144
+ # #>pdf-small
145
+ # doc.annotations.create_ellipse(doc.pages[0], 30, 50, a: 15, b: 20).
146
+ # regenerate_appearance
147
+ #
148
+ # doc.annotations.create_ellipse(doc.pages[0], 70, 50, a: 15, b: 20).
149
+ # border_style(color: "hp-blue", width: 2).
150
+ # interior_color("hp-orange").
151
+ # regenerate_appearance
152
+ #
153
+ # See: Type::Annotations::Circle
154
+ def create_ellipse(page, x, y, a:, b:)
155
+ annot = create_and_add_to_page(:Circle, page)
156
+ annot[:Rect] = [x - a, y - b, x + a, y + b]
157
+ annot.border_style(color: 0, width: 1)
158
+ annot
159
+ end
160
+
161
+ # :call-seq:
162
+ # annotations.create_polyline(page, *points) -> annotation
163
+ #
164
+ # Creates a polyline annotation for the given +points+ (alternating horizontal and vertical
165
+ # coordinates) on the given page and returns it.
166
+ #
167
+ # The polyline uses a black color and a width of 1pt. It can be further styled using the
168
+ # convenience methods on the returned annotation object.
169
+ #
170
+ # Example:
171
+ #
172
+ # #>pdf-small
173
+ # doc.annotations.create_polyline(doc.pages[0], 20, 20, 30, 70, 80, 60, 40, 30).
174
+ # border_style(color: "hp-blue", width: 2, style: [3, 1]).
175
+ # regenerate_appearance
176
+ #
177
+ # See: Type::Annotations::Polyline
178
+ def create_polyline(page, *points)
179
+ create_and_add_to_page(:PolyLine, page).
180
+ vertices(*points).
181
+ border_style(color: 0, width: 1)
182
+ end
183
+
184
+ # :call-seq:
185
+ # annotations.create_polygon(page, *points) -> annotation
186
+ #
187
+ # Creates a polygon annotation for the given +points+ (alternating horizontal and vertical
188
+ # coordinates) on the given page and returns it.
189
+ #
190
+ # The polygon uses a black color and a width of 1pt for the border and no interior color. It
191
+ # can be further styled using the convenience methods on the returned annotation object.
192
+ #
193
+ # Example:
194
+ #
195
+ # #>pdf-small
196
+ # doc.annotations.create_polygon(doc.pages[0], 20, 20, 30, 70, 80, 60, 40, 30).
197
+ # border_style(color: "hp-blue", width: 2, style: [3, 1]).
198
+ # interior_color("hp-orange").
199
+ # regenerate_appearance
200
+ #
201
+ # See: Type::Annotations::Polygon
202
+ def create_polygon(page, *points)
203
+ create_and_add_to_page(:Polygon, page).
204
+ vertices(*points).
205
+ border_style(color: 0, width: 1)
206
+ end
207
+
208
+ private
209
+
210
+ # Returns the root of the destinations name tree.
211
+ def create_and_add_to_page(subtype, page)
212
+ annot = @document.add({Type: :Annot, Subtype: subtype})
213
+ (page[:Annots] ||= []) << annot
214
+ annot
215
+ end
216
+
217
+ end
218
+
219
+ end
220
+ 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
@@ -92,13 +92,23 @@ module HexaPDF
92
92
  #
93
93
  # style.font = ['Helvetica', variant: :bold]
94
94
  #
95
- # Helvetica in bold could also be set the conventional way:
95
+ # Helvetica in bold could also be set in the following ways:
96
96
  #
97
97
  # style.font = 'Helvetica bold'
98
+ # # or
99
+ # style.font_bold = true
100
+ # style.font = 'Helvetica'
101
+ #
102
+ # The font_bold and font_italic style properties are always taken into account. For example,
103
+ # if the font is set to 'Helvetica italic' and font_bold to +true+, the actual font would be
104
+ # the bold _and_ italic Helvetica font.
98
105
  #
99
106
  # However, using an array it is also possible to specify other options when setting a font,
100
107
  # like the :subset option.
101
108
  #
109
+ # * It is possible to resolve the font of a style object manually by using the #resolve_font
110
+ # method.
111
+ #
102
112
  class Layout
103
113
 
104
114
  # This class is used when a box can contain child boxes and the creation of such boxes should
@@ -218,6 +228,77 @@ module HexaPDF
218
228
  style
219
229
  end
220
230
 
231
+ # Returns +true+ if a style with the given +name+ exists, else +false+.
232
+ #
233
+ # Example:
234
+ #
235
+ # layout.style(:header, font: 'Helvetica')
236
+ # layout.style?(:header) # => true
237
+ # layout.style?(:paragraph) # => false
238
+ #
239
+ # See: #style
240
+ def style?(name)
241
+ @styles.key?(name)
242
+ end
243
+
244
+ FONT_BOLD_VARIANT_MAPPER = { #:nodoc:
245
+ nil => {true => :bold, false: :none},
246
+ none: {true => :bold, false: :none},
247
+ bold: {true => :bold, false: :none},
248
+ italic: {true => :bold_italic, false: :italic},
249
+ bold_italic: {true => :bold_italic, false: :italic},
250
+ }
251
+
252
+ FONT_ITALIC_VARIANT_MAPPER = { #:nodoc:
253
+ nil => {true => :italic, false: :none},
254
+ none: {true => :italic, false: :none},
255
+ italic: {true => :italic, false: :none},
256
+ bold: {true => :bold_italic, false: :bold},
257
+ bold_italic: {true => :bold_italic, false: :bold},
258
+ }
259
+
260
+ # Resolves the font object for the given +style+ and applies the result to it.
261
+ #
262
+ # The Layout::Style#font property is the only one without a default value but is needed for
263
+ # many operations. This method ensures that the +style+ has a valid font object for the font
264
+ # property by resolving the font name.
265
+ #
266
+ # The font object is resolved in the following way:
267
+ #
268
+ # * If the font property is not set, the font value of the :base style is used and if that is
269
+ # also not set, the 'font.default' configuration value is used.
270
+ #
271
+ # * Afterwards, if the font property is a valid font object, nothing needs to be done.
272
+ #
273
+ # * Otherwise, if the font property is a single font name or a [font name, options hash]
274
+ # array, it is resolved to a font object, also taking the font_bold and font_italic style
275
+ # properties into account.
276
+ #
277
+ # Example:
278
+ #
279
+ # style = layout.style(:header, font: 'Helvetica')
280
+ # style.font # => 'Helvetica'
281
+ # layout.resolve_font(style)
282
+ # style.font # => #<HexaPDF::Font::Type1Wrapper>
283
+ #
284
+ # See: The "Box Styles" section in Layout for more details.
285
+ def resolve_font(style)
286
+ unless style.font?
287
+ style.font(@styles[:base].font? && @styles[:base].font || @document.config['font.default'])
288
+ end
289
+ unless style.font.respond_to?(:pdf_object)
290
+ name, options = *style.font
291
+ options ||= {}
292
+ if style.font_bold?
293
+ options[:variant] = FONT_BOLD_VARIANT_MAPPER.dig(options[:variant], style.font_bold)
294
+ end
295
+ if style.font_italic?
296
+ options[:variant] = FONT_ITALIC_VARIANT_MAPPER.dig(options[:variant], style.font_italic)
297
+ end
298
+ style.font(@document.fonts.add(name, **options))
299
+ end
300
+ end
301
+
221
302
  # :call-seq:
222
303
  # layout.styles -> styles
223
304
  # layout.styles(**mapping) -> styles
@@ -286,8 +367,9 @@ module HexaPDF
286
367
  box_options[:children] = ChildrenCollector.collect(self, &block)
287
368
  end
288
369
  end
370
+ style = retrieve_style(style)
289
371
  box_class_for_name(name).new(width: width, height: height,
290
- style: retrieve_style(style), **box_options, &box_block)
372
+ style: style, **style.box_options, **box_options, &box_block)
291
373
  end
292
374
 
293
375
  # Creates an array of HexaPDF::Layout::TextFragment objects for the given +text+.
@@ -354,7 +436,7 @@ module HexaPDF
354
436
  box_style = (box_style ? retrieve_style(box_style) : style)
355
437
  box_class_for_name(:text).new(items: text_fragments(text, style: style),
356
438
  width: width, height: height, properties: properties,
357
- style: box_style)
439
+ style: box_style, **box_style.box_options)
358
440
  end
359
441
  alias text text_box
360
442
 
@@ -457,7 +539,8 @@ module HexaPDF
457
539
  end
458
540
  end
459
541
  box_class_for_name(:text).new(items: data, width: width, height: height,
460
- properties: properties, style: box_style)
542
+ properties: properties, style: box_style,
543
+ **box_style.box_options)
461
544
  end
462
545
  alias formatted_text formatted_text_box
463
546
 
@@ -479,7 +562,7 @@ module HexaPDF
479
562
  style = retrieve_style(style, style_properties)
480
563
  image = file.kind_of?(HexaPDF::Stream) ? file : @document.images.add(file)
481
564
  box_class_for_name(:image).new(image: image, width: width, height: height,
482
- properties: properties, style: style)
565
+ properties: properties, style: style, **style.box_options)
483
566
  end
484
567
  alias image image_box
485
568
 
@@ -533,9 +616,10 @@ module HexaPDF
533
616
  @argument_infos.each_with_object({}) do |arg_info, result|
534
617
  next unless arg_info.rows.include?(row) && arg_info.cols.include?(col)
535
618
  if arg_info.args[:cell]
536
- arg_info.args[:cell] = (result[:cell] || {}).merge(arg_info.args[:cell])
619
+ result.update(arg_info.args, cell: (result[:cell] || {}).merge(arg_info.args[:cell]))
620
+ else
621
+ result.update(arg_info.args)
537
622
  end
538
- result.update(arg_info.args)
539
623
  end
540
624
  end
541
625
 
@@ -608,7 +692,8 @@ module HexaPDF
608
692
  end
609
693
  box_class_for_name(:table).new(cells: cells, column_widths: column_widths, header: header,
610
694
  footer: footer, cell_style: cell_style, width: width,
611
- height: height, properties: properties, style: style)
695
+ height: height, properties: properties, style: style,
696
+ **style.box_options)
612
697
  end
613
698
  alias table table_box
614
699
 
@@ -685,13 +770,7 @@ module HexaPDF
685
770
  end
686
771
  style = HexaPDF::Layout::Style.create(@styles[style] || style || @styles[:base])
687
772
  style = style.dup.update(**properties) unless properties.nil? || properties.empty?
688
- unless style.font?
689
- style.font(@styles[:base].font? && @styles[:base].font || @document.config['font.default'])
690
- end
691
- unless style.font.respond_to?(:pdf_object)
692
- name, options = *style.font
693
- style.font(@document.fonts.add(name, **(options || {})))
694
- end
773
+ resolve_font(style)
695
774
  style
696
775
  end
697
776
 
@@ -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
@@ -430,8 +430,12 @@ module HexaPDF
430
430
  @metadata[ns_dc]['creator'] = info_dict[:Author] if info_dict.key?(:Author)
431
431
  @metadata[ns_dc]['description'] = info_dict[:Subject] if info_dict.key?(:Subject)
432
432
  @metadata[ns_xmp]['CreatorTool'] = info_dict[:Creator] if info_dict.key?(:Creator)
433
- @metadata[ns_xmp]['CreateDate'] = info_dict[:CreationDate] if info_dict.key?(:CreationDate)
434
- @metadata[ns_xmp]['ModifyDate'] = info_dict[:ModDate] if info_dict.key?(:ModDate)
433
+ if info_dict.key?(:CreationDate) && !info_dict[:CreationDate].kind_of?(String)
434
+ @metadata[ns_xmp]['CreateDate'] = info_dict[:CreationDate]
435
+ end
436
+ if info_dict.key?(:ModDate) && !info_dict[:ModDate].kind_of?(String)
437
+ @metadata[ns_xmp]['ModifyDate'] = info_dict[:ModDate] if info_dict.key?(:ModDate)
438
+ end
435
439
  @metadata[ns_pdf]['Keywords'] = info_dict[:Keywords] if info_dict.key?(:Keywords)
436
440
  @metadata[ns_pdf]['Producer'] = info_dict[:Producer] if info_dict.key?(:Producer)
437
441
  if info_dict.key?(:Trapped) && info_dict[:Trapped] != :Unknown
@@ -528,7 +532,10 @@ module HexaPDF
528
532
  # Formats the given date-time object (Time, Date, or DateTime) to be a valid XMP date-time
529
533
  # value.
530
534
  def xmp_date(date)
531
- date.strftime("%Y-%m-%dT%H:%M:%S%:z")
535
+ case date
536
+ when Time, Date, DateTime then date.strftime("%Y-%m-%dT%H:%M:%S%:z")
537
+ else ''
538
+ end
532
539
  end
533
540
 
534
541
  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