hexapdf 1.6.0 → 1.8.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 (287) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/LICENSE +1 -1
  4. data/examples/005-merging.rb +2 -1
  5. data/examples/032-acro_form_list_and_fill.rb +47 -0
  6. data/examples/033-text_extraction.rb +34 -0
  7. data/lib/hexapdf/cli/batch.rb +1 -1
  8. data/lib/hexapdf/cli/command.rb +1 -1
  9. data/lib/hexapdf/cli/debug_info.rb +1 -1
  10. data/lib/hexapdf/cli/files.rb +1 -1
  11. data/lib/hexapdf/cli/fonts.rb +6 -4
  12. data/lib/hexapdf/cli/form.rb +1 -1
  13. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  14. data/lib/hexapdf/cli/images.rb +17 -17
  15. data/lib/hexapdf/cli/info.rb +3 -1
  16. data/lib/hexapdf/cli/inspect.rb +1 -1
  17. data/lib/hexapdf/cli/merge.rb +14 -2
  18. data/lib/hexapdf/cli/modify.rb +1 -1
  19. data/lib/hexapdf/cli/optimize.rb +1 -1
  20. data/lib/hexapdf/cli/split.rb +1 -1
  21. data/lib/hexapdf/cli/usage.rb +1 -1
  22. data/lib/hexapdf/cli/watermark.rb +1 -1
  23. data/lib/hexapdf/cli.rb +1 -1
  24. data/lib/hexapdf/composer.rb +1 -1
  25. data/lib/hexapdf/configuration.rb +10 -1
  26. data/lib/hexapdf/content/canvas.rb +2 -2
  27. data/lib/hexapdf/content/canvas_composer.rb +1 -1
  28. data/lib/hexapdf/content/color_space.rb +1 -1
  29. data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
  30. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  31. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  32. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  33. data/lib/hexapdf/content/graphic_object.rb +1 -1
  34. data/lib/hexapdf/content/graphics_state.rb +1 -1
  35. data/lib/hexapdf/content/operator.rb +1 -1
  36. data/lib/hexapdf/content/parser.rb +1 -1
  37. data/lib/hexapdf/content/processor.rb +1 -1
  38. data/lib/hexapdf/content/smart_text_extractor.rb +311 -0
  39. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  40. data/lib/hexapdf/content.rb +3 -1
  41. data/lib/hexapdf/data_dir.rb +1 -1
  42. data/lib/hexapdf/dictionary.rb +1 -1
  43. data/lib/hexapdf/dictionary_fields.rb +1 -1
  44. data/lib/hexapdf/digital_signature/cms_handler.rb +1 -1
  45. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  46. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +1 -1
  47. data/lib/hexapdf/digital_signature/signature.rb +1 -1
  48. data/lib/hexapdf/digital_signature/signatures.rb +1 -1
  49. data/lib/hexapdf/digital_signature/signing/default_handler.rb +2 -16
  50. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +22 -9
  51. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +1 -1
  52. data/lib/hexapdf/digital_signature/signing.rb +1 -1
  53. data/lib/hexapdf/digital_signature/verification_result.rb +1 -1
  54. data/lib/hexapdf/digital_signature.rb +1 -1
  55. data/lib/hexapdf/document/annotations.rb +1 -1
  56. data/lib/hexapdf/document/destinations.rb +1 -1
  57. data/lib/hexapdf/document/files.rb +1 -1
  58. data/lib/hexapdf/document/fonts.rb +1 -1
  59. data/lib/hexapdf/document/images.rb +1 -1
  60. data/lib/hexapdf/document/layout.rb +1 -1
  61. data/lib/hexapdf/document/metadata.rb +1 -1
  62. data/lib/hexapdf/document/pages.rb +1 -1
  63. data/lib/hexapdf/document.rb +8 -4
  64. data/lib/hexapdf/encryption/aes.rb +1 -1
  65. data/lib/hexapdf/encryption/arc4.rb +1 -1
  66. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  67. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  68. data/lib/hexapdf/encryption/identity.rb +1 -1
  69. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  70. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  71. data/lib/hexapdf/encryption/security_handler.rb +1 -1
  72. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
  73. data/lib/hexapdf/encryption.rb +1 -1
  74. data/lib/hexapdf/error.rb +1 -1
  75. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  76. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  77. data/lib/hexapdf/filter/brotli_decode.rb +88 -0
  78. data/lib/hexapdf/filter/crypt.rb +1 -1
  79. data/lib/hexapdf/filter/encryption.rb +1 -1
  80. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  81. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  82. data/lib/hexapdf/filter/pass_through.rb +1 -1
  83. data/lib/hexapdf/filter/predictor.rb +1 -1
  84. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  85. data/lib/hexapdf/filter.rb +2 -1
  86. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  87. data/lib/hexapdf/font/cmap/writer.rb +1 -1
  88. data/lib/hexapdf/font/cmap.rb +1 -1
  89. data/lib/hexapdf/font/encoding/base.rb +1 -1
  90. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  91. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  92. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  93. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  94. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  95. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  96. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  97. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  98. data/lib/hexapdf/font/encoding.rb +1 -1
  99. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  100. data/lib/hexapdf/font/true_type/builder.rb +2 -2
  101. data/lib/hexapdf/font/true_type/font.rb +14 -1
  102. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  103. data/lib/hexapdf/font/true_type/subsetter.rb +11 -6
  104. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  105. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  106. data/lib/hexapdf/font/true_type/table/directory.rb +6 -1
  107. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  108. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  109. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  114. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  115. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  116. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  117. data/lib/hexapdf/font/true_type/table.rb +1 -1
  118. data/lib/hexapdf/font/true_type.rb +2 -1
  119. data/lib/hexapdf/font/true_type_wrapper.rb +3 -3
  120. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  121. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  122. data/lib/hexapdf/font/type1/font.rb +1 -1
  123. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  124. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  125. data/lib/hexapdf/font/type1.rb +1 -1
  126. data/lib/hexapdf/font/type1_wrapper.rb +1 -1
  127. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  128. data/lib/hexapdf/font_loader/from_file.rb +5 -1
  129. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  130. data/lib/hexapdf/font_loader/variant_from_name.rb +1 -1
  131. data/lib/hexapdf/font_loader.rb +1 -1
  132. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  133. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  134. data/lib/hexapdf/image_loader/png.rb +1 -1
  135. data/lib/hexapdf/image_loader.rb +1 -1
  136. data/lib/hexapdf/importer.rb +1 -1
  137. data/lib/hexapdf/layout/box.rb +1 -1
  138. data/lib/hexapdf/layout/box_fitter.rb +1 -1
  139. data/lib/hexapdf/layout/column_box.rb +1 -1
  140. data/lib/hexapdf/layout/container_box.rb +1 -1
  141. data/lib/hexapdf/layout/frame.rb +1 -1
  142. data/lib/hexapdf/layout/image_box.rb +1 -1
  143. data/lib/hexapdf/layout/inline_box.rb +1 -1
  144. data/lib/hexapdf/layout/line.rb +1 -1
  145. data/lib/hexapdf/layout/list_box.rb +1 -1
  146. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  147. data/lib/hexapdf/layout/page_style.rb +1 -1
  148. data/lib/hexapdf/layout/style.rb +7 -3
  149. data/lib/hexapdf/layout/table_box.rb +1 -1
  150. data/lib/hexapdf/layout/text_box.rb +1 -1
  151. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  152. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  153. data/lib/hexapdf/layout/text_shaper.rb +1 -1
  154. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  155. data/lib/hexapdf/layout.rb +1 -1
  156. data/lib/hexapdf/name_tree_node.rb +1 -1
  157. data/lib/hexapdf/number_tree_node.rb +1 -1
  158. data/lib/hexapdf/object.rb +1 -1
  159. data/lib/hexapdf/parser.rb +1 -1
  160. data/lib/hexapdf/pdf_array.rb +1 -1
  161. data/lib/hexapdf/rectangle.rb +1 -1
  162. data/lib/hexapdf/reference.rb +1 -1
  163. data/lib/hexapdf/revision.rb +1 -1
  164. data/lib/hexapdf/revisions.rb +1 -1
  165. data/lib/hexapdf/serializer.rb +3 -3
  166. data/lib/hexapdf/stream.rb +1 -1
  167. data/lib/hexapdf/task/dereference.rb +1 -1
  168. data/lib/hexapdf/task/import_pages.rb +185 -0
  169. data/lib/hexapdf/task/merge_acro_form.rb +1 -1
  170. data/lib/hexapdf/task/optimize.rb +1 -1
  171. data/lib/hexapdf/task/pdfa.rb +109 -2
  172. data/lib/hexapdf/task.rb +2 -1
  173. data/lib/hexapdf/test_utils.rb +1 -1
  174. data/lib/hexapdf/tokenizer.rb +1 -1
  175. data/lib/hexapdf/type/acro_form/appearance_generator.rb +1 -1
  176. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  177. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  178. data/lib/hexapdf/type/acro_form/field.rb +1 -1
  179. data/lib/hexapdf/type/acro_form/form.rb +5 -1
  180. data/lib/hexapdf/type/acro_form/java_script_actions.rb +1 -1
  181. data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
  182. data/lib/hexapdf/type/acro_form/text_field.rb +5 -3
  183. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  184. data/lib/hexapdf/type/acro_form.rb +1 -1
  185. data/lib/hexapdf/type/action.rb +1 -1
  186. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  187. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  188. data/lib/hexapdf/type/actions/launch.rb +1 -1
  189. data/lib/hexapdf/type/actions/set_ocg_state.rb +1 -1
  190. data/lib/hexapdf/type/actions/uri.rb +1 -1
  191. data/lib/hexapdf/type/actions.rb +1 -1
  192. data/lib/hexapdf/type/annotation.rb +1 -1
  193. data/lib/hexapdf/type/annotations/appearance_generator.rb +1 -1
  194. data/lib/hexapdf/type/annotations/border_effect.rb +1 -1
  195. data/lib/hexapdf/type/annotations/border_styling.rb +1 -1
  196. data/lib/hexapdf/type/annotations/circle.rb +1 -1
  197. data/lib/hexapdf/type/annotations/interior_color.rb +1 -1
  198. data/lib/hexapdf/type/annotations/line.rb +1 -1
  199. data/lib/hexapdf/type/annotations/line_ending_styling.rb +1 -1
  200. data/lib/hexapdf/type/annotations/link.rb +1 -1
  201. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  202. data/lib/hexapdf/type/annotations/polygon.rb +1 -1
  203. data/lib/hexapdf/type/annotations/polygon_polyline.rb +1 -1
  204. data/lib/hexapdf/type/annotations/polyline.rb +1 -1
  205. data/lib/hexapdf/type/annotations/square.rb +1 -1
  206. data/lib/hexapdf/type/annotations/square_circle.rb +1 -1
  207. data/lib/hexapdf/type/annotations/text.rb +1 -1
  208. data/lib/hexapdf/type/annotations/widget.rb +10 -1
  209. data/lib/hexapdf/type/annotations.rb +1 -1
  210. data/lib/hexapdf/type/catalog.rb +1 -1
  211. data/lib/hexapdf/type/cid_font.rb +1 -1
  212. data/lib/hexapdf/type/cmap.rb +1 -1
  213. data/lib/hexapdf/type/document_security_store.rb +80 -0
  214. data/lib/hexapdf/type/embedded_file.rb +1 -1
  215. data/lib/hexapdf/type/file_specification.rb +1 -1
  216. data/lib/hexapdf/type/font.rb +4 -4
  217. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  218. data/lib/hexapdf/type/font_simple.rb +1 -1
  219. data/lib/hexapdf/type/font_true_type.rb +1 -1
  220. data/lib/hexapdf/type/font_type0.rb +1 -1
  221. data/lib/hexapdf/type/font_type1.rb +1 -1
  222. data/lib/hexapdf/type/font_type3.rb +6 -1
  223. data/lib/hexapdf/type/form.rb +1 -1
  224. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  225. data/lib/hexapdf/type/icon_fit.rb +1 -1
  226. data/lib/hexapdf/type/image.rb +1 -1
  227. data/lib/hexapdf/type/info.rb +1 -1
  228. data/lib/hexapdf/type/mark_information.rb +1 -1
  229. data/lib/hexapdf/type/marked_content_reference.rb +1 -1
  230. data/lib/hexapdf/type/measure.rb +1 -1
  231. data/lib/hexapdf/type/metadata.rb +1 -1
  232. data/lib/hexapdf/type/names.rb +1 -1
  233. data/lib/hexapdf/type/namespace.rb +1 -1
  234. data/lib/hexapdf/type/object_reference.rb +1 -1
  235. data/lib/hexapdf/type/object_stream.rb +1 -1
  236. data/lib/hexapdf/type/optional_content_configuration.rb +1 -1
  237. data/lib/hexapdf/type/optional_content_group.rb +1 -1
  238. data/lib/hexapdf/type/optional_content_membership.rb +1 -1
  239. data/lib/hexapdf/type/optional_content_properties.rb +1 -1
  240. data/lib/hexapdf/type/outline.rb +1 -1
  241. data/lib/hexapdf/type/outline_item.rb +1 -1
  242. data/lib/hexapdf/type/output_intent.rb +1 -1
  243. data/lib/hexapdf/type/page.rb +12 -1
  244. data/lib/hexapdf/type/page_label.rb +1 -1
  245. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  246. data/lib/hexapdf/type/resources.rb +1 -1
  247. data/lib/hexapdf/type/struct_elem.rb +1 -1
  248. data/lib/hexapdf/type/struct_tree_root.rb +1 -1
  249. data/lib/hexapdf/type/trailer.rb +1 -1
  250. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  251. data/lib/hexapdf/type/xref_stream.rb +1 -1
  252. data/lib/hexapdf/type.rb +2 -1
  253. data/lib/hexapdf/utils/bit_field.rb +1 -1
  254. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  255. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  256. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  257. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  258. data/lib/hexapdf/utils/object_hash.rb +1 -1
  259. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  260. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  261. data/lib/hexapdf/utils.rb +1 -1
  262. data/lib/hexapdf/version.rb +2 -2
  263. data/lib/hexapdf/writer.rb +1 -1
  264. data/lib/hexapdf/xref_section.rb +1 -1
  265. data/lib/hexapdf.rb +1 -1
  266. data/test/data/pdfa/mismatching_glyph_widths_cidfont_type2.pdf +0 -0
  267. data/test/hexapdf/content/test_smart_text_extractor.rb +129 -0
  268. data/test/hexapdf/digital_signature/common.rb +19 -5
  269. data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +29 -4
  270. data/test/hexapdf/digital_signature/test_signatures.rb +3 -3
  271. data/test/hexapdf/filter/test_brotli_decode.rb +34 -0
  272. data/test/hexapdf/font/test_true_type_wrapper.rb +2 -2
  273. data/test/hexapdf/font/true_type/table/test_directory.rb +5 -3
  274. data/test/hexapdf/font/true_type/test_builder.rb +9 -0
  275. data/test/hexapdf/font/true_type/test_font.rb +17 -3
  276. data/test/hexapdf/font/true_type/test_subsetter.rb +11 -9
  277. data/test/hexapdf/font_loader/test_from_file.rb +7 -0
  278. data/test/hexapdf/task/test_import_pages.rb +126 -0
  279. data/test/hexapdf/task/test_pdfa.rb +72 -0
  280. data/test/hexapdf/test_document.rb +13 -0
  281. data/test/hexapdf/test_serializer.rb +1 -1
  282. data/test/hexapdf/type/acro_form/test_form.rb +6 -0
  283. data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
  284. data/test/hexapdf/type/annotations/test_widget.rb +11 -0
  285. data/test/hexapdf/type/test_font_type3.rb +4 -0
  286. data/test/hexapdf/type/test_page.rb +8 -0
  287. metadata +25 -1
