hexapdf 1.7.0 → 1.9.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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/LICENSE +1 -1
  4. data/README.md +3 -0
  5. data/Rakefile +1 -1
  6. data/data/hexapdf/fonts/Inter-Bold.ttf +0 -0
  7. data/data/hexapdf/fonts/Inter-BoldItalic.ttf +0 -0
  8. data/data/hexapdf/fonts/Inter-Italic.ttf +0 -0
  9. data/data/hexapdf/fonts/Inter-Regular.ttf +0 -0
  10. data/data/hexapdf/fonts/OFL.txt +92 -0
  11. data/examples/005-merging.rb +2 -1
  12. data/examples/019-acro_form.rb +3 -1
  13. data/examples/030-pdfa.rb +9 -16
  14. data/examples/034-text_shaping.rb +37 -0
  15. data/lib/hexapdf/cli/batch.rb +1 -1
  16. data/lib/hexapdf/cli/command.rb +1 -1
  17. data/lib/hexapdf/cli/debug_info.rb +1 -1
  18. data/lib/hexapdf/cli/files.rb +1 -1
  19. data/lib/hexapdf/cli/fonts.rb +6 -4
  20. data/lib/hexapdf/cli/form.rb +1 -1
  21. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  22. data/lib/hexapdf/cli/images.rb +17 -17
  23. data/lib/hexapdf/cli/info.rb +1 -1
  24. data/lib/hexapdf/cli/inspect.rb +1 -1
  25. data/lib/hexapdf/cli/merge.rb +14 -2
  26. data/lib/hexapdf/cli/modify.rb +1 -1
  27. data/lib/hexapdf/cli/optimize.rb +1 -1
  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 +1 -1
  32. data/lib/hexapdf/composer.rb +1 -1
  33. data/lib/hexapdf/configuration.rb +15 -2
  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 +1 -1
  43. data/lib/hexapdf/content/operator.rb +1 -1
  44. data/lib/hexapdf/content/parser.rb +1 -1
  45. data/lib/hexapdf/content/processor.rb +1 -1
  46. data/lib/hexapdf/content/smart_text_extractor.rb +10 -4
  47. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  48. data/lib/hexapdf/content.rb +1 -1
  49. data/lib/hexapdf/data_dir.rb +1 -1
  50. data/lib/hexapdf/dictionary.rb +1 -1
  51. data/lib/hexapdf/dictionary_fields.rb +1 -1
  52. data/lib/hexapdf/digital_signature/cms_handler.rb +1 -1
  53. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  54. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +1 -1
  55. data/lib/hexapdf/digital_signature/signature.rb +1 -1
  56. data/lib/hexapdf/digital_signature/signatures.rb +1 -1
  57. data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -1
  58. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +1 -1
  59. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +1 -1
  60. data/lib/hexapdf/digital_signature/signing.rb +1 -1
  61. data/lib/hexapdf/digital_signature/verification_result.rb +1 -1
  62. data/lib/hexapdf/digital_signature.rb +1 -1
  63. data/lib/hexapdf/document/annotations.rb +26 -1
  64. data/lib/hexapdf/document/destinations.rb +1 -1
  65. data/lib/hexapdf/document/files.rb +1 -1
  66. data/lib/hexapdf/document/fonts.rb +1 -1
  67. data/lib/hexapdf/document/images.rb +1 -1
  68. data/lib/hexapdf/document/layout.rb +1 -1
  69. data/lib/hexapdf/document/metadata.rb +1 -1
  70. data/lib/hexapdf/document/pages.rb +1 -1
  71. data/lib/hexapdf/document.rb +1 -1
  72. data/lib/hexapdf/encryption/aes.rb +1 -1
  73. data/lib/hexapdf/encryption/arc4.rb +1 -1
  74. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  75. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  76. data/lib/hexapdf/encryption/identity.rb +1 -1
  77. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  78. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  79. data/lib/hexapdf/encryption/security_handler.rb +1 -1
  80. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
  81. data/lib/hexapdf/encryption.rb +1 -1
  82. data/lib/hexapdf/error.rb +1 -1
  83. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  84. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  85. data/lib/hexapdf/filter/brotli_decode.rb +1 -1
  86. data/lib/hexapdf/filter/crypt.rb +1 -1
  87. data/lib/hexapdf/filter/encryption.rb +1 -1
  88. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  89. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  90. data/lib/hexapdf/filter/pass_through.rb +1 -1
  91. data/lib/hexapdf/filter/predictor.rb +1 -1
  92. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  93. data/lib/hexapdf/filter.rb +1 -1
  94. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  95. data/lib/hexapdf/font/cmap/writer.rb +16 -10
  96. data/lib/hexapdf/font/cmap.rb +1 -1
  97. data/lib/hexapdf/font/encoding/base.rb +1 -1
  98. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  99. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  100. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  101. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  102. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  103. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  104. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  105. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  106. data/lib/hexapdf/font/encoding.rb +1 -1
  107. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  108. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  109. data/lib/hexapdf/font/true_type/font.rb +1 -1
  110. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  111. data/lib/hexapdf/font/true_type/subsetter.rb +4 -4
  112. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  114. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  115. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  116. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  117. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  118. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  119. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  120. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  121. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  122. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  123. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  124. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  125. data/lib/hexapdf/font/true_type/table.rb +1 -1
  126. data/lib/hexapdf/font/true_type.rb +1 -1
  127. data/lib/hexapdf/font/true_type_wrapper.rb +9 -6
  128. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  129. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  130. data/lib/hexapdf/font/type1/font.rb +1 -1
  131. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  132. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  133. data/lib/hexapdf/font/type1.rb +1 -1
  134. data/lib/hexapdf/font/type1_wrapper.rb +1 -1
  135. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  136. data/lib/hexapdf/font_loader/from_file.rb +5 -1
  137. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  138. data/lib/hexapdf/font_loader/variant_from_name.rb +1 -1
  139. data/lib/hexapdf/font_loader.rb +48 -1
  140. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  141. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  142. data/lib/hexapdf/image_loader/png.rb +1 -1
  143. data/lib/hexapdf/image_loader.rb +1 -1
  144. data/lib/hexapdf/importer.rb +1 -1
  145. data/lib/hexapdf/layout/box.rb +1 -1
  146. data/lib/hexapdf/layout/box_fitter.rb +1 -1
  147. data/lib/hexapdf/layout/column_box.rb +1 -1
  148. data/lib/hexapdf/layout/container_box.rb +3 -5
  149. data/lib/hexapdf/layout/frame.rb +1 -1
  150. data/lib/hexapdf/layout/image_box.rb +1 -1
  151. data/lib/hexapdf/layout/inline_box.rb +1 -1
  152. data/lib/hexapdf/layout/line.rb +1 -1
  153. data/lib/hexapdf/layout/list_box.rb +1 -1
  154. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  155. data/lib/hexapdf/layout/page_style.rb +1 -1
  156. data/lib/hexapdf/layout/style.rb +67 -5
  157. data/lib/hexapdf/layout/table_box.rb +97 -14
  158. data/lib/hexapdf/layout/text_box.rb +1 -1
  159. data/lib/hexapdf/layout/text_fragment.rb +14 -8
  160. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  161. data/lib/hexapdf/layout/text_shaper.rb +163 -11
  162. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  163. data/lib/hexapdf/layout.rb +1 -1
  164. data/lib/hexapdf/name_tree_node.rb +1 -1
  165. data/lib/hexapdf/number_tree_node.rb +1 -1
  166. data/lib/hexapdf/object.rb +1 -1
  167. data/lib/hexapdf/parser.rb +1 -1
  168. data/lib/hexapdf/pdf_array.rb +1 -1
  169. data/lib/hexapdf/rectangle.rb +1 -1
  170. data/lib/hexapdf/reference.rb +1 -1
  171. data/lib/hexapdf/revision.rb +1 -1
  172. data/lib/hexapdf/revisions.rb +1 -1
  173. data/lib/hexapdf/serializer.rb +3 -3
  174. data/lib/hexapdf/stream.rb +1 -1
  175. data/lib/hexapdf/task/dereference.rb +1 -1
  176. data/lib/hexapdf/task/import_pages.rb +185 -0
  177. data/lib/hexapdf/task/merge_acro_form.rb +1 -1
  178. data/lib/hexapdf/task/optimize.rb +1 -1
  179. data/lib/hexapdf/task/pdfa.rb +1 -1
  180. data/lib/hexapdf/task.rb +2 -1
  181. data/lib/hexapdf/test_utils.rb +1 -1
  182. data/lib/hexapdf/tokenizer.rb +1 -1
  183. data/lib/hexapdf/type/acro_form/appearance_generator.rb +1 -1
  184. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  185. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  186. data/lib/hexapdf/type/acro_form/field.rb +1 -1
  187. data/lib/hexapdf/type/acro_form/form.rb +1 -1
  188. data/lib/hexapdf/type/acro_form/java_script_actions.rb +1 -1
  189. data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
  190. data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
  191. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  192. data/lib/hexapdf/type/acro_form.rb +1 -1
  193. data/lib/hexapdf/type/action.rb +1 -1
  194. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  195. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  196. data/lib/hexapdf/type/actions/launch.rb +1 -1
  197. data/lib/hexapdf/type/actions/set_ocg_state.rb +1 -1
  198. data/lib/hexapdf/type/actions/uri.rb +1 -1
  199. data/lib/hexapdf/type/actions.rb +1 -1
  200. data/lib/hexapdf/type/annotation.rb +1 -1
  201. data/lib/hexapdf/type/annotations/appearance_generator.rb +43 -1
  202. data/lib/hexapdf/type/annotations/border_effect.rb +1 -1
  203. data/lib/hexapdf/type/annotations/border_styling.rb +1 -1
  204. data/lib/hexapdf/type/annotations/circle.rb +1 -1
  205. data/lib/hexapdf/type/annotations/ink.rb +107 -0
  206. data/lib/hexapdf/type/annotations/interior_color.rb +1 -1
  207. data/lib/hexapdf/type/annotations/line.rb +1 -1
  208. data/lib/hexapdf/type/annotations/line_ending_styling.rb +1 -1
  209. data/lib/hexapdf/type/annotations/link.rb +1 -1
  210. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  211. data/lib/hexapdf/type/annotations/polygon.rb +1 -1
  212. data/lib/hexapdf/type/annotations/polygon_polyline.rb +1 -1
  213. data/lib/hexapdf/type/annotations/polyline.rb +1 -1
  214. data/lib/hexapdf/type/annotations/square.rb +1 -1
  215. data/lib/hexapdf/type/annotations/square_circle.rb +1 -1
  216. data/lib/hexapdf/type/annotations/text.rb +1 -1
  217. data/lib/hexapdf/type/annotations/widget.rb +1 -1
  218. data/lib/hexapdf/type/annotations.rb +2 -1
  219. data/lib/hexapdf/type/catalog.rb +1 -1
  220. data/lib/hexapdf/type/cid_font.rb +1 -1
  221. data/lib/hexapdf/type/cmap.rb +1 -1
  222. data/lib/hexapdf/type/document_security_store.rb +1 -1
  223. data/lib/hexapdf/type/embedded_file.rb +1 -1
  224. data/lib/hexapdf/type/file_specification.rb +1 -1
  225. data/lib/hexapdf/type/font.rb +4 -4
  226. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  227. data/lib/hexapdf/type/font_simple.rb +1 -1
  228. data/lib/hexapdf/type/font_true_type.rb +1 -1
  229. data/lib/hexapdf/type/font_type0.rb +1 -1
  230. data/lib/hexapdf/type/font_type1.rb +1 -1
  231. data/lib/hexapdf/type/font_type3.rb +6 -1
  232. data/lib/hexapdf/type/form.rb +1 -1
  233. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  234. data/lib/hexapdf/type/icon_fit.rb +1 -1
  235. data/lib/hexapdf/type/image.rb +1 -1
  236. data/lib/hexapdf/type/info.rb +1 -1
  237. data/lib/hexapdf/type/mark_information.rb +1 -1
  238. data/lib/hexapdf/type/marked_content_reference.rb +1 -1
  239. data/lib/hexapdf/type/measure.rb +1 -1
  240. data/lib/hexapdf/type/metadata.rb +1 -1
  241. data/lib/hexapdf/type/names.rb +1 -1
  242. data/lib/hexapdf/type/namespace.rb +1 -1
  243. data/lib/hexapdf/type/object_reference.rb +1 -1
  244. data/lib/hexapdf/type/object_stream.rb +1 -1
  245. data/lib/hexapdf/type/optional_content_configuration.rb +1 -1
  246. data/lib/hexapdf/type/optional_content_group.rb +1 -1
  247. data/lib/hexapdf/type/optional_content_membership.rb +1 -1
  248. data/lib/hexapdf/type/optional_content_properties.rb +1 -1
  249. data/lib/hexapdf/type/outline.rb +1 -1
  250. data/lib/hexapdf/type/outline_item.rb +1 -1
  251. data/lib/hexapdf/type/output_intent.rb +1 -1
  252. data/lib/hexapdf/type/page.rb +1 -1
  253. data/lib/hexapdf/type/page_label.rb +1 -1
  254. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  255. data/lib/hexapdf/type/resources.rb +1 -1
  256. data/lib/hexapdf/type/struct_elem.rb +1 -1
  257. data/lib/hexapdf/type/struct_tree_root.rb +1 -1
  258. data/lib/hexapdf/type/trailer.rb +1 -1
  259. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  260. data/lib/hexapdf/type/xref_stream.rb +1 -1
  261. data/lib/hexapdf/type.rb +1 -1
  262. data/lib/hexapdf/utils/bit_field.rb +1 -1
  263. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  264. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  265. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  266. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  267. data/lib/hexapdf/utils/object_hash.rb +1 -1
  268. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  269. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  270. data/lib/hexapdf/utils.rb +1 -1
  271. data/lib/hexapdf/version.rb +2 -2
  272. data/lib/hexapdf/writer.rb +1 -1
  273. data/lib/hexapdf/xref_section.rb +1 -1
  274. data/lib/hexapdf.rb +1 -1
  275. data/test/hexapdf/digital_signature/common.rb +5 -5
  276. data/test/hexapdf/digital_signature/test_cms_handler.rb +1 -1
  277. data/test/hexapdf/document/test_annotations.rb +10 -0
  278. data/test/hexapdf/document/test_layout.rb +6 -3
  279. data/test/hexapdf/filter/test_brotli_decode.rb +1 -1
  280. data/test/hexapdf/font/cmap/test_writer.rb +8 -6
  281. data/test/hexapdf/font/test_true_type_wrapper.rb +6 -2
  282. data/test/hexapdf/font/true_type/test_subsetter.rb +7 -6
  283. data/test/hexapdf/font_loader/test_from_file.rb +7 -0
  284. data/test/hexapdf/layout/test_container_box.rb +3 -1
  285. data/test/hexapdf/layout/test_style.rb +4 -0
  286. data/test/hexapdf/layout/test_table_box.rb +117 -1
  287. data/test/hexapdf/layout/test_text_fragment.rb +18 -8
  288. data/test/hexapdf/layout/test_text_shaper.rb +55 -5
  289. data/test/hexapdf/task/test_import_pages.rb +126 -0
  290. data/test/hexapdf/test_serializer.rb +1 -1
  291. data/test/hexapdf/type/annotations/test_appearance_generator.rb +63 -0
  292. data/test/hexapdf/type/annotations/test_ink.rb +31 -0
  293. data/test/hexapdf/type/test_font_type3.rb +4 -0
  294. metadata +26 -2
