hexapdf 0.32.2 → 0.33.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -1
  3. data/README.md +9 -0
  4. data/examples/002-graphics.rb +15 -17
  5. data/examples/003-arcs.rb +9 -9
  6. data/examples/009-text_layouter_alignment.rb +1 -1
  7. data/examples/010-text_layouter_inline_boxes.rb +2 -2
  8. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  9. data/examples/012-text_layouter_styling.rb +7 -7
  10. data/examples/013-text_layouter_shapes.rb +1 -1
  11. data/examples/014-text_in_polygon.rb +1 -1
  12. data/examples/015-boxes.rb +8 -7
  13. data/examples/016-frame_automatic_box_placement.rb +2 -2
  14. data/examples/017-frame_text_flow.rb +2 -1
  15. data/examples/018-composer.rb +1 -1
  16. data/examples/020-column_box.rb +2 -1
  17. data/examples/025-table_box.rb +46 -0
  18. data/lib/hexapdf/cli/command.rb +5 -2
  19. data/lib/hexapdf/cli/form.rb +5 -5
  20. data/lib/hexapdf/cli/inspect.rb +3 -3
  21. data/lib/hexapdf/composer.rb +104 -52
  22. data/lib/hexapdf/configuration.rb +44 -39
  23. data/lib/hexapdf/content/canvas.rb +393 -267
  24. data/lib/hexapdf/content/color_space.rb +72 -25
  25. data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
  26. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
  27. data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
  28. data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
  29. data/lib/hexapdf/content/graphic_object.rb +6 -7
  30. data/lib/hexapdf/content/graphics_state.rb +54 -45
  31. data/lib/hexapdf/content/operator.rb +52 -54
  32. data/lib/hexapdf/content/parser.rb +2 -2
  33. data/lib/hexapdf/content/processor.rb +15 -15
  34. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  35. data/lib/hexapdf/content.rb +5 -0
  36. data/lib/hexapdf/dictionary.rb +6 -5
  37. data/lib/hexapdf/dictionary_fields.rb +42 -14
  38. data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
  39. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  40. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
  41. data/lib/hexapdf/digital_signature/signature.rb +6 -6
  42. data/lib/hexapdf/digital_signature/signatures.rb +13 -12
  43. data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
  44. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
  45. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
  46. data/lib/hexapdf/digital_signature/signing.rb +4 -0
  47. data/lib/hexapdf/digital_signature/verification_result.rb +2 -2
  48. data/lib/hexapdf/digital_signature.rb +7 -2
  49. data/lib/hexapdf/document/destinations.rb +12 -11
  50. data/lib/hexapdf/document/files.rb +1 -1
  51. data/lib/hexapdf/document/fonts.rb +1 -1
  52. data/lib/hexapdf/document/layout.rb +167 -39
  53. data/lib/hexapdf/document/pages.rb +3 -2
  54. data/lib/hexapdf/document.rb +89 -55
  55. data/lib/hexapdf/encryption/aes.rb +5 -5
  56. data/lib/hexapdf/encryption/arc4.rb +1 -1
  57. data/lib/hexapdf/encryption/fast_aes.rb +2 -2
  58. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  59. data/lib/hexapdf/encryption/identity.rb +1 -1
  60. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  61. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  62. data/lib/hexapdf/encryption/security_handler.rb +31 -24
  63. data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
  64. data/lib/hexapdf/encryption.rb +7 -2
  65. data/lib/hexapdf/error.rb +18 -0
  66. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  67. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  68. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  69. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  70. data/lib/hexapdf/filter/pass_through.rb +1 -1
  71. data/lib/hexapdf/filter/predictor.rb +1 -1
  72. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  73. data/lib/hexapdf/filter.rb +55 -6
  74. data/lib/hexapdf/font/cmap/parser.rb +2 -2
  75. data/lib/hexapdf/font/cmap.rb +1 -1
  76. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  77. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  78. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
  79. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  80. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  81. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
  82. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  83. data/lib/hexapdf/font/invalid_glyph.rb +3 -0
  84. data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
  85. data/lib/hexapdf/font/type1_wrapper.rb +19 -4
  86. data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
  87. data/lib/hexapdf/font_loader/from_file.rb +5 -5
  88. data/lib/hexapdf/font_loader/standard14.rb +3 -3
  89. data/lib/hexapdf/font_loader.rb +3 -0
  90. data/lib/hexapdf/image_loader/jpeg.rb +2 -2
  91. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  92. data/lib/hexapdf/image_loader/png.rb +2 -2
  93. data/lib/hexapdf/image_loader.rb +1 -1
  94. data/lib/hexapdf/importer.rb +13 -0
  95. data/lib/hexapdf/layout/box.rb +9 -2
  96. data/lib/hexapdf/layout/box_fitter.rb +2 -2
  97. data/lib/hexapdf/layout/column_box.rb +18 -4
  98. data/lib/hexapdf/layout/frame.rb +30 -12
  99. data/lib/hexapdf/layout/image_box.rb +5 -0
  100. data/lib/hexapdf/layout/inline_box.rb +1 -0
  101. data/lib/hexapdf/layout/list_box.rb +17 -1
  102. data/lib/hexapdf/layout/page_style.rb +4 -4
  103. data/lib/hexapdf/layout/style.rb +18 -3
  104. data/lib/hexapdf/layout/table_box.rb +682 -0
  105. data/lib/hexapdf/layout/text_box.rb +5 -3
  106. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  107. data/lib/hexapdf/layout/text_layouter.rb +12 -4
  108. data/lib/hexapdf/layout.rb +1 -0
  109. data/lib/hexapdf/name_tree_node.rb +1 -1
  110. data/lib/hexapdf/number_tree_node.rb +1 -1
  111. data/lib/hexapdf/object.rb +18 -7
  112. data/lib/hexapdf/parser.rb +7 -7
  113. data/lib/hexapdf/pdf_array.rb +1 -1
  114. data/lib/hexapdf/rectangle.rb +1 -1
  115. data/lib/hexapdf/reference.rb +1 -1
  116. data/lib/hexapdf/revision.rb +1 -1
  117. data/lib/hexapdf/revisions.rb +3 -3
  118. data/lib/hexapdf/serializer.rb +15 -15
  119. data/lib/hexapdf/stream.rb +4 -2
  120. data/lib/hexapdf/tokenizer.rb +14 -14
  121. data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
  122. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  123. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  124. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  125. data/lib/hexapdf/type/acro_form/form.rb +1 -1
  126. data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
  127. data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
  128. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  129. data/lib/hexapdf/type/acro_form.rb +1 -1
  130. data/lib/hexapdf/type/action.rb +1 -1
  131. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  132. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  133. data/lib/hexapdf/type/actions/launch.rb +1 -1
  134. data/lib/hexapdf/type/actions/uri.rb +1 -1
  135. data/lib/hexapdf/type/actions.rb +1 -1
  136. data/lib/hexapdf/type/annotation.rb +3 -3
  137. data/lib/hexapdf/type/annotations/link.rb +1 -1
  138. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  139. data/lib/hexapdf/type/annotations/text.rb +1 -1
  140. data/lib/hexapdf/type/annotations/widget.rb +2 -2
  141. data/lib/hexapdf/type/annotations.rb +1 -1
  142. data/lib/hexapdf/type/catalog.rb +1 -1
  143. data/lib/hexapdf/type/cid_font.rb +3 -3
  144. data/lib/hexapdf/type/embedded_file.rb +1 -1
  145. data/lib/hexapdf/type/file_specification.rb +2 -2
  146. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  147. data/lib/hexapdf/type/font_simple.rb +2 -2
  148. data/lib/hexapdf/type/font_type0.rb +3 -3
  149. data/lib/hexapdf/type/font_type3.rb +1 -1
  150. data/lib/hexapdf/type/form.rb +1 -1
  151. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  152. data/lib/hexapdf/type/icon_fit.rb +1 -1
  153. data/lib/hexapdf/type/image.rb +1 -1
  154. data/lib/hexapdf/type/info.rb +1 -1
  155. data/lib/hexapdf/type/mark_information.rb +1 -1
  156. data/lib/hexapdf/type/names.rb +2 -2
  157. data/lib/hexapdf/type/object_stream.rb +2 -1
  158. data/lib/hexapdf/type/outline.rb +1 -1
  159. data/lib/hexapdf/type/outline_item.rb +1 -1
  160. data/lib/hexapdf/type/page.rb +19 -10
  161. data/lib/hexapdf/type/page_label.rb +1 -1
  162. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  163. data/lib/hexapdf/type/resources.rb +1 -1
  164. data/lib/hexapdf/type/trailer.rb +2 -2
  165. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  166. data/lib/hexapdf/type/xref_stream.rb +2 -2
  167. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  168. data/lib/hexapdf/version.rb +1 -1
  169. data/lib/hexapdf/writer.rb +4 -4
  170. data/lib/hexapdf/xref_section.rb +2 -2
  171. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
  172. data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
  173. data/test/hexapdf/content/test_canvas.rb +0 -1
  174. data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
  175. data/test/hexapdf/document/test_files.rb +2 -2
  176. data/test/hexapdf/document/test_layout.rb +98 -0
  177. data/test/hexapdf/encryption/test_security_handler.rb +12 -11
  178. data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
  179. data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
  180. data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
  181. data/test/hexapdf/layout/test_box.rb +1 -1
  182. data/test/hexapdf/layout/test_column_box.rb +65 -21
  183. data/test/hexapdf/layout/test_frame.rb +14 -14
  184. data/test/hexapdf/layout/test_image_box.rb +4 -0
  185. data/test/hexapdf/layout/test_inline_box.rb +5 -0
  186. data/test/hexapdf/layout/test_list_box.rb +40 -6
  187. data/test/hexapdf/layout/test_page_style.rb +3 -2
  188. data/test/hexapdf/layout/test_style.rb +50 -0
  189. data/test/hexapdf/layout/test_table_box.rb +722 -0
  190. data/test/hexapdf/layout/test_text_box.rb +18 -0
  191. data/test/hexapdf/layout/test_text_layouter.rb +4 -0
  192. data/test/hexapdf/test_dictionary_fields.rb +4 -1
  193. data/test/hexapdf/test_document.rb +1 -0
  194. data/test/hexapdf/test_filter.rb +8 -0
  195. data/test/hexapdf/test_importer.rb +9 -0
  196. data/test/hexapdf/test_object.rb +16 -5
  197. data/test/hexapdf/test_stream.rb +7 -0
  198. data/test/hexapdf/test_writer.rb +3 -3
  199. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
  200. data/test/hexapdf/type/acro_form/test_form.rb +4 -3
  201. data/test/hexapdf/type/test_page.rb +18 -4
  202. metadata +17 -8