@@ -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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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
@@ -270,7 +270,7 @@ module HexaPDF
270
270
  str << ">>"
271
271
  end
272
272
 
273
- STRING_ESCAPE_MAP = {"(" => "\\(", ")" => "\\)", "\\" => "\\\\", "\r" => "\\r"}.freeze # :nodoc:
273
+ STRING_ESCAPE_MAP = {"(" => "\\(", ")" => "\\)", "\\" => "\\\\", "\r" => "\\r", "\n" => "\\n"}.freeze # :nodoc:
274
274
 
275
275
  # Serializes a String object.
276
276
  #
@@ -287,7 +287,7 @@ module HexaPDF
287
287
  else
288
288
  obj.b
289
289
  end
290
- obj.gsub!(/[()\\\r]/n, STRING_ESCAPE_MAP)
290
+ obj.gsub!(/[()\\\r\n]/n, STRING_ESCAPE_MAP)
291
291
  "(#{obj})"
292
292
  end
293
293
 
@@ -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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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,185 @@
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-2026 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/serializer'
38
+ require 'set'
39
+
40
+ module HexaPDF
41
+ module Task
42
+
43
+ # Task for importing pages from another document that preserves the visual appearance.
44
+ #
45
+ # It takes care of
46
+ #
47
+ # * importing the specified pages with all associated objects,
48
+ # * handling optional content groups and their default state,
49
+ # * and merging form fields.
50
+ #
51
+ # Note that the /Order, /AS and /Locked fields of the default optional content configuration
52
+ # dictionary are not preserved.
53
+ #
54
+ # Example:
55
+ #
56
+ # doc.task(:import_pages, source: source_doc, pages: [1..-2])
57
+ module ImportPages
58
+
59
+ # Performs the necessary steps to import the pages from the +source+ docment into the target
60
+ # document +doc+. Returns the imported pages.
61
+ #
62
+ # +source+::
63
+ # Specifies the source PDF document from which the pages should be imported.
64
+ #
65
+ # +pages+::
66
+ # Specifies the pages that should be imported. The argument has to be one of the
67
+ # following:
68
+ #
69
+ # +:all+:: Imports all pages from the +source+ document.
70
+ # Integer value:: Imports the page with the given zero-based index.
71
+ # Range value:: Imports the pages from the zero-based range.
72
+ # Array of Integer or Range values:: Imports all specified pages or page ranges.
73
+ # Array of source page objects:: Imports the given pages.
74
+ #
75
+ # +:append+::
76
+ # Specifies whether the imported pages should be appended to the target document's page
77
+ # tree.
78
+ #
79
+ # +ocgs+::
80
+ # Specifies the handling of optional content groups:
81
+ #
82
+ # +:preserve+:: Preserve the on/off state for all used OCGs.
83
+ # +:ignore+:: Ignore the on/off state.
84
+ #
85
+ # +:acro_form+::
86
+ # Specifies whether AcroForm fields should be merged into the target document.
87
+ #
88
+ # +:merge+:: Merge AcroForm fields using the MergeAcroForm task.
89
+ # +:ignore+:: Ignore AcroForm fields.
90
+ def self.call(doc, source:, pages: :all, append: true, ocgs: :preserve, acro_form: :merge)
91
+ # Retrieve all specified source pages
92
+ pages = if pages == :all
93
+ source.pages.each.to_a
94
+ elsif pages.kind_of?(Integer)
95
+ [source.pages[pages]]
96
+ elsif pages.kind_of?(Array) && pages[0].kind_of?(HexaPDF::Type::Page)
97
+ pages
98
+ else
99
+ result = Set.new
100
+ all_pages = source.pages.each.to_a
101
+ pages = [pages] unless pages.kind_of?(Array)
102
+ pages.each {|selector| result.merge(Array(all_pages[selector])) }
103
+ result
104
+ end
105
+
106
+ # Import the source pages and optionally append them to the target page tree
107
+ pages = pages.map do |page|
108
+ imported_page = doc.import(page)
109
+ doc.pages << imported_page if append
110
+ imported_page
111
+ end
112
+
113
+ doc.task(:merge_acro_form, source: source, pages: pages) if acro_form == :merge
114
+ preserve_ocgs(doc, source, pages) if ocgs == :preserve
115
+
116
+ pages
117
+ end
118
+
119
+ # Preserves the state of the OCGs found on +pages+ so that the visual appearance in the target
120
+ # document +doc+ is the same as in the +source+ document.
121
+ def self.preserve_ocgs(doc, source, pages)
122
+ # Find all OCGs used on all pages
123
+ ocgs = Set.new
124
+ process_ocg_or_ocmd = lambda do |obj|
125
+ if obj.type == :OCG
126
+ ocgs << obj
127
+ elsif obj.type == :OCMD
128
+ ocgs.merge(obj[:OCGs].to_ary)
129
+ end
130
+ end
131
+ seen_resources = {}
132
+ pages.each do |page|
133
+ unless seen_resources[page.resources] # handle case when pages share the resources dict
134
+ page.resources[:Properties]&.each do |name, obj|
135
+ next unless obj
136
+ process_ocg_or_ocmd.call(obj)
137
+ end
138
+
139
+ page.resources[:XObject]&.each do |name, obj|
140
+ process_ocg_or_ocmd.call(obj[:OC]) if obj.key?(:OC)
141
+ end
142
+ end
143
+
144
+ page.each_annotation do |annot|
145
+ process_ocg_or_ocmd.call(annot[:OC]) if annot.key?(:OC)
146
+ end
147
+
148
+ seen_resources[page.resources] = true
149
+ end
150
+
151
+ return if ocgs.empty?
152
+
153
+ # Add all found OCGs to the optional content properties dictionary
154
+ ocp = doc.optional_content
155
+ ocgs.each {|ocg| ocp.add_ocg(ocg) }
156
+
157
+ # Create a mapping from source OCGs to target OCGs and vice-versa
158
+ source_ocg = {}
159
+ target_ocg = {}
160
+ source.optional_content.ocgs.each do |ocg|
161
+ imported_ocg = doc.import(ocg)
162
+ next unless ocgs.include?(imported_ocg)
163
+ source_ocg[imported_ocg] = ocg
164
+ target_ocg[ocg] = imported_ocg
165
+ end
166
+
167
+ # Ensure the initial state of the OCGs is correct
168
+ source_config = source.optional_content.default_configuration
169
+ target_config = ocp.default_configuration
170
+ ocgs.each do |ocg|
171
+ target_config.ocg_state(ocg, source_config.ocg_state(source_ocg[ocg]))
172
+ end
173
+
174
+ # Copy radio button groups from the source document, removing unknown OCGs from them
175
+ source_config[:RBGroups]&.each do |array|
176
+ result = array.map {|ocg| target_ocg[ocg] }.compact
177
+ next if result.empty?
178
+ (target_config[:RBGroups] ||= []) << result
179
+ end
180
+ end
181
+
182
+ end
183
+
184
+ end
185
+ 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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
@@ -40,6 +40,7 @@ require 'hexapdf/content/parser'
40
40
  require 'hexapdf/content/operator'