@@ -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
@@ -45,6 +45,53 @@ module HexaPDF
45
45
  # needs to be usable by the PDF canvas. See below for details.
46
46
  #
47
47
  #
48
+ # == Available Font Loaders
49
+ #
50
+ # The following font loaders are available:
51
+ #
52
+ # HexaPDF::FontLoader::Standard14::
53
+ # This one makes the standard 14 PDF fonts (Helvetica in variants none, italic, bold and bold
54
+ # italic; Times in variants none, italic, bold and bold italic; Symbol; and ZapfDingbats)
55
+ # available.
56
+ #
57
+ # Usage:
58
+ #
59
+ # canvas.font('Times', variant: :bold)
60
+ #
61
+ # HexaPDF::FontLoader::FromFile::
62
+ # Interprets the font name as filename and tries to load the font from there. In this case a
63
+ # +:variant+ argument cannot be used as the font file is directly specified.
64
+ #
65
+ # Usage:
66
+ #
67
+ # canvas.font('/usr/share/fonts/truetype/hack/Hack-Regular.ttf')
68
+ #
69
+ # HexaPDF::FontLoader::VariantFromName:
70
+ # This one doesn't really load a font itself but makes it possible to append the variant name
71
+ # to the font name. So it resolves e.g. 'Times bold' to the font 'Times' in variant :bold.
72
+ #
73
+ # Usage:
74
+ #
75
+ # canvas.font('Times bold')
76
+ #
77
+ # HexaPDF::FontLoader::FromConfiguration
78
+ # This font loader defers to FromFile when loading the actual fonts. It allows defining font
79
+ # mappings using the configuration option 'font.map' where a font name is mapped to a hash
80
+ # that maps variant names to font file names.
81
+ #
82
+ # Usage:
83
+ #
84
+ # doc.config['font.map'] = {
85
+ # 'Hack' => {
86
+ # none: '/usr/share/fonts/ttf/Hack-Regular.ttf',
87
+ # bold: '/usr/share/fonts/ttf/Hack-Bold.ttf',
88
+ # italic: '/usr/share/fonts/ttf/Hack-Italic.ttf',
89
+ # bold_italic: '/usr/share/fonts/ttf/Hack-BoldItalic.ttf',
90
+ # },
91
+ # }
92
+ # canvas.font('Hack', variant: :italic)
93
+ #
94
+ #
48
95
  # == Implementation of a Font Loader