@@ -71,21 +71,20 @@ module HexaPDF
71
71
  #
72
72
  # == Box Styles
73
73
  #
74
- # All box creation methods accept HexaPDF::Layout::Style objects or names for style objects
75
- # (defined via #style). This allows one to predefine certain styles (like first level heading,
76
- # second level heading, paragraph, ...) and consistently use them throughout the document
77
- # creation process.
74
+ # All box creation methods accept Layout::Style objects or names for style objects (defined via
75
+ # #style). This allows one to predefine certain styles (like first level heading, second level
76
+ # heading, paragraph, ...) and consistently use them throughout the document creation process.
78
77
  #
79
- # One style property, HexaPDF::Layout::Style#font, is handled specially:
78
+ # One style property, Layout::Style#font, is handled specially:
80
79
  #
81
80
  # * If no font is set on a style, the font "Times" is automatically set because otherwise there
82
81
  # would be problems with text drawing operations (font is the only style property that has no
83
82
  # valid default value).
84
83
  #
85
- # * Standard style objects only allow font wrapper objects to be set via the
86
- # HexaPDF::Layout::Style#font method. This class makes usage easier by allowing strings or an
87
- # array [name, options_hash] to be used, like with e.g Content::Canvas#font. So to use
88
- # Helvetica as font, one could just do:
84
+ # * Standard style objects only allow font wrapper objects to be set via the Layout::Style#font
85
+ # method. This class makes usage easier by allowing strings or an array [name, options_hash]
86
+ # to be used, like with e.g Content::Canvas#font. So to use Helvetica as font, one could just
87
+ # do:
89
88
  #