41
41
  require 'hexapdf/type/xref_stream'
42
42
  require 'hexapdf/type/object_stream'
43
+ require 'hexapdf/font/true_type'
43
44
 
44
45
  module HexaPDF
45
46
  module Task
@@ -51,6 +52,13 @@ module HexaPDF
51
52
  # * prevents the Standard 14 PDF fonts to be used.
52
53
  # * adds an appropriate output intent if none is set.
53
54
  # * adds the necessary PDF/A metadata properties.
55
+ #
56
+ # Additionally, it applies fixes to the document so that the structures and content of
57
+ # non-conforming PDFs are corrected. See ::call for more information on the available fixes.
58
+ #
59
+ # Note that you should use a PDF/A validation tool like veraPDF (https://verapdf.org/) to ensure
60
+ # that the resulting files confirm to the PDF/A specification because not all documents can be
61
+ # fixed at the moment.
54
62
  module PDFA
55
63
 
56
64
  # Performs the necessary tasks to make the document PDF/A compatible.
@@ -58,7 +66,22 @@ module HexaPDF
58
66
  # +level+::
59
67
  # Specifies the PDF/A conformance level that should be used. Can be one of the following
60
68
  # strings: 2b, 2u, 3b, 3u.
61
- def self.call(doc, level: '3u')
69
+ #
70
+ # +fixes+::
71
+ # Specifies the fixes that should be applied when converting a non-conforming PDF. If a
72
+ # document is created with HexaPDF but also includes parts of loaded documents, this
73
+ # argument hast to be set to +:all+.
74
+ #
75
+ # Can be +:default+ (which is also the default value), +:all+ or an array with one or more
76
+ # fix names.
77
+ #
78
+ # +:default+:: Applies all fixes if the document was loaded from a file. Otherwise applies
79
+ # only those fixes necessary for files created with HexaPDF.
80
+ #
81
+ # +:all+: Applies all available fixes.
82
+ #
83
+ # +:glyph_widths+:: Corrects mismatching width information in fonts.
84
+ def self.call(doc, level: '3u', fixes: :default)
62
85
  unless level.match?(/\A[23][bu]\z/)