49
96
  #
50
97
  # Each font loader is a (stateless) object (normally a module) that has to be callable, i.e. it
@@ -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
@@ -163,15 +163,13 @@ module HexaPDF
163
163
  @box_fitter = BoxFitter.new([my_frame])
164
164
  children.each {|box| @box_fitter.fit(box) }
165
165
 
166
- if @box_fitter.success?
166
+ if @box_fitter.success? || (!@box_fitter.fit_results.empty? && @splitable)
167
167
  update_content_width do
168
168
  result = @box_fitter.fit_results.max_by {|r| r.mask.x + r.mask.width }
169
169
  children.empty? ? 0 : result.mask.x + result.mask.width - my_frame.left
170
170
  end
171
171
  update_content_height { @box_fitter.content_heights.max }
172
- fit_result.success!
173
- elsif !@box_fitter.fit_results.empty? && @splitable
174
- fit_result.overflow!
172
+ @box_fitter.success? ? fit_result.success! : fit_result.overflow!
175
173
  end
176
174
  end
177
175
 
@@ -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
@@ -811,18 +811,76 @@ module HexaPDF
811
811
  # font_features(features = nil)
812
812
  #
813
813
  # The font features (e.g. kerning, ligatures, ...) that should be applied by the shaping
814
- # engine, defaults to {} (i.e. no font features are applied).
814
+ # engine, defaults to the empty hash {} (i.e. no font features are applied).
815
815
  #