90
89
  # style.font = 'Helvetica'
91
90
  #
@@ -108,11 +107,14 @@ module HexaPDF
108
107
  # text, formatted_text, image, ...:: Any method accepted by the Layout class without the _box
109
108
  # suffix.
110
109
  #
111
- # list, column, ...:: Any name registered for the configuration option +layout.boxes.map+.
110
+ # list, column, ...:: Any name registered with the configuration option +layout.boxes.map+.
111
+ #
112
+ # The special method #multiple allows adding multiple boxes as a single array to the collected
113
+ # children.
112
114
  #
113
115
  # Example:
114
116
  #
115
- # document.layout.box(:list) do |list|
117
+ # document.layout.box(:list) do |list| # list is a ChildrenCollector
116
118
  # list.text_box("Some text here") # layout method
117
119
  # list.image(image_path) # layout method without _box suffix
118
120
  # list.column(columns: 3) do |column| # registered box name
@@ -166,7 +168,7 @@ module HexaPDF
166
168
 
167
169
  end
168
170
 
169
- # The mapping of style name (a Symbol) to HexaPDF::Layout::Style instance.
171
+ # The mapping of style name (a Symbol) to Layout::Style instance.
170
172
  attr_reader :styles
171
173
 
172
174
  # Creates a new Layout object for the given PDF document.