63
86
  raise ArgumentError, "The given PDF/A conformance level '#{level}' is not supported"
64
87
  end
@@ -68,6 +91,15 @@ module HexaPDF
68
91
  doc.metadata.property('pdfaid', 'part', part)
69
92
  doc.metadata.property('pdfaid', 'conformance', conformance.upcase)
70
93
  add_srgb_icc_output_intent(doc) unless doc.catalog.key?(:OutputIntents)
94
+
95
+ fixes = if fixes == :all || (fixes == :default && doc.revisions.parser)
96
+ ALL_FIXES
97
+ elsif fixes == :default
98
+ ALL_FIXES - FIXES_FOR_LOADED_DOCUMENTS
99
+ else
100
+ fixes
101
+ end
102
+ fixes.each {|fix| send(fix, doc) }
71
103
  end
72
104
  end
73
105
 
@@ -81,6 +113,81 @@ module HexaPDF
81
113
  ]
82
114
  end
83
115
 
116
+ ALL_FIXES = [:fix_glyph_widths] # :nodoc:
117
+
118
+ FIXES_FOR_LOADED_DOCUMENTS = [:fix_glyph_widths] # :nodoc:
119
+
120
+ # Makes the glyph widths stored in the embedded fonts the same as the ones specified in the
121
+ # PDF font data structures.
122
+ #
123
+ # Note: Currently only handles Type 2 CIDFonts.
124
+ def self.fix_glyph_widths(doc) # :nodoc:
125
+ # Step 1: Collect all CIDs together with their respective fonts
126
+ processor = CIDCollector.new
127
+ doc.pages.each do |page|
128
+ page.process_contents(processor)
129
+ page.each_annotation do |annotation|
130
+ next unless (appearance = annotation.appearance)
131
+ appearance.process_contents(processor, original_resources: page.resources)
132
+ end
133
+ end
134
+
135
+ # Step 2: Process all found fonts
136
+ processor.map.each do |font_object, all_cids|
137
+ next if all_cids.empty?
138
+ font = HexaPDF::Font::TrueType::Font.new(StringIO.new(font_object.font_file.stream))
139
+ cid_to_gid = cid_to_gid_mapping(font_object)
140
+
141
+ # Process all found CIDs by comparing their width with the ones defined in the font and
142
+ # correcting the font if necessary.
143
+ raw_hmtx = font[:hmtx].raw_data
144
+ width_conversion_factor = 1000.0 / font[:head].units_per_em
145
+ all_cids.each do |cid|
146
+ cid_width = font_object.width(cid)
147
+ gid = cid_to_gid[cid]
148
+ gid_width = font[:hmtx][gid].advance_width * width_conversion_factor
149
+ next if (cid_width - gid_width).abs.round <= 1
150
+ raw_hmtx[4 * gid, 2] = [(cid_width / width_conversion_factor).round].pack('n')
151
+ end
152
+
153
+ font_object.font_file.stream = font.build('hmtx' => raw_hmtx)
154
+ end
155
+ end
156
+
157
+ # Processes the contents of a stream and collects the CIDs for each composite font.
158
+ class CIDCollector < HexaPDF::Content::Processor
159
+
160
+ # The mapping from the composite font's descendant font to the set of used CIDs.
161
+ attr_reader :map
162
+
163
+ def initialize(*) # :nodoc:
164
+ super
165
+ @map = Hash.new {|h, k| h[k] = Set.new }
166
+ end
167
+
168
+ def show_text(data) # :nodoc:
169
+ font = graphics_state.font
170
+ return unless font[:Subtype] == :Type0 && font.descendant_font[:Subtype] == :CIDFontType2
171
+
172
+ Array(data).each do |item|
173
+ next if item.kind_of?(Numeric)
174
+ @map[font.descendant_font].merge(font.decode(item))
175
+ end
176
+ end
177
+ alias show_text_with_positioning show_text
178
+
179
+ end
180
+
181
+ # Returns an object responding to #[] that maps CIDs to GIDs for Type 2 CIDFonts.
182
+ def self.cid_to_gid_mapping(font)
183
+ if font[:CIDToGIDMap] == :Identity
184
+ proc {|cid| cid }
185
+ else
186
+ font[:CIDToGIDMap].stream.unpack('n*')
187
+ end
188
+ end
189
+ private_class_method :cid_to_gid_mapping
190
+
84
191
  end