816
- # Each feature to be applied is indicated by a key with a truthy value.
816
+ # Each feature can either be activated using `true` as value, deactivated using `false` as
817
+ # value or set to a specific mode using an integer. What is actually supported depends on the
818
+ # used shaping engine.
817
819
  #
818
- # See: HexaPDF::Layout::TextShaper#shape_text for available features.
820
+ # See: #shaping_engine
819
821
  #
820
822
  # Examples:
821
823
  #
822
824
  # #>pdf-composer100
823
825
  # composer.style(:base, font: ["Times", custom_encoding: true], font_size: 30)
824
826
  # composer.text("Test flight")
825
- # composer.text("Test flight", font_features: {kern: true, liga: true})
827
+ # composer.text("Test flight", font_features: {kern: true, liga: false})
828
+
829
+ ##
830
+ # :method: font_script
831
+ # :call-seq:
832
+ # font_script(script = nil)
833
+ #
834
+ # The script in which the text is written, defaults to +nil+.
835
+ #
836
+ # This is used by the shaping engine to select the correct shaping implementation. If not set,
837
+ # the script is guessed from the text.
838
+ #
839
+ # See: #shaping_engine
840
+
841
+ ##
842
+ # :method: language
843
+ # :call-seq:
844
+ # language(lang = nil)
845
+ #
846
+ # The language in which the text is written, defaults to +nil+.
847
+ #
848
+ # This is used, for example, by the shaping engine to correctly shape the text.
849
+ #
850
+ # See: #shaping_engine
851
+
852
+ ##
853
+ # :method: direction
854
+ # :call-seq:
855
+ # direction(dir = nil)
856
+ #
857
+ # The direction of text, defaults to +:ltr+ (possible values are +:ltr+ and +:rtl+).
858
+ #
859
+ # This is used by the shaping engine to correctly shape the text.
860
+ #
861
+ # See: #shaping_engine
862
+
863
+ ##
864
+ # :method: shaping_engine
865
+ # :call-seq:
866
+ # shaping_engine(engine = nil)
867
+ #
868
+ # The shaping engine that should be used, defaults to +:internal+. The other possible value is
869
+ # +:harfbuzz+.
870
+ #
871
+ # When set to +:harfbuzz+, the Rubygem +harfbuzz-ruby+ needs to be installed. If it is not
872
+ # available, HexaPDF will raise an error.
873
+ #
874
+ # See: HexaPDF::Layout::TextShaper for details.
875
+ #
876
+ # Examples:
877
+ #
878
+ # #>pdf-composer100
879
+ # composer.style(:base, font: 'Inter')
880
+ # composer.text("Incoming WAVE!")
881
+ # composer.formatted_text(["Incoming WAVE!",
882
+ # {text: " Take Cover!", font_features: {ss06: true}}],
883
+ # shaping_engine: :harfbuzz)
826
884
 