@@ -179,20 +181,20 @@ module HexaPDF
179
181
  # layout.style(name) -> style
180
182
  # layout.style(name, base: :base, **properties) -> style
181
183
  #
182
- # Creates or updates the HexaPDF::Layout::Style object called +name+ with the given property
183
- # values and returns it.
184
+ # Creates or updates the Layout::Style object called +name+ with the given property values and
185
+ # returns it.
186
+ #
187
+ # If neither +base+ nor any style properties are specified, the style +name+ is just returned.
184
188
  #
185
189
  # This method allows convenient access to the stored styles and to update them. Such styles
186
190
  # can then be used by name in the various box creation methods, e.g. #text_box or #image_box.
187
191
  #
188
- # If neither +base+ nor any style properties are specified, the style +name+ is just returned.
189
- #
190
192
  # If the style +name+ does not exist yet and the argument +base+ specifies the name of another
191
193
  # style, that style is duplicated and used as basis for the style. This also means that the
192
194
  # referenced +base+ style needs be defined first!
193
195
  #
194
- # The special name :base should be used for setting the base style which is used when no
195
- # specific style is set.
196
+ # The special name :base should be used for setting the base style which is used for the
197
+ # +base+ argument when no specific style is specified.
196
198
  #
197
199
  # Note that the style property 'font' is handled specially, see the class documentation for
198
200
  # details.
@@ -269,26 +271,26 @@ module HexaPDF
269
271
  #
270
272
  # +style+, +style_properties+::
271
273
  # The box and the text are styled using the given +style+. This can either be a style name
272
- # set via #style or anything HexaPDF::Layout::Style::create accepts. If any additional
274
+ # set via #style or anything Layout::Style::create accepts. If any additional
273
275
  # +style_properties+ are specified, the style is duplicated and the additional styles are
274
276
  # applied.
275
277
  #
276
278
  # +properties+::
277
- # This can be used to set custom properties on the created text box. See Box#properties
278
- # for details and usage.
279
+ # This can be used to set custom properties on the created text box. See
280
+ # Layout::Box#properties for details and usage.
279
281
  #
280
282
  # +box_style+::
281
283
  # Sometimes it is necessary for the box to have a different style than the text, e.g. when
282
284
  # using overlays. In such a case use +box_style+ for specifiying the style of the box (a
283
- # style name set via #style or anything HexaPDF::Layout::Style::create accepts).
285
+ # style name set via #style or anything Layout::Style::create accepts).
284
286
  #
285
287
  # The +style+ together with the +style_properties+ will be used for the text style.
286
288
  #
287
289
  # Examples:
288
290
  #