85
192
 
86
193
  end
data/lib/hexapdf/task.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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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,6 +66,7 @@ module HexaPDF
66
66
  autoload(:Dereference, 'hexapdf/task/dereference')
67
67
  autoload(:PDFA, 'hexapdf/task/pdfa')
68
68
  autoload(:MergeAcroForm, 'hexapdf/task/merge_acro_form')
69
+ autoload(:ImportPages, 'hexapdf/task/import_pages')
69
70
 
70
71
  end
71
72
 
@@ -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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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
@@ -412,6 +412,8 @@ module HexaPDF
412
412
  #
413
413
  # * For radio buttons the value needs to be a String or a Symbol representing the name of
414
414
  # the radio button widget to select.
415
+ #
416
+ # * Values for password fields are ignored as they should not be stored in the PDF.
415
417
  def fill(data)
416
418
  data.each do |field_name, value|
417
419
  field = field_by_name(field_name)
@@ -427,6 +429,8 @@ module HexaPDF
427
429
  when /\A(?:n(o)?|f(alse)?)\z/ then false
428
430
  else value
429
431
  end
432
+ when :password_field
433
+ # Ignore the value
430
434
  else
431
435
  raise HexaPDF::Error, "AcroForm field type #{field.concrete_field_type} not yet supported"
432
436
  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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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
@@ -344,8 +344,10 @@ module HexaPDF
344
344
  super
345
345
 
346
346
  if self[:V] && !(self[:V].kind_of?(String) || self[:V].kind_of?(HexaPDF::Stream))
347
- yield("Text field doesn't contain text but #{self[:V].class} object")
348
- return
347
+ correctable = self[:V].kind_of?(Symbol)
348
+ yield("Text field doesn't contain text but an object of type #{self[:V].class}", correctable)
349
+ return unless correctable
350
+ self[:V] = self[:V].to_s
349
351
  end
350
352
  if (max_len = self[:MaxLen]) && field_value && field_value.length > max_len
351
353
  correctable = true
@@ -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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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-2025 Thomas Leitner
7
+ # Copyright (C) 2014-2026 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