827
885
  ##
828
886
  # :method: text_rendering_mode
@@ -1541,6 +1599,10 @@ module HexaPDF
1541
1599
  [:horizontal_scaling, 100],
1542
1600
  [:text_rise, 0],
1543
1601
  [:font_features, {}],
1602
+ [:font_script, nil],
1603
+ [:language, nil],
1604
+ [:direction, :ltr, {valid_values: [:ltr, :rtl]}],
1605
+ [:shaping_engine, :internal, {valid_values: [:internal, :harfbuzz]}],
1544
1606
  [:text_rendering_mode, "Content::TextRenderingMode::FILL",
1545
1607
  {setter: "Content::TextRenderingMode.normalize(value)"}],
1546
1608
  [:subscript, false,
@@ -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
@@ -108,6 +108,17 @@ module HexaPDF
108
108
  # [layout.text('E'), layout.text('F')]]
109
109
  # composer.column(height: 50) {|col| col.table(cells) }
110
110
  #
111
+ # If a cell doesn't completely fit, it is split. Non-split cells in the same continuation row
112
+ # retain their style but are empty:
113
+ #
114
+ # #>pdf-composer
115
+ # cells = [[layout.text('A'), layout.text('B')],
116
+ # [layout.text('C'), layout.text("D1\nD2")],
117
+ # [layout.text('E'), layout.text('F')]]
118
+ # composer.column(height: 50) {|col| col.table(cells) }
119
+ #
120
+ # Note that the above is only true for cells in rows that are not part of a row-span.
121
+ #
111
122
  # It is also possible to use row and column spans:
112
123
  #
113
124
  # #>pdf-composer
@@ -237,6 +248,7 @@ module HexaPDF
237
248
 
238
249
  # Fits the children of the table cell into the given rectangular area.
239
250
  def fit_content(available_width, available_height, frame)
251
+ @remaining_boxes = nil
240
252
  width = available_width - reserved_width
241
253
  height = @used_height = available_height - reserved_height
242
254
  return if width <= 0 || height <= 0
@@ -245,21 +257,27 @@ module HexaPDF
245
257
  case children
246
258
  when Box
247
259
  child_result = frame.fit(children)
248
- if child_result.success?
260
+ box, @remaining_boxes = children.split if child_result.overflow?
261
+ if child_result.success? || box
249
262
  @preferred_width = child_result.x + child_result.box.width + reserved_width
250
263
  @height = @preferred_height = child_result.box.height + reserved_height
251
264
  @fit_results = [child_result]
252
- fit_result.success!
265
+ box ? fit_result.overflow! : fit_result.success!
253
266
  end
254
267
  when Array
255
268
  box_fitter = BoxFitter.new([frame])
256
269
  children.each {|box| box_fitter.fit(box) }
257
- if box_fitter.success?
270
+ if box_fitter.success? || !box_fitter.fit_results.empty?
258
271
  max_x_result = box_fitter.fit_results.max_by {|result| result.x + result.box.width }
259
272
  @preferred_width = max_x_result.x + max_x_result.box.width + reserved_width
260
273
  @height = @preferred_height = box_fitter.content_heights[0] + reserved_height
261
274
  @fit_results = box_fitter.fit_results
262
- fit_result.success!
275
+ if box_fitter.success?
276
+ fit_result.success!
277
+ else
278
+ @remaining_boxes = box_fitter.remaining_boxes
279
+ fit_result.overflow!
280
+ end
263
281
  end
264
282
  else
265
283
  @preferred_width = reserved_width
@@ -274,6 +292,14 @@ module HexaPDF
274
292
  end
275
293
  end
276
294
 
295
+ # Splits the content of the cell.
296
+ def split_content
297
+ box = create_split_box
298
+ box.instance_variable_set(:@children, @remaining_boxes)
299
+ box.instance_variable_set(:@remaining_boxes, nil)
300
+ [self, box]
301
+ end
302
+
277
303
  # Draws the content of the cell.
278
304
  def draw_content(canvas, x, y)
279
305
  return if @fit_results.empty?
@@ -363,7 +389,7 @@ module HexaPDF
363
389
  # Note that the same cell instance may be returned for different (row, column) arguments if
364
390
  # the cell spans more than one row and/or column.
365
391
  def [](row, column)
366
- @cells[row]&.[](column)
392
+ row == @overridden_row_index ? @overridden_row_cells[column] : @cells[row]&.[](column)
367
393
  end
368
394
 
369
395
  # Returns the number of rows.
@@ -378,7 +404,15 @@ module HexaPDF
378
404
 
379
405
  # Iterates over each row.
380
406
  def each_row(&block)
381
- @cells.each(&block)
407
+ return to_enum(__method__) unless block_given?
408
+
409
+ if @overridden_row_index
410
+ @cells[0...@overridden_row_index].each(&block)
411
+ block&.call(@overridden_row_cells)
412
+ @cells[(@overridden_row_index + 1)..-1].each(&block)
413
+ else
414
+ @cells.each(&block)
415
+ end
382
416
  end
383
417
 
384
418
  # Applies the given style properties to all cells and optionally yields all cells for more
@@ -409,12 +443,19 @@ module HexaPDF
409
443
  row_heights = {}
410
444
  zero_height_rows = {}
411
445
  row_spans = []
446
+ split_row_cells = nil
412
447
 
413
448
  @cells[start_row..-1].each.with_index(start_row) do |columns, row_index|
449
+ columns = @overridden_row_cells if row_index == @overridden_row_index
450
+
451
+ # Rows containing row-spanning cells aren't supported for cell-splitting
452
+ row_has_row_spans = columns.any? {|cell| cell.row != row_index || cell.row_span > 1 }
453
+
414
454
  # 1. Fit all columns of the row and record the max height of all non-row-span cells. If
415
455
  # a row has zero height (usually because it only has row-span cells), record that
416
456
  # information. Additionally store all cells with row-spans.
417
457
  row_fit = true
458
+ row_has_split_cells = false
418
459
  row_height = 0
419
460
  columns.each_with_index do |cell, col_index|
420
461
  next if cell.row != row_index || cell.column != col_index
@@ -423,9 +464,14 @@ module HexaPDF
423
464
  else
424
465
  column_info[cell.column].last
425
466
  end
426
- unless cell.fit(available_cell_width, available_height, frame).success?
427
- row_fit = false
428
- break
467
+ cell_fit_result = cell.fit(available_cell_width, available_height, frame)
468
+ unless cell_fit_result.success?
469
+ if !row_has_row_spans && cell_fit_result.overflow?
470
+ row_has_split_cells = true
471
+ else
472
+ row_fit = false
473
+ break
474
+ end
429
475
  end
430
476
  if row_height < cell.preferred_height && cell.row_span == 1
431
477
  row_height = cell.preferred_height
@@ -462,6 +508,11 @@ module HexaPDF
462
508
  available_height -= cell.preferred_height - row_span_height
463
509
  end
464
510
  end
511
+
512
+ if row_has_split_cells
513
+ split_row_cells = create_split_row_cells(columns)
514
+ break
515
+ end
465
516
  else
466
517
  last_fitted_row_index = columns.min_by(&:row).row - 1 if height != available_height
467
518
  break
@@ -473,6 +524,7 @@ module HexaPDF
473
524
  # final height and top-left corner of each cell needs to be set.
474
525
  running_height = 0
475
526
  @cells[start_row..last_fitted_row_index].each.with_index(start_row) do |columns, row_index|
527
+ columns = @overridden_row_cells if row_index == @overridden_row_index
476
528
  columns.each_with_index do |cell, col_index|
477
529
  next if cell.row != row_index || cell.column != col_index
478
530
  cell.left = column_info[cell.column].first
@@ -488,13 +540,15 @@ module HexaPDF
488
540
  end
489
541
  end
490
542
 
491
- [height - available_height, last_fitted_row_index < start_row ? -1 : last_fitted_row_index]
543
+ [height - available_height, last_fitted_row_index < start_row ? -1 : last_fitted_row_index,
544
+ split_row_cells]
492
545
  end
493
546
 
494
547
  # Draws the rows from +start_row+ to +end_row+ on the given +canvas+, with the top-left
495
548
  # corner of the resulting table being at (+x+, +y+).
496
549
  def draw_rows(start_row, end_row, canvas, x, y)
497
550
  @cells[start_row..end_row].each.with_index(start_row) do |columns, row_index|
551
+ columns = @overridden_row_cells if row_index == @overridden_row_index
498
552
  columns.each_with_index do |cell, col_index|
499
553
  next if cell.row != row_index || cell.column != col_index
500
554
  cell.draw(canvas, x + cell.left, y - cell.top - cell.height)
@@ -557,6 +611,22 @@ module HexaPDF
557
611
  end
558
612
  end
559
613
 
614
+ # Splits all cells in +columns+ and returns the new array.
615
+ def create_split_row_cells(columns)
616
+ columns.map.with_index do |cell, col_index|
617
+ next cell unless cell.column == col_index
618
+ _, split_box = cell.split
619
+ if split_box
620
+ split_box
621
+ else
622
+ # Create an empty clone of the fully fit cell
623
+ empty_cell = cell.send(:create_split_box)
624
+ empty_cell.instance_variable_set(:@children, nil)
625
+ empty_cell
626
+ end
627
+ end
628
+ end
629
+
560
630
  end
561
631
 
562
632
  # The Cells instance containing the data of the table.
@@ -682,6 +752,7 @@ module HexaPDF
682
752
  @special_cells_fit_not_successful = false
683
753
  [@header_cells, @footer_cells].each do |special_cells|
684
754
  next unless special_cells
755
+ # Ignore split-cells (3rd result element) as not fully-fit headers/footers lead to failure
685
756
  special_used_height, last_fitted_row_index = special_cells.fit_rows(0, height, columns, frame)
686
757
  height -= special_used_height
687
758
  used_height += special_used_height
@@ -689,13 +760,14 @@ module HexaPDF
689
760
  return nil if @special_cells_fit_not_successful
690
761
  end
691
762
 
692
- main_used_height, @last_fitted_row_index = @cells.fit_rows(@start_row_index, height, columns, frame)
763
+ main_used_height, @last_fitted_row_index, @split_row_cells =
764
+ @cells.fit_rows(@start_row_index, height, columns, frame)
693
765
  used_height += main_used_height
694
766
 
695
767
  update_content_width { columns[-1].sum + rw }
696
768
  update_content_height { used_height + rh }
697
769
 
698
- if @last_fitted_row_index == @cells.number_of_rows - 1
770
+ if @last_fitted_row_index == @cells.number_of_rows - 1 && !@split_row_cells
699
771
  fit_result.success!
700
772
  elsif @last_fitted_row_index >= 0
701
773
  fit_result.overflow!
@@ -724,8 +796,19 @@ module HexaPDF
724
796
  # Splits the content of the table box. This method is called from Box#split.
725
797
  def split_content
726
798
  box = create_split_box
727
- box.instance_variable_set(:@start_row_index, @last_fitted_row_index + 1)
799
+
800
+ if @split_row_cells
801
+ cells = @cells.clone
802
+ cells.instance_variable_set(:@overridden_row_cells, @split_row_cells)
803
+ cells.instance_variable_set(:@overridden_row_index, @last_fitted_row_index)
804
+ box.instance_variable_set(:@cells, cells)
805
+ box.instance_variable_set(:@start_row_index, @last_fitted_row_index)
806
+ else
807
+ box.instance_variable_set(:@start_row_index, @last_fitted_row_index + 1)
808
+ end
809
+
728
810
  box.instance_variable_set(:@last_fitted_row_index, -1)
811
+ box.instance_variable_set(:@split_row_cells, nil)
729
812
  box.instance_variable_set(:@special_cells_fit_not_successful, nil)
730
813
  header_cells = @header ? Cells.new(@header.call(self), cell_style: @cell_style) : nil
731
814
  box.instance_variable_set(:@header_cells, header_cells)
@@ -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