289
- # layout.text_box("Test " * 15)
291
+ # layout.text_box("Test is on " * 15)
290
292
  # layout.text_box("Now " * 7, width: 100)
291
- # layout.text_box("Another test", font_size: 15, fill_color: "green")
293
+ # layout.text_box("Another test", font_size: 15, fill_color: "hp-blue")
292
294
  # layout.text_box("Different box style", fill_color: 'white', box_style: {
293
295
  # underlays: [->(c, b) { c.rectangle(0, 0, b.content_width, b.content_height).fill }]
294
296
  # })
@@ -307,7 +309,8 @@ module HexaPDF
307
309
  # formatted differently.
308
310
  #
309
311
  # The argument +data+ needs to be an array of String, HexaPDF::Layout::InlineBox and/or Hash
310
- # objects and is transformed so that it is suitable as argument for the text box:
312
+ # objects and is transformed so that it is suitable as argument for the text box
313
+ # initialization method.
311
314
  #
312
315
  # * A String object is treated like {text: data}.
313
316
  #
@@ -322,9 +325,9 @@ module HexaPDF
322
325
  # for the text. If this is set and :box is not, the hash will be transformed into a
323
326
  # text fragment with an appropriate link overlay.
324
327
  #
325
- # style:: The style to be use as base style instead of the style created from the +style+
326
- # and +style_properties+ arguments. See HexaPDF::Layout::Style::create for allowed
327
- # values.
328
+ # style:: The style to use as base style instead of the style created from the +style+ and
329
+ # +style_properties+ arguments. This can either be a style name set via #style or
330
+ # anything HexaPDF::Layout::Style::create allows.
328
331
  #
329
332
  # If any style properties are set, the used style is duplicated and the additional
330
333
  # properties applied.
@@ -336,23 +339,42 @@ module HexaPDF
336
339
  #
337
340
  # The value must be one or more (as an array) positional arguments to be used with the
338
341
  # #inline_box method. The rest of the hash keys are passed as keyword arguments to
339
- # #inline_box except for :block that value of which would be passed as the block.
342
+ # #inline_box except for :block which would be passed as the block.
340
343
  #
341
344
  # See #text_box for details on +width+, +height+, +style+, +style_properties+, +properties+
342
345
  # and +box_style+.
343
346
  #
344
347
  # Examples:
345
348
  #
349
+ # # Text without special styling
346
350
  # layout.formatted_text_box(["Some string"])
347
- # layout.formatted_text_box(["Some ", {text: "string", fill_color: 128}])
348
- # layout.formatted_text_box(["Some ", {link: "https://example.com",
349
- # fill_color: 'blue', text: "Example"}])
350
- # layout.formatted_text_box(["Some ", {text: "string", style: {font_size: 20}}])
351
- # layout.formatted_text_box(["Some ", {box: [:text, "string"], valign: :top}])
351
+ #
352
+ # # A predefined inline box
353
+ # ibox = layout.inline_box(:text, 'Hello')
354
+ # layout.formatted_text_box([ibox])
355
+ #
356
+ # # Text with styling properties
357
+ # layout.formatted_text_box([{text: "string", fill_color: 128}])
358
+ #
359
+ # # Text referencing a base style
360
+ # layout.formatted_text_box([{text: "string", style: :bold}])
361
+ #
362
+ # # Text with a link
363
+ # layout.formatted_text_box([{link: "https://example.com",
364
+ # fill_color: 'blue', text: "Example"}])
365
+ #
366
+ # # Inline boxes created from the given data
367
+ # layout.formatted_text_box([{box: [:text, "string"], valign: :top}])
352
368
  # block = lambda {|list| list.text("First item"); list.text("Second item") }
353
369
  # layout.formatted_text_box(["Some ", {box: :list, item_spacing: 10, block: block}])
354
370
  #
355
- # See: #text_box, HexaPDF::Layout::TextBox, HexaPDF::Layout::TextFragment
371
+ # # Combining the above variants
372
+ # layout.formatted_text_box(["Hello", {box: [:text, 'World!']}, "Here comes a ",
373
+ # {link: 'https://example.com', text: 'link'}, '!',
374
+ # {text: 'And more!', style: :bold, font_size: 20}])
375
+ #
376
+ # See: #text_box, #inline_box, HexaPDF::Layout::TextBox, HexaPDF::Layout::TextFragment,
377
+ # HexaPDF::Layout::InlineBox
356
378
  def formatted_text_box(data, width: 0, height: 0, style: nil, properties: nil, box_style: nil,
357
379
  **style_properties)
358
380
  style = retrieve_style(style, style_properties)
@@ -406,25 +428,131 @@ module HexaPDF
406
428
  properties: properties, style: style)
407
429
  end
408
430
 
431
+ # This helper class is used by Layout#table_box to allow specifying the keyword arguments used
432
+ # when converting cell data to box instances.
433
+ class CellArgumentCollector
434
+
435
+ # Stores a single keyword argument definition for a number of rows/columns.
436
+ ArgumentInfo = Struct.new(:rows, :cols, :args)
437
+
438
+ # Returns all stored ArgumentInfo instances.
439
+ attr_reader :argument_infos
440
+
441
+ # Creates a new instance, providing the number of rows and columns of the table.
442
+ def initialize(number_of_rows, number_of_columns)
443
+ @argument_infos = []
444
+ @number_of_rows = number_of_rows
445
+ @number_of_columns = number_of_columns
446
+ end
447
+
448
+ # Stores the keyword arguments in +args+ for the given 0-based rows and columns which can
449
+ # either be a single number or a range of numbers.
450
+ def []=(rows = 0..-1, cols = 0..-1, args)
451
+ rows = adjust_range(rows.kind_of?(Integer) ? rows..rows : rows, @number_of_rows)
452
+ cols = adjust_range(cols.kind_of?(Integer) ? cols..cols : cols, @number_of_columns)
453
+ @argument_infos << ArgumentInfo.new(rows, cols, args)
454
+ end
455
+
456
+ # Retrieves the merged keyword arguments for the cell in +row+ and +col+.
457
+ #
458
+ # Earlier defined arguments are overridden by later ones.
459
+ def retrieve_arguments_for(row, col)
460
+ @argument_infos.each_with_object({}) do |arg_info, result|
461
+ next unless arg_info.rows.cover?(row) && arg_info.cols.cover?(col)
462
+ result.update(arg_info.args)
463
+ end
464
+ end
465
+
466
+ private
467
+
468
+ # Adjusts the +range+ so that both the begin and the end of the range are zero or positive
469
+ # integers smaller than +max+.
470
+ def adjust_range(range, max)
471
+ (range.begin % max)..(range.end % max)
472
+ end
473
+
474
+ end
475
+
476
+ # Creates a HexaPDF::Layout::TableBox for the given table data.
477
+ #
478
+ # This method is a small wrapper around the actual class and mainly facilitates transforming
479
+ # the contents of the +data+ into the box instances needed by the table box implementation.
480
+ #
481
+ # In addition to everything the table box implementation allows for +data+, it is also
482
+ # possible to specify strings as cell contents. Those strings will be converted to text boxes
483
+ # by using the #text_box method. *Note* that this functionality is *not* available for the
484
+ # header and footer!
485
+ #
486
+ # Additional arguments for the #text_box invocations can be specified using the optional block
487
+ # that yields a CellArgumentCollector instance. This allows customization of the text boxes.
488
+ # By specifying the special key +:cell+ it is also possible to assign style properties to the
489
+ # cells themselves.
490
+ #
491
+ # See HexaPDF::Layout::TableBox::new for details on +column_widths+, +header+, +footer+, and
492
+ # +cell_style+.
493
+ #
494
+ # See #text_box for details on +width+, +height+, +style+, +style_properties+ and
495
+ # +properties+.
496
+ #
497
+ # Examples:
498
+ #
499
+ # layout.table_box([[layout.text('A'), layout.text('B')],
500
+ # [layout.image(image_path), layout.text('D')]]
501
+ # layout.table_box([['A', 'B'], [layout.image(image_path), 'D]]) # same as above
502
+ #
503
+ # layout.table_box([['A', 'B'], ['C', 'D]]) do |args|
504
+ # # assign the predefined style :cell_text to all texts
505
+ # args[] = {style: :cell_text}
506
+ # # row 0 has a grey background and bold text
507
+ # args[0] = {font: ['Helvetica', variant: :bold], cell: {background_color: 'eee'}}
508
+ # # text in last column is right aligned
509
+ # args[0..-1, -1] = {align: :right}
510
+ # end
511
+ #
512
+ # See: HexaPDF::Layout::TableBox
513
+ def table_box(data, column_widths: nil, header: nil, footer: nil, cell_style: nil,
514
+ width: 0, height: 0, style: nil, properties: nil, **style_properties)
515
+ style = retrieve_style(style, style_properties)
516
+ cells = HexaPDF::Layout::TableBox::Cells.new(data, cell_style: cell_style)
517
+ collector = CellArgumentCollector.new(cells.number_of_rows, cells.number_of_columns)
518
+ yield(collector) if block_given?
519
+ cells.style do |cell|
520
+ args = collector.retrieve_arguments_for(cell.row, cell.column)
521
+ cstyle = args.delete(:cell)
522
+ result = case cell.children
523
+ when Array, HexaPDF::Layout::Box
524
+ cell.children
525
+ else
526
+ text_box(cell.children.to_s, **args)
527
+ end
528
+ cell.children = result
529
+ cell.style.update(**cstyle) if cstyle
530
+ end
531
+ box_class_for_name(:table).new(cells: cells, column_widths: column_widths, header: header,
532
+ footer: footer, cell_style: cell_style, width: width,
533
+ height: height, properties: properties, style: style)
534
+ end
535
+
409
536
  LOREM_IPSUM = [ # :nodoc:
410
537
  "Lorem ipsum dolor sit amet, con\u{00AD}sectetur adipis\u{00AD}cing elit, sed " \
411
538
  "do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
412
539
  "Ut enim ad minim veniam, quis nostrud exer\u{00AD}citation ullamco laboris nisi ut " \
413
- "aliquip ex ea commodo consequat. ",
540
+ "aliquip ex ea commodo consequat.",
414
541
  "Duis aute irure dolor in reprehen\u{00AD}derit in voluptate velit esse cillum dolore " \
415
- "eu fugiat nulla pariatur. ",
542
+ "eu fugiat nulla pariatur.",
416
543
  "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " \
417
544
  "mollit anim id est laborum.",
418
545
  ]
419
546
 
420
- # Uses #text_box to create +count+ paragraphs of lorem ipsum text.
547
+ # Uses #text_box to create +count+ paragraphs with +sentences+ number of sentences (1 to 4) of
548
+ # lorem ipsum text.
421
549
  #
422
550
  # The +text_box_properties+ arguments are passed as is to #text_box.
423
551
  def lorem_ipsum_box(sentences: 4, count: 1, **text_box_properties)
424
552
  text_box(([LOREM_IPSUM[0, sentences].join(" ")] * count).join("\n\n"), **text_box_properties)
425
553
  end
426
554
 
427
- BOX_METHOD_NAMES = [:text, :formatted_text, :image, :lorem_ipsum] #:nodoc:
555
+ BOX_METHOD_NAMES = [:text, :formatted_text, :image, :table, :lorem_ipsum] #:nodoc:
428
556
 
429
557
  # Allows creating boxes using more convenient method names:
430
558
  #
@@ -39,7 +39,7 @@ require 'hexapdf/error'
39
39
  module HexaPDF
40
40
  class Document
41
41
 
42
- # This class provides methods for managing the pages of a PDF file.
42
+ # This class provides methods for managing the pages and page labels of a PDF file.
43
43
  #
44
44
  # For page manipulation it uses the methods of HexaPDF::Type::PageTreeNode underneath but
45
45
  # provides a more convenient interface.
@@ -178,7 +178,8 @@ module HexaPDF
178
178
  @document.catalog.pages.each_page(&block)
179
179
  end
180
180
 
181
- # Returns the number of pages in the PDF document. May be zero if the document has no pages.
181
+ # Returns the number of pages in the PDF document. May be zero if the document has no pages
182
+ # yet.
182
183
  def count
183
184
  @document.catalog.pages.page_count
184
185
  end