hexapdf 0.32.1 → 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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -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/cli.rb +4 -0
  22. data/lib/hexapdf/composer.rb +104 -52
  23. data/lib/hexapdf/configuration.rb +44 -39
  24. data/lib/hexapdf/content/canvas.rb +393 -267
  25. data/lib/hexapdf/content/color_space.rb +72 -25
  26. data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
  27. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
  28. data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
  29. data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
  30. data/lib/hexapdf/content/graphic_object.rb +6 -7
  31. data/lib/hexapdf/content/graphics_state.rb +54 -45
  32. data/lib/hexapdf/content/operator.rb +52 -54
  33. data/lib/hexapdf/content/parser.rb +2 -2
  34. data/lib/hexapdf/content/processor.rb +15 -15
  35. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  36. data/lib/hexapdf/content.rb +5 -0
  37. data/lib/hexapdf/dictionary.rb +6 -5
  38. data/lib/hexapdf/dictionary_fields.rb +42 -14
  39. data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
  40. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  41. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
  42. data/lib/hexapdf/digital_signature/signature.rb +6 -6
  43. data/lib/hexapdf/digital_signature/signatures.rb +13 -12
  44. data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
  45. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
  46. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
  47. data/lib/hexapdf/digital_signature/signing.rb +4 -0
  48. data/lib/hexapdf/digital_signature/verification_result.rb +2 -2
  49. data/lib/hexapdf/digital_signature.rb +7 -2
  50. data/lib/hexapdf/document/destinations.rb +12 -11
  51. data/lib/hexapdf/document/files.rb +1 -1
  52. data/lib/hexapdf/document/fonts.rb +1 -1
  53. data/lib/hexapdf/document/layout.rb +167 -39
  54. data/lib/hexapdf/document/pages.rb +3 -2
  55. data/lib/hexapdf/document.rb +89 -55
  56. data/lib/hexapdf/encryption/aes.rb +5 -5
  57. data/lib/hexapdf/encryption/arc4.rb +1 -1
  58. data/lib/hexapdf/encryption/fast_aes.rb +2 -2
  59. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  60. data/lib/hexapdf/encryption/identity.rb +1 -1
  61. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  62. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  63. data/lib/hexapdf/encryption/security_handler.rb +31 -24
  64. data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
  65. data/lib/hexapdf/encryption.rb +7 -2
  66. data/lib/hexapdf/error.rb +18 -0
  67. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  68. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  69. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  70. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  71. data/lib/hexapdf/filter/pass_through.rb +1 -1
  72. data/lib/hexapdf/filter/predictor.rb +1 -1
  73. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  74. data/lib/hexapdf/filter.rb +55 -6
  75. data/lib/hexapdf/font/cmap/parser.rb +2 -2
  76. data/lib/hexapdf/font/cmap.rb +1 -1
  77. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  78. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  79. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
  80. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  81. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  82. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
  83. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  84. data/lib/hexapdf/font/invalid_glyph.rb +3 -0
  85. data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
  86. data/lib/hexapdf/font/type1_wrapper.rb +19 -4
  87. data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
  88. data/lib/hexapdf/font_loader/from_file.rb +5 -5
  89. data/lib/hexapdf/font_loader/standard14.rb +3 -3
  90. data/lib/hexapdf/font_loader.rb +3 -0
  91. data/lib/hexapdf/image_loader/jpeg.rb +2 -2
  92. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  93. data/lib/hexapdf/image_loader/png.rb +2 -2
  94. data/lib/hexapdf/image_loader.rb +1 -1
  95. data/lib/hexapdf/importer.rb +13 -0
  96. data/lib/hexapdf/layout/box.rb +9 -2
  97. data/lib/hexapdf/layout/box_fitter.rb +2 -2
  98. data/lib/hexapdf/layout/column_box.rb +18 -4
  99. data/lib/hexapdf/layout/frame.rb +30 -12
  100. data/lib/hexapdf/layout/image_box.rb +5 -0
  101. data/lib/hexapdf/layout/inline_box.rb +1 -0
  102. data/lib/hexapdf/layout/list_box.rb +17 -1
  103. data/lib/hexapdf/layout/page_style.rb +4 -4
  104. data/lib/hexapdf/layout/style.rb +18 -3
  105. data/lib/hexapdf/layout/table_box.rb +682 -0
  106. data/lib/hexapdf/layout/text_box.rb +5 -3
  107. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  108. data/lib/hexapdf/layout/text_layouter.rb +12 -4
  109. data/lib/hexapdf/layout.rb +1 -0
  110. data/lib/hexapdf/name_tree_node.rb +1 -1
  111. data/lib/hexapdf/number_tree_node.rb +1 -1
  112. data/lib/hexapdf/object.rb +18 -7
  113. data/lib/hexapdf/parser.rb +8 -8
  114. data/lib/hexapdf/pdf_array.rb +1 -1
  115. data/lib/hexapdf/rectangle.rb +1 -1
  116. data/lib/hexapdf/reference.rb +1 -1
  117. data/lib/hexapdf/revision.rb +1 -1
  118. data/lib/hexapdf/revisions.rb +3 -3
  119. data/lib/hexapdf/serializer.rb +15 -15
  120. data/lib/hexapdf/stream.rb +4 -2
  121. data/lib/hexapdf/tokenizer.rb +14 -14
  122. data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
  123. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  124. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  125. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  126. data/lib/hexapdf/type/acro_form/form.rb +1 -1
  127. data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
  128. data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
  129. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  130. data/lib/hexapdf/type/acro_form.rb +1 -1
  131. data/lib/hexapdf/type/action.rb +1 -1
  132. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  133. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  134. data/lib/hexapdf/type/actions/launch.rb +1 -1
  135. data/lib/hexapdf/type/actions/uri.rb +1 -1
  136. data/lib/hexapdf/type/actions.rb +1 -1
  137. data/lib/hexapdf/type/annotation.rb +3 -3
  138. data/lib/hexapdf/type/annotations/link.rb +1 -1
  139. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  140. data/lib/hexapdf/type/annotations/text.rb +1 -1
  141. data/lib/hexapdf/type/annotations/widget.rb +2 -2
  142. data/lib/hexapdf/type/annotations.rb +1 -1
  143. data/lib/hexapdf/type/catalog.rb +1 -1
  144. data/lib/hexapdf/type/cid_font.rb +3 -3
  145. data/lib/hexapdf/type/embedded_file.rb +1 -1
  146. data/lib/hexapdf/type/file_specification.rb +2 -2
  147. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  148. data/lib/hexapdf/type/font_simple.rb +2 -2
  149. data/lib/hexapdf/type/font_type0.rb +3 -3
  150. data/lib/hexapdf/type/font_type3.rb +1 -1
  151. data/lib/hexapdf/type/form.rb +1 -1
  152. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  153. data/lib/hexapdf/type/icon_fit.rb +1 -1
  154. data/lib/hexapdf/type/image.rb +1 -1
  155. data/lib/hexapdf/type/info.rb +1 -1
  156. data/lib/hexapdf/type/mark_information.rb +1 -1
  157. data/lib/hexapdf/type/names.rb +2 -2
  158. data/lib/hexapdf/type/object_stream.rb +7 -3
  159. data/lib/hexapdf/type/outline.rb +1 -1
  160. data/lib/hexapdf/type/outline_item.rb +1 -1
  161. data/lib/hexapdf/type/page.rb +19 -10
  162. data/lib/hexapdf/type/page_label.rb +1 -1
  163. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  164. data/lib/hexapdf/type/resources.rb +1 -1
  165. data/lib/hexapdf/type/trailer.rb +2 -2
  166. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  167. data/lib/hexapdf/type/xref_stream.rb +2 -2
  168. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  169. data/lib/hexapdf/version.rb +1 -1
  170. data/lib/hexapdf/writer.rb +4 -4
  171. data/lib/hexapdf/xref_section.rb +2 -2
  172. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
  173. data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
  174. data/test/hexapdf/content/test_canvas.rb +0 -1
  175. data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
  176. data/test/hexapdf/document/test_files.rb +2 -2
  177. data/test/hexapdf/document/test_layout.rb +98 -0
  178. data/test/hexapdf/encryption/test_security_handler.rb +12 -11
  179. data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
  180. data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
  181. data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
  182. data/test/hexapdf/layout/test_box.rb +1 -1
  183. data/test/hexapdf/layout/test_column_box.rb +65 -21
  184. data/test/hexapdf/layout/test_frame.rb +14 -14
  185. data/test/hexapdf/layout/test_image_box.rb +4 -0
  186. data/test/hexapdf/layout/test_inline_box.rb +5 -0
  187. data/test/hexapdf/layout/test_list_box.rb +40 -6
  188. data/test/hexapdf/layout/test_page_style.rb +3 -2
  189. data/test/hexapdf/layout/test_style.rb +50 -0
  190. data/test/hexapdf/layout/test_table_box.rb +722 -0
  191. data/test/hexapdf/layout/test_text_box.rb +18 -0
  192. data/test/hexapdf/layout/test_text_layouter.rb +4 -0
  193. data/test/hexapdf/test_dictionary_fields.rb +4 -1
  194. data/test/hexapdf/test_document.rb +1 -0
  195. data/test/hexapdf/test_filter.rb +8 -0
  196. data/test/hexapdf/test_importer.rb +9 -0
  197. data/test/hexapdf/test_object.rb +16 -5
  198. data/test/hexapdf/test_parser.rb +1 -1
  199. data/test/hexapdf/test_stream.rb +7 -0
  200. data/test/hexapdf/test_writer.rb +3 -3
  201. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
  202. data/test/hexapdf/type/acro_form/test_form.rb +4 -3
  203. data/test/hexapdf/type/test_object_stream.rb +9 -3
  204. data/test/hexapdf/type/test_page.rb +18 -4
  205. metadata +17 -8
@@ -63,7 +63,7 @@ module HexaPDF
63
63
  # The PDF operators themselves are implemented as classes, see Operator. The canvas class uses
64
64
  # the Operator::BaseOperator#invoke and Operator::BaseOperator#serialize methods for applying
65
65
  # changes and serialization, with one exception: color setters don't invoke the corresponding
66
- # operator implementation but directly work on the graphics state.
66
+ # operator implementation but work directly on the graphics state.
67
67
  #
68
68
  # === General Graphics State Manipulation Methods
69
69
  #
@@ -161,7 +161,7 @@ module HexaPDF
161
161
  # showing operators, and such groups of operators are allowed to be used only in certain
162
162
  # graphics objects or the page description level.
163
163
  #
164
- # Have a look at the PDF specification (PDF1.7 s8.2) for more details.
164
+ # Have a look at the PDF specification (PDF2.0 s8.2) for more details.
165
165
  #
166
166
  # HexaPDF tries to ensure the proper use of the operators and graphics objects and if it
167
167
  # cannot do it, an error is raised. So if you don't modify a content stream directly but via
@@ -169,15 +169,20 @@ module HexaPDF
169
169
  #
170
170
  # === Graphics State
171
171
  #
172
- # Some operators modify the so called graphics state (see Content::GraphicsState). The graphics
173
- # state is a collection of settings that is used during processing or creating a content stream.
174
- # For example, the path painting operators don't have operands to specify the line width or the
172
+ # Some operators modify the so called graphics state (see GraphicsState). The graphics state is
173
+ # a collection of settings that is used during processing or creating a content stream. For
174
+ # example, the path painting operators don't have operands to specify the line width or the
175
175
  # stroke color but take this information from the graphics state.
176
176
  #
177
177
  # One important thing about the graphics state is that it is only possible to restore a prior
178
178
  # state using the save and restore methods. It is not possible to reset the graphics state
179
179
  # while creating the content stream!
180
180
  #
181
+ # This means, for example, if you use a clipping path (see #clip_path) you should first save the
182
+ # graphics state (#save_graphics_state) and then restore it afterwards
183
+ # (#restore_graphics_state). Otherwise all following operations will be clipped to the clipping
184
+ # path.
185
+ #
181
186
  # === Paths
182
187
  #
183
188
  # A PDF path object consists of one or more subpaths. Each subpath can be a rectangle or can
@@ -200,7 +205,7 @@ module HexaPDF
200
205
  # In addition filling may be done using either the nonzero winding number rule or the even-odd
201
206
  # rule.
202
207
  #
203
- # See: PDF1.7 s8, s9
208
+ # See: PDF2.0 s8, s9
204
209
  class Canvas
205
210
 
206
211
  include HexaPDF::Utils::MathHelpers
@@ -208,12 +213,20 @@ module HexaPDF
208
213
 
209
214
  # The context for which the canvas was created (a HexaPDF::Type::Page or HexaPDF::Type::Form
210
215
  # object).
216
+ #
217
+ # The context object is used for two things:
218
+ #
219
+ # * To store the resources (#resources) that are needed by the canvas (e.g. font references).
220
+ #
221
+ # * To access the HexaPDF::Document object to which this canvas and the context object
222
+ # belongs. This is used internally but it is also useful in other situations since some
223
+ # parts of HexaPDF only yield a canvas object, and not also the underlying document object.
211
224
  attr_reader :context
212
225
 
213
226
  # The serialized contents produced by the various canvas operations up to this point.
214
227
  #
215
228
  # Note that the returned string may not be a completely valid PDF content stream since a
216
- # graphic object may be open or the graphics state not completely restored.
229
+ # graphic object may be open or the graphics state may not be completely restored.
217
230
  #
218
231
  # See: #stream_data
219
232
  attr_reader :contents
@@ -228,7 +241,7 @@ module HexaPDF
228
241
  # canvas!
229
242
  attr_reader :stream_data
230
243
 
231
- # The Content::GraphicsState object containing the current graphics state.
244
+ # The GraphicsState object containing the current graphics state.
232
245
  #
233
246
  # The graphics state must not be changed directly, only by using the provided methods. If it
234
247
  # is changed directly, the output will not be correct.
@@ -236,7 +249,7 @@ module HexaPDF
236
249
 
237
250
  # The current graphics object.
238
251
  #
239
- # The graphics object should not be changed directly. It is automatically updated by the
252
+ # The graphics object should not be changed directly! It is automatically updated by the
240
253
  # invoked methods.
241
254
  #
242
255
  # This attribute can have the following values:
@@ -246,13 +259,13 @@ module HexaPDF
246
259
  # :clipping_path:: The current graphics object is a clipping path.
247
260
  # :text:: The current graphics object is a text object.
248
261
  #
249
- # See: PDF1.7 s8.2
262
+ # See: PDF2.0 s8.2
250
263
  attr_accessor :graphics_object
251
264
 
252
265
  # The current point [x, y] of the path.
253
266
  #
254
- # This attribute holds the current point which is only valid if the current graphics objects
255
- # is :path.
267
+ # This attribute holds the current point which is only valid if the current graphics object
268
+ # (see #graphic_object) is :path.
256
269
  #
257
270
  # When the current point changes, the array is modified in place instead of creating a new
258
271
  # array!
@@ -260,7 +273,8 @@ module HexaPDF
260
273
 
261
274
  # The operator name/implementation map used when invoking or serializing an operator.
262
275
  #
263
- # Defaults to Content::Operator::DEFAULT_OPERATORS.
276
+ # Defaults to Operator::DEFAULT_OPERATORS, i.e. the standard implementation provided by
277
+ # HexaPDF.
264
278
  attr_reader :operators
265
279
 
266
280
  # Creates a new Canvas object for the given context object (either a HexaPDF::Type::Page or a
@@ -285,7 +299,7 @@ module HexaPDF
285
299
  @current_point = [0, 0]
286
300
  @start_point = [0, 0]
287
301
  @contents = ''.b
288
- @stream_data = HexaPDF::StreamData.new do
302
+ source = HexaPDF::Filter.source_from_proc do
289
303
  case graphics_object
290
304
  when :path, :clipping_path then end_path
291
305
  when :text then end_text
@@ -293,6 +307,7 @@ module HexaPDF
293
307
  restore_graphics_state while graphics_state.saved_states?
294
308
  @contents
295
309
  end
310
+ @stream_data = HexaPDF::StreamData.new(source)
296
311
  end
297
312
 
298
313
  # Returns the resource dictionary of the context object.
@@ -306,7 +321,7 @@ module HexaPDF
306
321
  # canvas.save_graphics_state => canvas
307
322
  # canvas.save_graphics_state { block } => canvas
308
323
  #
309
- # Saves the current graphics state and returns self.
324
+ # Saves the current graphics state and returns +self+.
310
325
  #
311
326
  # If invoked without a block a corresponding call to #restore_graphics_state must be done to
312
327
  # ensure proper nesting. Otherwise, i.e. when invoked with a block, the graphics state is
@@ -320,7 +335,7 @@ module HexaPDF
320
335
  # #>pdf
321
336
  # # With a block
322
337
  # canvas.save_graphics_state do
323
- # canvas.stroke_color("red") # After the block the color is reset
338
+ # canvas.stroke_color("hp-blue") # After the block the color is reset
324
339
  # canvas.line(20, 20, 70, 180).stroke
325
340
  # end
326
341
  # canvas.line(60, 20, 110, 180).stroke
@@ -332,7 +347,7 @@ module HexaPDF
332
347
  # restore_graphics_state
333
348
  # canvas.line(140, 20, 190, 180).stroke
334
349
  #
335
- # See: PDF1.7 s8.4.2, #restore_graphics_state
350
+ # See: PDF2.0 s8.4.2, #restore_graphics_state
336
351
  def save_graphics_state
337
352
  raise_unless_at_page_description_level
338
353
  invoke0(:q)
@@ -347,11 +362,20 @@ module HexaPDF
347
362
  # :call-seq:
348
363
  # canvas.restore_graphics_state => canvas
349
364
  #
350
- # Restores the current graphics state and returns self.
365
+ # Restores the graphics state to the last saved version and returns +self+.
351
366
  #
352
367
  # Must not be invoked more times than #save_graphics_state.
353
368
  #
354
- # See: PDF1.7 s8.4.2, #save_graphics_state
369
+ # Example:
370
+ #
371
+ # #>pdf
372
+ # canvas.save_graphics_state
373
+ # canvas.circle(100, 100, 50).clip_path.end_path
374
+ # canvas.fill_color("hp-blue").rectangle(0, 0, 100, 100).fill
375
+ # canvas.restore_graphics_state
376
+ # canvas.rectangle(100, 0, 100, 100).fill
377
+ #
378
+ # See: PDF2.0 s8.4.2, #save_graphics_state
355
379
  def restore_graphics_state
356
380
  raise_unless_at_page_description_level
357
381
  invoke0(:Q)
@@ -363,7 +387,7 @@ module HexaPDF
363
387
  # canvas.transform(a, b, c, d, e, f) => canvas
364
388
  # canvas.transform(a, b, c, d, e, f) { block } => canvas
365
389
  #
366
- # Transforms the user space by applying the given matrix to the current transformation
390
+ # Transforms the coordinate system by applying the given matrix to the current transformation
367
391
  # matrix and returns self.
368
392
  #
369
393
  # If invoked with a block, the transformation is only active during the block by saving and
@@ -375,16 +399,16 @@ module HexaPDF
375
399
  # c d 0
376
400
  # e f 1
377
401
  #
378
- # Examples:
402
+ # Example:
379
403
  #
380
404
  # #>pdf
381
405
  # canvas.transform(1, 0, 0, 1, 100, 100) do # Translate origin to (100, 100)
382
- # canvas.stroke_color("red").
406
+ # canvas.stroke_color("hp-blue").
383
407
  # line(0, 0, 100, 50).stroke # Actually from (100, 100) to (200, 150)
384
408
  # end
385
409
  # canvas.line(0, 0, 100, 50).stroke # Really from (0, 0) to (100, 50)
386
410
  #
387
- # See: PDF1.7 s8.3, s8.4.4
411
+ # See: PDF2.0 s8.3, s8.4.4
388
412
  def transform(a, b, c, d, e, f)
389
413
  raise_unless_at_page_description_level
390
414
  save_graphics_state if block_given?
@@ -400,30 +424,31 @@ module HexaPDF
400
424
  # canvas.rotate(angle, origin: nil) => canvas
401
425
  # canvas.rotate(angle, origin: nil) { block } => canvas
402
426
  #
403
- # Rotates the user space +angle+ degrees around the coordinate system origin or around the
404
- # given point and returns self.
427
+ # Rotates the coordinate system +angle+ degrees around the origin or around the given point
428
+ # and returns +self+.
405
429
  #
406
- # If invoked with a block, the rotation of the user space is only active during the block by
407
- # saving and restoring the graphics state.
430
+ # If invoked with a block, the rotation of the coordinate system is only active during the
431
+ # block by saving and restoring the graphics state.
408
432
  #
409
433
  # Note that the origin of the coordinate system itself doesn't change even if the +origin+
410
434
  # argument is given!
411
435
  #
412
436
  # origin::
413
- # The point around which the user space should be rotated.
437
+ # The point around which the coordinate system should be rotated.
414
438
  #
415
439
  # Examples:
416
440
  #
417
441
  # #>pdf-center
418
- # canvas.stroke_color("lightgrey").
419
- # rectangle(0, 0, 60, 40).stroke
442
+ # canvas.stroke_color("hp-gray-light").
443
+ # rectangle(0, 0, 60, 40).stroke # The rectangle that gets rotated
444
+ #
420
445
  # canvas.rotate(45) do # Positive x-axis pointing to top-right corner
421
- # canvas.stroke_color("red").
446
+ # canvas.stroke_color("hp-blue").
422
447
  # rectangle(0, 0, 60, 40).stroke
423
448
  # end
424
449
  #
425
450
  # canvas.rotate(-45, origin: [-50, -50]) do # Rotate around (-50,-50)
426
- # canvas.stroke_color("blue").
451
+ # canvas.stroke_color("hp-orange").
427
452
  # rectangle(0, 0, 60, 40).stroke
428
453
  # end
429
454
  #
@@ -443,8 +468,8 @@ module HexaPDF
443
468
  # canvas.scale(sx, sy = sx, origin: nil) => canvas
444
469
  # canvas.scale(sx, sy = sx, origin: nil) { block } => canvas
445
470
  #
446
- # Scales the user space +sx+ units in the horizontal and +sy+ units in the vertical
447
- # direction and returns self. If the optional +origin+ is specified, scaling is done from
471
+ # Scales the coordinate system +sx+ units in the horizontal and +sy+ units in the vertical
472
+ # direction and returns +self+. If the optional +origin+ is specified, scaling is done from
448
473
  # that point.
449
474
  #
450
475
  # If invoked with a block, the scaling is only active during the block by saving and
@@ -454,20 +479,21 @@ module HexaPDF
454
479
  # argument is given!
455
480
  #
456
481
  # origin::
457
- # The point from which the user space should be scaled.
482
+ # The point from which the coordinate system should be scaled.
458
483
  #
459
484
  # Examples:
460
485
  #
461
486
  # #>pdf-center
462
- # canvas.stroke_color("lightgrey").
463
- # rectangle(10, 10, 10, 10).stroke
487
+ # canvas.stroke_color("hp-gray-light").
488
+ # rectangle(10, 10, 10, 10).stroke # The rectangle that gets scaled
489
+ #
464
490
  # canvas.scale(4, 2) do # Scale from origin
465
- # canvas.stroke_color("blue").
491
+ # canvas.stroke_color("hp-blue").
466
492
  # rectangle(10, 10, 10, 10).stroke # Actually (40, 20) to (80, 40)
467
493
  # end
468
494
  #
469
495
  # canvas.scale(-2, 4, origin: [10, 10]) do # Scale from (10, 10)
470
- # canvas.stroke_color("red").
496
+ # canvas.stroke_color("hp-orange").
471
497
  # rectangle(10, 10, 10, 10).stroke # Actually (10, 10) to (-10, 40)
472
498
  # end
473
499
  #
@@ -484,18 +510,19 @@ module HexaPDF
484
510
  # canvas.translate(x, y) => canvas
485
511
  # canvas.translate(x, y) { block } => canvas
486
512
  #
487
- # Translates the user space coordinate system origin to the given +x+ and +y+ coordinates
488
- # and returns self.
513
+ # Translates the coordinate system coordinate system origin to the given +x+ and +y+
514
+ # coordinates and returns +self+.
489
515
  #
490
- # If invoked with a block, the translation of the user space is only active during the block
491
- # by saving and restoring the graphics state.
516
+ # If invoked with a block, the translation of the coordinate system is only active during the
517
+ # block by saving and restoring the graphics state.
492
518
  #
493
519
  # Examples:
494
520
  #
495
521
  # #>pdf-center
496
- # canvas.rectangle(0, 0, 40, 20).stroke # Rectangle from (0, 0) to (40, 20)
522
+ # canvas.stroke_color("hp-gray-light").
523
+ # rectangle(0, 0, 40, 20).stroke # Rectangle from (0, 0) to (40, 20)
497
524
  # canvas.translate(50, 50) do # Origin is now at (50, 50)
498
- # canvas.stroke_color("red").
525
+ # canvas.stroke_color("hp-blue").
499
526
  # rectangle(0, 0, 40, 20).stroke # Actually (50, 50) to (90, 70)
500
527
  # end
501
528
  #
@@ -508,7 +535,7 @@ module HexaPDF
508
535
  # canvas.skew(a, b, origin: nil) => canvas
509
536
  # canvas.skew(a, b, origin: nil) { block } => canvas
510
537
  #
511
- # Skews the the x-axis by +a+ degrees and the y-axis by +b+ degress and returns self. If the
538
+ # Skews the the x-axis by +a+ degrees and the y-axis by +b+ degress and returns +self+. If the
512
539
  # optional +origin+ is specified, skewing is done from that point.
513
540
  #
514
541
  # If invoked with a block, the skewing is only active during the block by saving and
@@ -522,15 +549,16 @@ module HexaPDF
522
549
  # Examples:
523
550
  #
524
551
  # #>pdf-center
525
- # canvas.stroke_color("lightgrey").
526
- # rectangle(10, 10, 40, 20).stroke
552
+ # canvas.stroke_color("hp-gray-light").
553
+ # rectangle(10, 10, 40, 20).stroke # The rectangle that gets skewed
554
+ #
527
555
  # canvas.skew(0, 30) do # Point (10, 10) is now actually (15, 10)
528
- # canvas.stroke_color("blue").
556
+ # canvas.stroke_color("hp-blue").
529
557
  # rectangle(10, 10, 40, 20).stroke # Now a parallelogram
530
558
  # end
531
559
  #
532
560
  # canvas.skew(30, 30, origin: [-50, 50]) do # Skew from (-50, 50)
533
- # canvas.stroke_color("red").
561
+ # canvas.stroke_color("hp-orange").
534
562
  # rectangle(-50, 50, 20, 20).stroke
535
563
  # end
536
564
  #
@@ -553,9 +581,15 @@ module HexaPDF
553
581
  #
554
582
  # The line width determines the thickness of a stroked path.
555
583
  #
556
- # Returns the current line width (see Content::GraphicsState#line_width) when no argument is
557
- # given. Otherwise sets the line width to the given +width+ and returns self. The setter
558
- # version can also be called in the line_width= form.
584
+ # Note that half the line width lies on either side of the path. For example, if a path from
585
+ # (0, 0) to (0, 100) is drawn with a line width of 20, the stroked path is actually 20 units
586
+ # wide, from -10 to 10. And if a rectangle is drawn stroked, but not filled, from (20, 20)
587
+ # with a width and height of 20 and a line width of 10, the "inside" of the rectangle would
588
+ # only be from (25, 25) to (35, 35). Also see the examples below.
589
+ #
590
+ # Returns the current line width (see GraphicsState#line_width) when no argument is given.
591
+ # Otherwise sets the line width to the given +width+ and returns +self+. The setter version
592
+ # can also be called in the line_width= form.
559
593
  #
560
594
  # If the +width+ and a block are provided, the changed line width is only active during the
561
595
  # block by saving and restoring the graphics state.
@@ -564,19 +598,22 @@ module HexaPDF
564
598
  #
565
599
  # #>pdf
566
600
  # canvas.line_width(10).
567
- # line(10, 10, 10, 190).stroke
601
+ # line(10, 100, 10, 190).stroke
568
602
  # canvas.line_width # => 10
569
603
  # canvas.line_width = 5 # => 5
570
- # canvas.line(60, 10, 60, 190).stroke
604
+ # canvas.line(60, 100, 60, 190).stroke
571
605
  #
572
606
  # canvas.line_width(10) do
573
607
  # canvas.line_width # => 10
574
- # canvas.line(110, 10, 110, 190).stroke
608
+ # canvas.line(110, 100, 110, 190).stroke
575
609
  # end
576
610
  # canvas.line_width # => 5
577
- # canvas.line(160, 10, 160, 190).stroke
611
+ # canvas.line(160, 100, 160, 190).stroke
578
612
  #
579
- # See: PDF1.7 s8.4.3.2
613
+ # canvas.line_width(10).rectangle(20, 20, 20, 20).stroke # The rectangle
614
+ # canvas.fill_color("hp-blue").rectangle(25, 25, 10, 10).fill # The inside
615
+ #
616
+ # See: PDF2.0 s8.4.3.2
580
617
  def line_width(width = nil, &block)
581
618
  gs_getter_setter(:line_width, :w, width, &block)
582
619
  end
@@ -587,7 +624,7 @@ module HexaPDF
587
624
  # canvas.line_cap_style(style) => canvas
588
625
  # canvas.line_cap_style(style) { block } => canvas
589
626
  #
590
- # The line cap style specifies how the ends of stroked open paths should look like.
627
+ # The line cap style specifies how the ends of stroked, open paths should look like.
591
628
  #
592
629
  # The +style+ parameter can be one of (also see LineCapStyle):
593
630
  #
@@ -598,11 +635,11 @@ module HexaPDF
598
635
  # :projecting_square or 2::
599
636
  # The stroke continues half the line width beyond the endpoint of a path.
600
637
  #
601
- # Note that the return value is always a normalized line cap style.
638
+ # Note that the return value is always a normalized line cap style (i.e. a NamedValue).
602
639
  #
603
- # Returns the current line cap style (see Content::GraphicsState#line_cap_style) when no
604
- # argument is given. Otherwise sets the line cap style to the given +style+ and returns self.
605
- # The setter version can also be called in the line_cap_style= form.
640
+ # Returns the current line cap style (see GraphicsState#line_cap_style) when no argument is
641
+ # given. Otherwise sets the line cap style to the given +style+ and returns +self+. The setter
642
+ # version can also be called in the line_cap_style= form.
606
643
  #
607
644
  # If the +style+ and a block are provided, the changed line cap style is only active during
608
645
  # the block by saving and restoring the graphics state.
@@ -628,7 +665,7 @@ module HexaPDF
628
665
  # line(50 + index * 50, 30, 50 + index * 50, 170).stroke
629
666
  # end
630
667
  #
631
- # See: PDF1.7 s8.4.3.3, Content::LineCapStyle
668
+ # See: PDF2.0 s8.4.3.3, Content::LineCapStyle
632
669
  def line_cap_style(style = nil, &block)
633
670
  gs_getter_setter(:line_cap_style, :J, style && LineCapStyle.normalize(style), &block)
634
671
  end
@@ -651,11 +688,11 @@ module HexaPDF
651
688
  # The two segments are finished with butt caps and the space between the ends is filled
652
689
  # with a triangle.
653
690
  #
654
- # Note that the return value is always a normalized line join style.
691
+ # Note that the return value is always a normalized line join style (i.e. a NamedValue).
655
692
  #
656
- # Returns the current line join style (see Content::GraphicsState#line_join_style) when no
657
- # argument is given. Otherwise sets the line join style to the given +style+ and returns self.
658
- # The setter version can also be called in the line_join_style= form.
693
+ # Returns the current line join style (see GraphicsState#line_join_style) when no argument is
694
+ # given. Otherwise sets the line join style to the given +style+ and returns +self+. The
695
+ # setter version can also be called in the line_join_style= form.
659
696
  #
660
697
  # If the +style+ and a block are provided, the changed line join style is only active during
661
698
  # the block by saving and restoring the graphics state.
@@ -681,7 +718,7 @@ module HexaPDF
681
718
  # polyline(20 + index * 60, 30, 40 + index * 60, 170, 60 + index * 60, 30).stroke
682
719
  # end
683
720
  #
684
- # See: PDF1.7 s8.4.3.4, Content::LineJoinStyle
721
+ # See: PDF2.0 s8.4.3.4, Content::LineJoinStyle
685
722
  def line_join_style(style = nil, &block)
686
723
  gs_getter_setter(:line_join_style, :j, style && LineJoinStyle.normalize(style), &block)
687
724
  end
@@ -696,9 +733,9 @@ module HexaPDF
696
733
  # mitered line joins (see #line_join_style). When the limit is exceeded, a bevel join is
697
734
  # used instead of a miter join.
698
735
  #
699
- # Returns the current miter limit (see Content::GraphicsState#miter_limit) when no argument is
700
- # given. Otherwise sets the miter limit to the given +limit+ and returns self. The setter
701
- # version can also be called in the miter_limit= form.
736
+ # Returns the current miter limit (see GraphicsState#miter_limit) when no argument is given.
737
+ # Otherwise sets the miter limit to the given +limit+ and returns +self+. The setter version
738
+ # can also be called in the miter_limit= form.
702
739
  #
703
740
  # If the +limit+ and a block are provided, the changed miter limit is only active during the
704
741
  # block by saving and restoring the graphics state.
@@ -722,7 +759,7 @@ module HexaPDF
722
759
  # 60 + index * 80, 30).stroke
723
760
  # end
724
761
  #
725
- # See: PDF1.7 s8.4.3.5
762
+ # See: PDF2.0 s8.4.3.5
726
763
  def miter_limit(limit = nil, &block)
727
764
  gs_getter_setter(:miter_limit, :M, limit, &block)
728
765
  end
@@ -740,7 +777,7 @@ module HexaPDF
740
777
  #
741
778
  # There are multiple ways to set the line dash pattern:
742
779
  #
743
- # * By providing a Content::LineDashPattern object
780
+ # * By providing a LineDashPattern object
744
781
  # * By providing a single Integer/Float that is used for both dashes and gaps
745
782
  # * By providing an array of Integers/Floats that specify the alternating dashes and gaps
746
783
  #
@@ -749,10 +786,10 @@ module HexaPDF
749
786
  #
750
787
  # A solid line can be achieved by using 0 for the length or by using an empty array.
751
788
  #
752
- # Returns the current line dash pattern (see Content::GraphicsState#line_dash_pattern) when no
753
- # argument is given. Otherwise sets the line dash pattern using the given arguments and
754
- # returns self. The setter version can also be called in the line_dash_pattern= form (but only
755
- # without the second argument!).
789
+ # Returns the current line dash pattern (a LineDashPattern object, see
790
+ # GraphicsState#line_dash_pattern) when no argument is given. Otherwise sets the line dash
791
+ # pattern using the given arguments and returns +self+. The setter version can also be called
792
+ # in the line_dash_pattern= form (but only without the second argument!).
756
793
  #
757
794
  # If arguments and a block are provided, the changed line dash pattern is only active during
758
795
  # the block by saving and restoring the graphics state.
@@ -778,7 +815,7 @@ module HexaPDF
778
815
  # stroke
779
816
  # end
780
817
  #
781
- # See: PDF1.7 s8.4.3.5, LineDashPattern
818
+ # See: PDF2.0 s8.4.3.5, LineDashPattern
782
819
  def line_dash_pattern(value = nil, phase = 0, &block)
783
820
  gs_getter_setter(:line_dash_pattern, :d, value && LineDashPattern.normalize(value, phase),
784
821
  &block)
@@ -799,9 +836,9 @@ module HexaPDF
799
836
  # * +:Saturation+
800
837
  # * +:Perceptual+
801
838
  #
802
- # Returns the current rendering intent (see Content::GraphicsState#rendering_intent) when no
803
- # argument is given. Otherwise sets the rendering intent using the +intent+ argument and
804
- # returns self. The setter version can also be called in the rendering_intent= form.
839
+ # Returns the current rendering intent (see GraphicsState#rendering_intent) when no argument
840
+ # is given. Otherwise sets the rendering intent using the +intent+ argument and returns
841
+ # +self+. The setter version can also be called in the rendering_intent= form.
805
842
  #
806
843
  # If the +intent+ and a block are provided, the changed rendering intent is only active
807
844
  # during the block by saving and restoring the graphics state.
@@ -817,7 +854,7 @@ module HexaPDF
817
854
  # end
818
855
  # canvas.rendering_intent # => :Saturation
819
856
  #
820
- # See: PDF1.7 s8.6.5.8, RenderingIntent
857
+ # See: PDF2.0 s8.6.5.8, RenderingIntent
821
858
  def rendering_intent(intent = nil, &bk)
822
859
  gs_getter_setter(:rendering_intent, :ri, intent && RenderingIntent.normalize(intent), &bk)
823
860
  end
@@ -837,23 +874,30 @@ module HexaPDF
837
874
  #
838
875
  # There are several ways to define the color that should be used:
839
876
  #
840
- # * A single numeric argument specifies a gray color (see
841
- # Content::ColorSpace::DeviceGray::Color).
842
- # * Three numeric arguments specify an RGB color (see Content::ColorSpace::DeviceRGB::Color).
877
+ # * A single numeric argument specifies a gray color (see ColorSpace::DeviceGray::Color).
878
+ #
879
+ # * Three numeric arguments specify an RGB color (see ColorSpace::DeviceRGB::Color).
880
+ #
843
881
  # * A string in the format "RRGGBB" where "RR" is the hexadecimal number for the red, "GG"
844
882
  # for the green and "BB" for the blue color value also specifies an RGB color.
883
+ #
845
884
  # * As does a string in the format "RGB" where "RR", "GG" and "BB" would be used as the
846
885
  # hexadecimal numbers for the red, green and blue color values of an RGB color.
847
- # * Any other string is treated as a CSS Color Module Level 3 color name, see
848
- # https://www.w3.org/TR/css-color-3/#svg-color.
849
- # * Four numeric arguments specify a CMYK color (see Content::ColorSpace::DeviceCMYK::Color).
886
+ #
887
+ # * Any other string is treated as a color name. HexaPDF supports CSS Color Module Level 3
888
+ # color names (see https://www.w3.org/TR/css-color-3/#svg-color) as well as HexaPDF design
889
+ # colors.
890
+ #
891
+ # * Four numeric arguments specify a CMYK color (see ColorSpace::DeviceCMYK::Color).
892
+ #
850
893
  # * A color object is used directly (normally used for color spaces other than DeviceRGB,
851
894
  # DeviceCMYK and DeviceGray).
895
+ #
852
896
  # * An array is treated as if its items were specified separately as arguments.
853
897
  #
854
- # Returns the current stroke color (see Content::GraphicsState#stroke_color) when no argument
855
- # is given. Otherwise sets the stroke color using the given arguments and returns self. The
856
- # setter version can also be called in the stroke_color= form.
898
+ # Returns the current stroke color (see GraphicsState#stroke_color) when no argument is given.
899
+ # Otherwise sets the stroke color using the given arguments and returns +self+. The setter
900
+ # version can also be called in the stroke_color= form.
857
901
  #
858
902
  # If the arguments and a block are provided, the changed stroke color is only active during
859
903
  # the block by saving and restoring the graphics state.
@@ -896,7 +940,7 @@ module HexaPDF
896
940
  # canvas.stroke_color # => ColorSpace::DeviceGray.color(0.4)
897
941
  # end
898
942
  #
899
- # See: PDF1.7 s8.6, ColorSpace
943
+ # See: PDF2.0 s8.6, ColorSpace
900
944
  def stroke_color(*color, &block)
901
945
  color_getter_setter(:stroke_color, color, :RG, :G, :K, :CS, :SCN, &block)
902
946
  end
@@ -922,10 +966,10 @@ module HexaPDF
922
966
  # the fill alpha value applies not just to fill values but to all non-stroking operations
923
967
  # (e.g. images, ...).
924
968
  #
925
- # Returns the current fill alpha (see Content::GraphicsState#fill_alpha) and stroke alpha (see
926
- # Content::GraphicsState#stroke_alpha) values using a hash with the keys +:fill_alpha+ and
969
+ # Returns the current fill alpha (see GraphicsState#fill_alpha) and stroke alpha (see
970
+ # GraphicsState#stroke_alpha) values using a hash with the keys +:fill_alpha+ and
927
971
  # +:stroke_alpha+ when no argument is given. Otherwise sets the fill and stroke alpha values
928
- # and returns self. The setter version can also be called in the #opacity= form.
972
+ # and returns +self+. The setter version can also be called in the #opacity= form.
929
973
  #
930
974
  # If the values are set and a block is provided, the changed alpha values are only active
931
975
  # during the block by saving and restoring the graphics state.
@@ -944,15 +988,15 @@ module HexaPDF
944
988
  # canvas.opacity # => {fill_alpha: 0.4, stroke_alpha: 0.9}
945
989
  #
946
990
  # # visual example
947
- # canvas.opacity(fill_alpha: 1, stroke_alpha: 1) do # background rectangle on right side
948
- # canvas.fill_color("lightgrey").rectangle(100, 0, 100, 200).fill
949
- # end
991
+ # canvas.opacity(fill_alpha: 1, stroke_alpha: 1)
992
+ # canvas.fill_color("hp-gray-light"). # background rectangle on right side
993
+ # rectangle(100, 0, 100, 200).fill
950
994
  # canvas.opacity(fill_alpha: 0.5, stroke_alpha: 0.8). # foreground rectangle, with a thick
951
995
  # line_width(20). # stroke that also overlays the
952
- # fill_color("red").stroke_color("blue"). # inside of the rectangle, creating
996
+ # fill_color("hp-blue").stroke_color("hp-blue"). # inside of the rectangle, creating
953
997
  # rectangle(20, 20, 160, 160).fill_stroke # multiple shadings due to opacity
954
998
  #
955
- # See: PDF1.7 s11.6.4.4
999
+ # See: PDF2.0 s11.6.4.4
956
1000
  def opacity(fill_alpha: nil, stroke_alpha: nil)
957
1001
  if !fill_alpha.nil? || !stroke_alpha.nil?
958
1002
  raise_unless_at_page_description_level_or_in_text
@@ -981,14 +1025,14 @@ module HexaPDF
981
1025
  # canvas.move_to(x, y) => canvas
982
1026
  #
983
1027
  # Begins a new subpath (and possibly a new path) by moving the current point to the given
984
- # point.
1028
+ # point and returns +self+.
985
1029
  #
986
1030
  # Examples:
987
1031
  #
988
1032
  # canvas.move_to(10, 50)
989
1033
  # canvas.current_point # => [10, 50]
990
1034
  #
991
- # See: PDF1.7 s8.5.2.1
1035
+ # See: PDF2.0 s8.5.2.1, #line_to, #curve_to, #rectangle
992
1036
  def move_to(x, y)
993
1037
  raise_unless_at_page_description_level_or_in_path
994
1038
  invoke2(:m, x, y)
@@ -1001,17 +1045,20 @@ module HexaPDF
1001
1045
  # canvas.line_to(x, y) => canvas
1002
1046
  #
1003
1047
  # Appends a straight line segment from the current point to the given point (which becomes the
1004
- # new current point) to the current subpath.
1048
+ # new current point) to the current subpath and returns +self+.
1049
+ #
1050
+ # If there is no current path when the method is invoked, an error is raised since a valid
1051
+ # current point (#current_point) is needed.
1005
1052
  #
1006
1053
  # Examples:
1007
1054
  #
1008
1055
  # #>pdf-center
1009
- # canvas.move_to(10, 50).
1010
- # line_to(80, 80)
1011
- # canvas.current_point # => [80, 80]
1056
+ # canvas.move_to(10, 50)
1057
+ # canvas.line_to(80, 80)
1058
+ # canvas.current_point # => [80, 80]
1012
1059
  # canvas.stroke
1013
1060
  #
1014
- # See: PDF1.7 s8.5.2.1
1061
+ # See: PDF2.0 s8.5.2.1, #move_to, #curve_to, #rectangle
1015
1062
  def line_to(x, y)
1016
1063
  raise_unless_in_path
1017
1064
  invoke2(:l, x, y)
@@ -1025,8 +1072,11 @@ module HexaPDF
1025
1072
  # canvas.curve_to(x, y, p1:) => canvas
1026
1073
  # canvas.curve_to(x, y, p2:) => canvas
1027
1074
  #
1028
- # Appends a cubic Bezier curve to the current subpath starting from the current point. The end
1029
- # point becomes the new current point.
1075
+ # Appends a cubic Bezier curve to the current subpath starting from the current point and
1076
+ # returns +self+. The end point becomes the new current point.
1077
+ #
1078
+ # If there is no current path when the method is invoked, an error is raised since a valid
1079
+ # current point (#current_point) is needed.
1030
1080
  #
1031
1081
  # A Bezier curve consists of the start point, the end point and the two control points +p1+
1032
1082
  # and +p2+. The start point is always the current point and the end point is specified as the
@@ -1047,7 +1097,7 @@ module HexaPDF
1047
1097
  # canvas.current_point # => [-30, 60]
1048
1098
  # canvas.stroke
1049
1099
  #
1050
- # See: PDF1.7 s8.5.2.2
1100
+ # See: PDF2.0 s8.5.2.2, #move_to, #line_to, #rectangle
1051
1101
  def curve_to(x, y, p1: nil, p2: nil)
1052
1102
  raise_unless_in_path
1053
1103
  if p1 && p2
@@ -1069,10 +1119,13 @@ module HexaPDF
1069
1119
  #
1070
1120
  # Appends a rectangle to the current path as a complete subpath (drawn in counterclockwise
1071
1121
  # direction), with the bottom left corner specified by +x+ and +y+ and the given +width+ and
1072
- # +height+.
1122
+ # +height+. Returns +self+.
1073
1123
  #
1074
1124
  # If +radius+ is greater than 0, the corners are rounded with the given radius.
1075
1125
  #
1126
+ # Note that the rectangle degrades to a line if either width or height is zero and to nothing
1127
+ # if both are zero.
1128
+ #
1076
1129
  # If there is no current path when the method is invoked, a new path is automatically begun.
1077
1130
  #
1078
1131
  # The current point is set to the bottom left corner if +radius+ is zero, otherwise it is set
@@ -1083,10 +1136,10 @@ module HexaPDF
1083
1136
  # #>pdf
1084
1137
  # canvas.rectangle(10, 110, 80, 50).stroke
1085
1138
  # canvas.rectangle(110, 110, 80, 50, radius: 10).stroke
1086
- # canvas.rectangle(10, 10, 80, 0).stroke # Degraded: Just a line
1087
- # canvas.rectangle(110, 10, 0, 0).stroke # Degraded: Draws nothing
1139
+ # canvas.rectangle(10, 90, 80, 0).stroke # Degraded: Just a line
1140
+ # canvas.rectangle(110, 90, 0, 0).stroke # Degraded: Draws nothing
1088
1141
  #
1089
- # See: PDF1.7 s8.5.2.1
1142
+ # See: PDF2.0 s8.5.2.1, #move_to, #line_to, #curve_to
1090
1143
  def rectangle(x, y, width, height, radius: 0)
1091
1144
  raise_unless_at_page_description_level_or_in_path
1092
1145
  if radius == 0
@@ -1103,7 +1156,10 @@ module HexaPDF
1103
1156
  # canvas.close_subpath => canvas
1104
1157
  #
1105
1158
  # Closes the current subpath by appending a straight line from the current point to the
1106
- # start point of the subpath which also becomes the new current point.
1159
+ # start point of the subpath which also becomes the new current point. Returns +self+.
1160
+ #
1161
+ # If there is no current path when the method is invoked, an error is raised since a valid
1162
+ # current point (#current_point) is needed.
1107
1163
  #
1108
1164
  # Examples:
1109
1165
  #
@@ -1114,7 +1170,7 @@ module HexaPDF
1114
1170
  # close_subpath. # Draws the line from (60, 60) to (10, 10)
1115
1171
  # stroke
1116
1172
  #
1117
- # See: PDF1.7 s8.5.2.1
1173
+ # See: PDF2.0 s8.5.2.1
1118
1174
  def close_subpath
1119
1175
  raise_unless_in_path
1120
1176
  invoke0(:h)
@@ -1126,13 +1182,16 @@ module HexaPDF
1126
1182
  # canvas.line(x0, y0, x1, y1) => canvas
1127
1183
  #
1128
1184
  # Moves the current point to (x0, y0) and appends a line to (x1, y1) to the current path.
1185
+ # Returns +self+.
1129
1186
  #
1130
- # This method is equal to "canvas.move_to(x0, y0).line_to(x1, y1)".
1187
+ # If there is no current path when the method is invoked, a new path is automatically begun.
1131
1188
  #
1132
1189
  # Examples:
1133
1190
  #
1134
1191
  # #>pdf
1135
1192
  # canvas.line(10, 10, 100, 100).stroke
1193
+ #
1194
+ # See: #move_to, #line_to
1136
1195
  def line(x0, y0, x1, y1)
1137
1196
  move_to(x0, y0)
1138
1197
  line_to(x1, y1)
@@ -1143,12 +1202,16 @@ module HexaPDF
1143
1202
  #
1144
1203
  # Moves the current point to (x0, y0) and appends line segments between all given
1145
1204
  # consecutive points, i.e. between (x0, y0) and (x1, y1), between (x1, y1) and (x2, y2) and
1146
- # so on. The last point becomes the new current point.
1205
+ # so on. The last point becomes the new current point. Returns +self+.
1206
+ #
1207
+ # If there is no current path when the method is invoked, a new path is automatically begun.
1147
1208
  #
1148
1209
  # Examples:
1149
1210
  #
1150
1211
  # #>pdf
1151
1212
  # canvas.polyline(50, 50, 150, 50, 150, 150, 50, 150, 50, 50).stroke
1213
+ #
1214
+ # See: #move_to, #line_to, #polygon
1152
1215
  def polyline(*points)
1153
1216
  check_poly_points(points)
1154
1217
  move_to(points[0], points[1])
@@ -1163,8 +1226,8 @@ module HexaPDF
1163
1226
  # :call-seq:
1164
1227
  # canvas.polygon(x0, y0, x1, y1, x2, y2, ..., radius: 0) => canvas
1165
1228
  #
1166
- # Appends a polygon consisting of the given points to the path as a complete subpath. The
1167
- # point (x0, y0 + radius) becomes the new current point.
1229
+ # Appends a polygon consisting of the given points to the path as a complete subpath and
1230
+ # returns +self+. The point (x0, y0 + radius) becomes the new current point.
1168
1231
  #
1169
1232
  # If +radius+ is greater than 0, the corners are rounded with the given radius.
1170
1233
  #
@@ -1174,8 +1237,10 @@ module HexaPDF
1174
1237
  #
1175
1238
  # #>pdf
1176
1239
  # canvas.polygon(10, 10, 90, 10, 70, 90, 20, 100).stroke
1177
- # canvas.stroke_color("red").
1240
+ # canvas.stroke_color("hp-blue").
1178
1241
  # polygon(130, 130, 150, 100, 170, 150, 130, 190, radius: 10).stroke
1242
+ #
1243
+ # See: #polyline
1179
1244
  def polygon(*points, radius: 0)
1180
1245
  if radius == 0
1181
1246
  polyline(*points)
@@ -1195,16 +1260,19 @@ module HexaPDF
1195
1260
  #
1196
1261
  # Appends a circle with center (cx, cy) and the given radius (in degrees) to the path as a
1197
1262
  # complete subpath (drawn in counterclockwise direction). The point (center_x + radius,
1198
- # center_y) becomes the new current point.
1263
+ # center_y) becomes the new current point. Returns +self+.
1199
1264
  #
1200
1265
  # If there is no current path when the method is invoked, a new path is automatically begun.
1201
1266
  #
1202
1267
  # Examples:
1203
1268
  #
1204
1269
  # #>pdf
1205
- # canvas.circle(100, 100, 30).stroke
1270
+ # canvas.circle(100, 100, 30)
1271
+ # cp = canvas.current_point
1272
+ # canvas.stroke
1273
+ # canvas.stroke_color("hp-orange").line(*cp, 180, 100).stroke
1206
1274
  #
1207
- # See: #arc (for approximation accuracy)
1275
+ # See: #arc (for approximation accuracy), #ellipse
1208
1276
  def circle(cx, cy, radius)
1209
1277
  arc(cx, cy, a: radius)
1210
1278
  close_subpath
@@ -1215,7 +1283,8 @@ module HexaPDF
1215
1283
  #
1216
1284
  # Appends an ellipse with center (cx, cy), semi-major axis +a+, semi-minor axis +b+ and an
1217
1285
  # inclination from the x-axis of +inclination+ degrees to the path as a complete subpath. The
1218
- # outer-most point on the semi-major axis becomes the new current point.
1286
+ # outer-most point on the positive semi-major axis becomes the new current point. Returns
1287
+ # self.
1219
1288
  #
1220
1289
  # If there is no current path when the method is invoked, a new path is automatically begun.
1221
1290
  #
@@ -1225,11 +1294,15 @@ module HexaPDF
1225
1294
  # # Ellipse aligned to x-axis and y-axis
1226
1295
  # canvas.ellipse(50, 50, a: 20, b: 10).stroke
1227
1296
  #
1228
- # # Inclined ellipse
1229
- # canvas.stroke_color("red").
1230
- # ellipse(150, 150, a: 20, b: 10, inclination: 30).stroke
1297
+ # # Inclined ellipse with line from the end point
1298
+ # canvas.stroke_color("hp-blue").
1299
+ # ellipse(150, 150, a: 20, b: 10, inclination: 30)
1300
+ # cp = canvas.current_point
1301
+ # x, y = 2 * canvas.current_point[0] - 150, 2 * canvas.current_point[1] - 150
1302
+ # canvas.stroke.
1303
+ # stroke_color("hp-orange").line(*cp, x, y).stroke
1231
1304
  #
1232
- # See: #arc (for approximation accuracy)
1305
+ # See: #arc (for approximation accuracy), #circle
1233
1306
  def ellipse(cx, cy, a:, b:, inclination: 0)
1234
1307
  arc(cx, cy, a: a, b: b, inclination: inclination)
1235
1308
  close_subpath
@@ -1238,8 +1311,8 @@ module HexaPDF
1238
1311
  # :call-seq:
1239
1312
  # canvas.arc(cx, cy, a:, b: a, start_angle: 0, end_angle: 360, clockwise: false, inclination: 0) => canvas
1240
1313
  #
1241
- # Appends an elliptical arc to the path. The endpoint of the arc becomes the new current
1242
- # point.
1314
+ # Appends an elliptical arc to the path and returns +self+. The endpoint of the arc becomes
1315
+ # the new current point.
1243
1316
  #
1244
1317
  # +cx+::
1245
1318
  # x-coordinate of the center point of the arc
@@ -1271,6 +1344,9 @@ module HexaPDF
1271
1344
  #
1272
1345
  # If there is no current path when the method is invoked, a new path is automatically begun.
1273
1346
  #
1347
+ # This arc does *not* start from the current point (#current_point). If this functionality is
1348
+ # needed, use #draw together with GraphicObject::EndpointArc.
1349
+ #
1274
1350
  # Since PDF doesn't have operators for drawing elliptical or circular arcs, they have to be
1275
1351
  # approximated using Bezier curves (see #curve_to). The accuracy of the approximation can be
1276
1352
  # controlled using the configuration option 'graphic_object.arc.max_curves'.
@@ -1284,19 +1360,19 @@ module HexaPDF
1284
1360
  # canvas.stroke
1285
1361
  #
1286
1362
  # # Circular and elliptical arcs from 30 degrees to 160 degrees
1287
- # canvas.stroke_color("red")
1363
+ # canvas.stroke_color("hp-blue")
1288
1364
  # canvas.arc(50, 100, a: 10, start_angle: 30, end_angle: 160)
1289
1365
  # canvas.arc(100, 100, a: 10, b: 5, start_angle: 30, end_angle: 160)
1290
1366
  # canvas.stroke
1291
1367
  #
1292
1368
  # # Arcs from 135 degrees to 30 degrees, the first in counterclockwise direction (i.e. the
1293
1369
  # # big arc), the other in clockwise direction (i.e. the small arc)
1294
- # canvas.stroke_color("blue")
1370
+ # canvas.stroke_color("hp-orange")
1295
1371
  # canvas.arc(50, 50, a: 10, start_angle: 135, end_angle: 30)
1296
1372
  # canvas.arc(100, 50, a: 10, start_angle: 135, end_angle: 30, clockwise: true)
1297
1373
  # canvas.stroke
1298
1374
  #
1299
- # See: Content::GraphicObject::Arc
1375
+ # See: #arc, #circle, #ellipse, GraphicObject::Arc, GraphicObject::EndpointArc
1300
1376
  def arc(cx, cy, a:, b: a, start_angle: 0, end_angle: 360, clockwise: false, inclination: 0)
1301
1377
  arc = GraphicObject::Arc.configure(cx: cx, cy: cy, a: a, b: b,
1302
1378
  start_angle: start_angle, end_angle: end_angle,
@@ -1314,18 +1390,18 @@ module HexaPDF
1314
1390
  # :call-seq:
1315
1391
  # canvas.line_with_rounded_corner(x0 = current_point[0], y0 = current_point[1], x1, y1, x2, y2, in_radius:, out_radius: in_radius)
1316
1392
  #
1317
- # Appends a line with a rounded corner at (x1, y1) from the current point. The end point of
1318
- # the rounded corner (i.e. +out_radius+ units from (x1, y1) in the direction of (x2, y2))
1319
- # becomes the current point. In degraded cases the corner point (x1, y1) becomes the current
1320
- # point.
1393
+ # Appends a line with a rounded corner at (x1, y1) from the current point and returns +self+.
1394
+ # The end point of the rounded corner (i.e. +out_radius+ units from (x1, y1) in the direction
1395
+ # of (x2, y2)) becomes the current point. In degraded cases the corner point (x1, y1) becomes
1396
+ # the current point.
1321
1397
  #
1322
1398
  # The corner is specified by (x0, y0) which defaults to the #current_point of the path, (x1,
1323
1399
  # y1) and (x2, y2) - all of which need to be different points. The +in_radius+ specifies the
1324
1400
  # corner radius into the corner and the +out_radius+ the one out of the corner. Degraded
1325
1401
  # cases, like with (x0, y0) == (x1, y1), are handled gracefully.
1326
1402
  #
1327
- # There has to be a current path when this method is invoked. For example, the current point
1328
- # ould be estabilshed beforehand using #move_to.
1403
+ # There has to be a current path when this method is invoked, otherwise an error is raised.
1404
+ # For example, the current point could be estabilshed beforehand using #move_to.
1329
1405
  #
1330
1406
  # Examples:
1331
1407
  #
@@ -1338,10 +1414,15 @@ module HexaPDF
1338
1414
  # canvas.line_with_rounded_corner(180, 120, 150, 100, in_radius: 0, out_radius: 10)
1339
1415
  # canvas.stroke
1340
1416
  #
1341
- # # Special effects when (x0, y0) is not the current point, like when the current point
1342
- # # would be equal to the corner point
1417
+ # # Special effects when (x0, y0) is not the current point, like when the current
1418
+ # # point would be equal to the corner point. Rounded rectangle use this method
1419
+ # # internally, as high-lighted by the blue segment.
1343
1420
  # canvas.rectangle(10, 10, 60, 60, radius: 60).stroke
1344
- # canvas.rectangle(110, 10, 60, 60, radius: 70).stroke
1421
+ # canvas.stroke_color("hp-blue").
1422
+ # move_to(70, 10). # Start point at the end of the lower-left rounded corner
1423
+ # line_with_rounded_corner(10, 10, 70, 10, 70, 70, in_radius: 60).stroke
1424
+ # canvas.stroke_color("black").
1425
+ # rectangle(110, 10, 60, 60, radius: 70).stroke
1345
1426
  def line_with_rounded_corner(x0 = current_point[0], y0 = current_point[1], x1, y1, x2, y2,
1346
1427
  in_radius:, out_radius: in_radius)
1347
1428
  if in_radius == 0 || out_radius == 0
@@ -1367,9 +1448,9 @@ module HexaPDF
1367
1448
  #
1368
1449
  # Returns the named graphic object, configured with the given options.
1369
1450
  #
1370
- # If an object responding to :configure is given, it is used. Otherwise the graphic object
1371
- # is looked up via the given name in the configuration option 'graphic_object.map'. Then the
1372
- # graphic object is configured with the given options if at least one is given.
1451
+ # If an object responding to :configure is given, it is used. Otherwise the graphic object is
1452
+ # looked up via the given name in the configuration option 'graphic_object.map'. Either way,
1453
+ # the graphic object is then configured with the given options if at least one is given.
1373
1454
  #
1374
1455
  # Examples:
1375
1456
  #
@@ -1378,7 +1459,7 @@ module HexaPDF
1378
1459
  # outer_a: 50, outer_b: 40, end_angle: 135)
1379
1460
  # canvas.draw(obj).stroke
1380
1461
  #
1381
- # See: Content::GraphicObject
1462
+ # See: #draw, GraphicObject
1382
1463
  def graphic_object(obj, **options)
1383
1464
  unless obj.respond_to?(:configure)
1384
1465
  obj = context.document.config.constantize('graphic_object.map', obj)
@@ -1404,7 +1485,7 @@ module HexaPDF
1404
1485
  # :call-seq:
1405
1486
  # canvas.stroke => canvas
1406
1487
  #
1407
- # Strokes the path.
1488
+ # Strokes the path and returns +self+.
1408
1489
  #
1409
1490
  # Examples:
1410
1491
  #
@@ -1412,7 +1493,7 @@ module HexaPDF
1412
1493
  # canvas.polyline(10, 10, 120, 40, 50, 160)
1413
1494
  # canvas.stroke
1414
1495
  #
1415
- # See: PDF1.7 s8.5.3.1, s8.5.3.2
1496
+ # See: PDF2.0 s8.5.3.1, s8.5.3.2, #close_stroke, #close_fill_stroke
1416
1497
  def stroke
1417
1498
  raise_unless_in_path_or_clipping_path
1418
1499
  invoke0(:S)
@@ -1422,7 +1503,7 @@ module HexaPDF
1422
1503
  # :call-seq:
1423
1504
  # canvas.close_stroke => canvas
1424
1505
  #
1425
- # Closes the last subpath and then strokes the path.
1506
+ # Closes the last subpath and then strokes the path. Returns +self+.
1426
1507
  #
1427
1508
  # Examples:
1428
1509
  #
@@ -1430,7 +1511,7 @@ module HexaPDF
1430
1511
  # canvas.polyline(10, 10, 120, 40, 50, 160) # No line from the top to the left
1431
1512
  # canvas.close_stroke
1432
1513
  #
1433
- # See: PDF1.7 s8.5.3.1, s8.5.3.2
1514
+ # See: PDF2.0 s8.5.3.1, s8.5.3.2, #stroke, #close_fill_stroke
1434
1515
  def close_stroke
1435
1516
  raise_unless_in_path_or_clipping_path
1436
1517
  invoke0(:s)
@@ -1440,25 +1521,26 @@ module HexaPDF
1440
1521
  # :call-seq:
1441
1522
  # canvas.fill(rule = :nonzero) => canvas
1442
1523
  #
1443
- # Fills the path using the given rule.
1524
+ # Fills the path using the given rule and returns +self+.
1444
1525
  #
1445
1526
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1446
1527
  # +:even_odd+ to use the even-odd rule for determining which regions to fill in. Details on
1447
- # how these rules work are found in the PDF 1.7 spec section 8.5.3.3 or via Internet search.
1528
+ # how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via Internet search.
1448
1529
  #
1449
1530
  # Any open subpaths are implicitly closed before being filled.
1450
1531
  #
1451
1532
  # Examples:
1452
1533
  #
1453
1534
  # #>pdf
1454
- # canvas.polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90)
1455
- # canvas.fill
1535
+ # canvas.fill_color("hp-blue").
1536
+ # polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90).
1537
+ # fill
1456
1538
  #
1457
- # canvas.fill_color("red")
1458
- # canvas.polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190)
1459
- # canvas.fill(:even_odd)
1539
+ # canvas.fill_color("hp-orange").
1540
+ # polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190).
1541
+ # fill(:even_odd)
1460
1542
  #
1461
- # See: PDF1.7 s8.5.3.1, s8.5.3.3
1543
+ # See: PDF2.0 s8.5.3.1, s8.5.3.3, #fill_stroke, #close_fill_stroke
1462
1544
  def fill(rule = :nonzero)
1463
1545
  raise_unless_in_path_or_clipping_path
1464
1546
  invoke0(rule == :nonzero ? :f : :'f*')
@@ -1468,26 +1550,27 @@ module HexaPDF
1468
1550
  # :call-seq:
1469
1551
  # canvas.fill_stroke(rule = :nonzero) => canvas
1470
1552
  #
1471
- # Fills and then strokes the path using the given rule.
1553
+ # Fills and then strokes the path using the given rule. Returns +self+.
1472
1554
  #
1473
1555
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1474
1556
  # +:even_odd+ to use the even-odd rule for determining which regions to fill in. Details on
1475
- # how these rules work are found in the PDF 1.7 spec section 8.5.3.3 or via Internet search.
1557
+ # how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via Internet search.
1476
1558
  #
1477
- # Note that any open subpaths are *not* closed!
1559
+ # Note that any open subpaths are *not* closed concerning the stroking operation.
1478
1560
  #
1479
1561
  # Examples:
1480
1562
  #
1481
1563
  # #>pdf
1482
- # canvas.stroke_color("green").line_width(3)
1483
- # canvas.polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90)
1484
- # canvas.fill_stroke # Note the missing stroke from the top corner
1564
+ # canvas.stroke_color("hp-orange").line_width(3)
1565
+ # canvas.fill_color("hp-blue").
1566
+ # polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90).
1567
+ # fill_stroke # Note the missing stroke from the top corner
1485
1568
  #
1486
- # canvas.fill_color("red")
1487
- # canvas.polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190)
1488
- # canvas.fill_stroke(:even_odd) # Note the missing stroke from the top corner
1569
+ # canvas.fill_color("hp-teal").
1570
+ # polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190).
1571
+ # fill_stroke(:even_odd) # Note the missing stroke from the top corner
1489
1572
  #
1490
- # See: PDF1.7 s8.5.3.1, s8.5.3.3
1573
+ # See: PDF2.0 s8.5.3.1, s8.5.3.3, #fill, #close_fill_stroke
1491
1574
  def fill_stroke(rule = :nonzero)
1492
1575
  raise_unless_in_path_or_clipping_path
1493
1576
  invoke0(rule == :nonzero ? :B : :'B*')
@@ -1497,24 +1580,26 @@ module HexaPDF
1497
1580
  # :call-seq:
1498
1581
  # canvas.close_fill_stroke(rule = :nonzero) => canvas
1499
1582
  #
1500
- # Closes the last subpath and then fills and strokes the path using the given rule.
1583
+ # Closes the last subpath and then fills and strokes the path using the given rule. Returns
1584
+ # +self+.
1501
1585
  #
1502
1586
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1503
1587
  # +:even_odd+ to use the even-odd rule for determining which regions to fill in. Details on
1504
- # how these rules work are found in the PDF 1.7 spec section 8.5.3.3 or via Internet search.
1588
+ # how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via Internet search.
1505
1589
  #
1506
1590
  # Examples:
1507
1591
  #
1508
1592
  # #>pdf
1509
- # canvas.stroke_color("green").line_width(3)
1510
- # canvas.polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90)
1511
- # canvas.close_fill_stroke
1593
+ # canvas.stroke_color("hp-orange").line_width(3)
1594
+ # canvas.fill_color("hp-blue").
1595
+ # polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90).
1596
+ # close_fill_stroke
1512
1597
  #
1513
- # canvas.fill_color("red")
1514
- # canvas.polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190)
1515
- # canvas.close_fill_stroke(:even_odd)
1598
+ # canvas.fill_color("hp-teal").
1599
+ # polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190).
1600
+ # close_fill_stroke(:even_odd)
1516
1601
  #
1517
- # See: PDF1.7 s8.5.3
1602
+ # See: PDF2.0 s8.5.3, #fill, #fill_stroke
1518
1603
  def close_fill_stroke(rule = :nonzero)
1519
1604
  raise_unless_in_path_or_clipping_path
1520
1605
  invoke0(rule == :nonzero ? :b : :'b*')
@@ -1524,17 +1609,17 @@ module HexaPDF
1524
1609
  # :call-seq:
1525
1610
  # canvas.end_path => canvas
1526
1611
  #
1527
- # Ends the path without stroking or filling it.
1612
+ # Ends the path without stroking or filling it and returns +self+.
1528
1613
  #
1529
- # This method is normally used in conjunction with the clipping path methods to define the
1530
- # clipping.
1614
+ # This method is usually used in conjunction with the clipping path methods to define the
1615
+ # clipping path.
1531
1616
  #
1532
1617
  # Examples:
1533
1618
  #
1534
1619
  # canvas.line(10, 10, 100, 100)
1535
1620
  # canvas.end_path # Nothing to see here!
1536
1621
  #
1537
- # See: PDF1.7 s8.5.3.1, #clip_path
1622
+ # See: PDF2.0 s8.5.3.1, #clip_path
1538
1623
  def end_path
1539
1624
  raise_unless_in_path_or_clipping_path
1540
1625
  invoke0(:n)
@@ -1544,11 +1629,11 @@ module HexaPDF
1544
1629
  # :call-seq:
1545
1630
  # canvas.clip_path(rule = :nonzero) => canvas
1546
1631
  #
1547
- # Modifies the clipping path by intersecting it with the current path.
1632
+ # Modifies the clipping path by intersecting it with the current path. Returns +self+.
1548
1633
  #
1549
1634
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1550
1635
  # +:even_odd+ to use the even-odd rule for determining which regions lie inside the clipping
1551
- # path. Details on how these rules work are found in the PDF 1.7 spec section 8.5.3.3 or via
1636
+ # path. Details on how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via
1552
1637
  # Internet search.
1553
1638
  #
1554
1639
  # The initial clipping path includes the entire canvas. Once the clipping path is reduced to a
@@ -1566,7 +1651,7 @@ module HexaPDF
1566
1651
  # clip_path(:even_odd).end_path
1567
1652
  # canvas.rectangle(0, 0, 200, 200).fill # Fills everything inside the clipping path
1568
1653
  #
1569
- # See: PDF1.7 s8.5.4
1654
+ # See: PDF2.0 s8.5.4, #end_path
1570
1655
  def clip_path(rule = :nonzero)
1571
1656
  raise_unless_in_path
1572
1657
  invoke0(rule == :nonzero ? :W : :'W*')
@@ -1583,8 +1668,8 @@ module HexaPDF
1583
1668
  # position and returns the XObject.
1584
1669
  #
1585
1670
  # Any image format for which a HexaPDF::ImageLoader object is available and registered with
1586
- # the configuration option 'image_loader' can be used. PNG and JPEG images are supported out
1587
- # of the box.
1671
+ # the configuration option 'image_loader' can be used. PNG (lossless), JPEG (lossy) and PDF
1672
+ # (vector) images are supported out of the box.
1588
1673
  #
1589
1674
  # If the filename or the IO specifies a PDF file, the first page of this file is used to
1590
1675
  # create a form XObject which is then drawn.
@@ -1608,18 +1693,18 @@ module HexaPDF
1608
1693
  # #>pdf
1609
1694
  # canvas.xobject(machu_picchu, at: [10, 10], width: 90) # bottom left
1610
1695
  #
1611
- # file = File.new(machu_picchu, 'rb') # top left
1696
+ # file = File.new(machu_picchu, 'rb') # top left
1612
1697
  # canvas.xobject(file, at: [10, 110], height: 50)
1613
1698
  #
1614
1699
  # image = doc.images.add(machu_picchu)
1615
1700
  # canvas.xobject(image, at: [110, 10], width: 50, height: 90) # bottom right
1616
1701
  #
1617
1702
  # form = doc.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, 100, 100]})
1618
- # form.canvas.line(10, 10, 90, 90).stroke
1703
+ # form.canvas.stroke_color("hp-blue").line(10, 10, 90, 90).stroke
1619
1704
  # canvas.line_width = 20
1620
1705
  # canvas.xobject(form, at: [100, 100]) # top right
1621
1706
  #
1622
- # See: PDF1.7 s8.8, s.8.10.1
1707
+ # See: PDF2.0 s8.8, s.8.10.1, HexaPDF::Type::Image, HexaPDF::Type::Form, HexaPDF::ImageLoader
1623
1708
  def xobject(obj, at:, width: nil, height: nil)
1624
1709
  unless obj.kind_of?(HexaPDF::Stream)
1625
1710
  obj = context.document.images.add(obj)
@@ -1653,14 +1738,18 @@ module HexaPDF
1653
1738
  # canvas.character_spacing(amount) => canvas
1654
1739
  # canvas.character_spacing(amount) { block } => canvas
1655
1740
  #
1656
- # The character spacing determines how much additional space is added between two
1657
- # consecutive characters. For horizontal writing positive values increase the distance
1658
- # between two characters, whereas for vertical writing negative values increase the
1741
+ # The character spacing determines how much additional space is added after each character
1742
+ # (or, more correctly, after each glyph). For horizontal writing positive values increase the
1743
+ # distance between two characters, whereas for vertical writing negative values increase the
1659
1744
  # distance.
1660
1745
  #
1661
- # Returns the current character spacing value (see Content::GraphicsState#character_spacing)
1662
- # when no argument is given. Otherwise sets the character spacing using the +amount+ argument
1663
- # and returns self. The setter version can also be called in the character_spacing= form.
1746
+ # Note that the character spacing is applied to all characters that are rendered. This has the
1747
+ # effect that there is also a space after the last character which might not be wanted in
1748
+ # certain cases (e.g. when justifying text).
1749
+ #
1750
+ # Returns the current character spacing value (see GraphicsState#character_spacing) when no
1751
+ # argument is given. Otherwise sets the character spacing using the +amount+ argument and
1752
+ # returns +self+. The setter version can also be called in the character_spacing= form.
1664
1753
  #
1665
1754
  # If the +amount+ and a block are provided, the changed character spacing is only active
1666
1755
  # during the block by saving and restoring the graphics state.
@@ -1680,11 +1769,18 @@ module HexaPDF
1680
1769
  # # visual example
1681
1770
  # canvas.font("Helvetica", size: 10)
1682
1771
  # canvas.character_spacing = 0 # initial value
1683
- # canvas.text("This is an example text.", at: [10, 150])
1684
- # canvas.character_spacing = 3
1685
- # canvas.text("This is an example text.", at: [10, 100])
1686
- #
1687
- # See: PDF1.7 s9.3.2
1772
+ # canvas.text("This is an example", at: [10, 150])
1773
+ # # show that the text cursor is directly after the last glyph
1774
+ # x, y = canvas.text_cursor
1775
+ # canvas.stroke_color("hp-blue").line(x, y, x, y + 10).stroke
1776
+ #
1777
+ # canvas.character_spacing = 5
1778
+ # canvas.text("This is an example", at: [10, 100])
1779
+ # # visualize the spacing after the last glyph
1780
+ # x, y = canvas.text_cursor
1781
+ # canvas.stroke_color("hp-blue").line(x, y, x, y + 10).stroke
1782
+ #
1783
+ # See: PDF2.0 s9.3.2, #word_spacing, #horizontal_scaling
1688
1784
  def character_spacing(amount = nil, &bk)
1689
1785
  gs_getter_setter(:character_spacing, :Tc, amount, &bk)
1690
1786
  end
@@ -1703,9 +1799,9 @@ module HexaPDF
1703
1799
  # *Important*: In HexaPDF only the standard 14 PDF Type1 fonts support this property! When
1704
1800
  # using any other font, for example a TrueType font, this property has no effect.
1705
1801
  #
1706
- # Returns the current word spacing value (see Content::GraphicsState#word_spacing) when no
1707
- # argument is given. Otherwise sets the word spacing using the +amount+ argument and returns
1708
- # self. The setter version can also be called in the word_spacing= form.
1802
+ # Returns the current word spacing value (see GraphicsState#word_spacing) when no argument is
1803
+ # given. Otherwise sets the word spacing using the +amount+ argument and returns +self+. The
1804
+ # setter version can also be called in the word_spacing= form.
1709
1805
  #
1710
1806
  # If the +amount+ and a block are provided, the changed word spacing is only active during
1711
1807
  # the block by saving and restoring the graphics state.
@@ -1729,7 +1825,7 @@ module HexaPDF
1729
1825
  # canvas.word_spacing = 10
1730
1826
  # canvas.text("This is an example text.", at: [10, 100])
1731
1827
  #
1732
- # See: PDF1.7 s9.3.3
1828
+ # See: PDF2.0 s9.3.3, #character_spacing, #horizontal_scaling
1733
1829
  def word_spacing(amount = nil, &bk)
1734
1830
  gs_getter_setter(:word_spacing, :Tw, amount, &bk)
1735
1831
  end
@@ -1744,10 +1840,9 @@ module HexaPDF
1744
1840
  # compressing them in the horizontal direction. The value is specified as percent of the
1745
1841
  # normal width, so 100 means no scaling.
1746
1842
  #
1747
- # Returns the current horizontal scaling value (see Content::GraphicsState#horizontal_scaling)
1748
- # when no argument is given. Otherwise sets the horizontal scaling using the +percent+
1749
- # argument and returns self. The setter version can also be called in the horizontal_scaling=
1750
- # form.
1843
+ # Returns the current horizontal scaling value (see GraphicsState#horizontal_scaling) when no
1844
+ # argument is given. Otherwise sets the horizontal scaling using the +percent+ argument and
1845
+ # returns +self+. The setter version can also be called in the horizontal_scaling= form.
1751
1846
  #
1752
1847
  # If the +percent+ and a block are provided, the changed horizontal scaling is only active
1753
1848
  # during the block by saving and restoring the graphics state.
@@ -1771,7 +1866,7 @@ module HexaPDF
1771
1866
  # canvas.horizontal_scaling = 50
1772
1867
  # canvas.text("This is an example text.", at: [10, 100])
1773
1868
  #
1774
- # See: PDF1.7 s9.3.4
1869
+ # See: PDF2.0 s9.3.4, #character_spacing, #word_spacing
1775
1870
  def horizontal_scaling(amount = nil, &bk)
1776
1871
  gs_getter_setter(:horizontal_scaling, :Tz, amount, &bk)
1777
1872
  end
@@ -1789,8 +1884,8 @@ module HexaPDF
1789
1884
  # There are other PDF content stream operators that would be effected but those are not used
1790
1885
  # by the canvas.
1791
1886
  #
1792
- # Returns the current leading value (see Content::GraphicsState#leading) when no argument is
1793
- # given. Otherwise sets the leading using the +amount+ argument and returns self. The setter
1887
+ # Returns the current leading value (see GraphicsState#leading) when no argument is given.
1888
+ # Otherwise sets the leading using the +amount+ argument and returns +self+. The setter
1794
1889
  # version can also be called in the leading= form.
1795
1890
  #
1796
1891
  # If the +amount+ and a block are provided, the changed leading is only active during the
@@ -1813,7 +1908,7 @@ module HexaPDF
1813
1908
  # canvas.leading = 15
1814
1909
  # canvas.text("This is an example text.\nwith a second\nand thrid line", at: [10, 150])
1815
1910
  #
1816
- # See: PDF1.7 s9.3.5, #move_text_cursor
1911
+ # See: PDF2.0 s9.3.5, #move_text_cursor
1817
1912
  def leading(amount = nil, &bk)
1818
1913
  gs_getter_setter(:leading, :TL, amount, &bk)
1819
1914
  end
@@ -1824,16 +1919,33 @@ module HexaPDF
1824
1919
  # canvas.text_rendering_mode(mode) => canvas
1825
1920
  # canvas.text_rendering_mode(mode) { block } => canvas
1826
1921
  #
1827
- # The text rendering mode determines if and how glyphs are rendered. The +mode+ parameter
1828
- # can either be a valid integer or one of the symbols +:fill+, +:stroke+, +:fill_stroke+,
1829
- # +:invisible+, +:fill_clip+, +:stroke_clip+, +:fill_stroke_clip+ or +:clip+ (see
1830
- # TextRenderingMode.normalize for details). Note that the return value is always a
1831
- # normalized text rendering mode value.
1832
- #
1833
- # Returns the current text rendering mode value (see
1834
- # Content::GraphicsState#text_rendering_mode) when no argument is given. Otherwise sets the
1835
- # text rendering mode using the +mode+ argument and returns self. The setter version can also
1836
- # be called in the text_rendering_mode= form.
1922
+ # The text rendering mode determines if and how glyphs are rendered.
1923
+ #
1924
+ # The +mode+ parameter can be one of the following (also see TextRenderingMode):
1925
+ #
1926
+ # :fill or 0::
1927
+ # The text is filled (default)
1928
+ # :stroke or 1::
1929
+ # The text is stroked.
1930
+ # :fill_stroke or 2::
1931
+ # The test is filled, then stroked.
1932
+ # :invisible or 3::
1933
+ # The text is neither filled nor stroked.
1934
+ # :fill_clip or 4::
1935
+ # The text is filled and added to the clipping path.
1936
+ # :stroke_clip or 5::
1937
+ # The text is stroked and added to the clipping path.
1938
+ # :fill_stroke_clip or 6::
1939
+ # The text is filled, then stroked and added to the clipping path.
1940
+ # :clip or 7::
1941
+ # The text is added to the clipping path.
1942
+ # either be a valid integer or one of the symbols +:fill+, +:stroke+,
1943
+ #
1944
+ # Note that the return value is always a normalized text rendering mode value.
1945
+ #
1946
+ # Returns the current text rendering mode value (see GraphicsState#text_rendering_mode) when
1947
+ # no argument is given. Otherwise sets the text rendering mode using the +mode+ argument and
1948
+ # returns +self+. The setter version can also be called in the text_rendering_mode= form.
1837
1949
  #
1838
1950
  # If the +mode+ and a block are provided, the changed text rendering mode is only active
1839
1951
  # during the block by saving and restoring the graphics state.
@@ -1858,7 +1970,7 @@ module HexaPDF
1858
1970
  # canvas.text("#{trm} text.", at: [20, 150 - 30 * index])
1859
1971
  # end
1860
1972
  #
1861
- # See: PDF1.7 s9.3.6, Content::GraphicsState::TextRenderingMode
1973
+ # See: PDF2.0 s9.3.6, GraphicsState::TextRenderingMode
1862
1974
  def text_rendering_mode(m = nil, &bk)
1863
1975
  gs_getter_setter(:text_rendering_mode, :Tr, m && TextRenderingMode.normalize(m), &bk)
1864
1976
  end
@@ -1872,9 +1984,9 @@ module HexaPDF
1872
1984
  # The text rise specifies the vertical distance to move the baseline up or down from its
1873
1985
  # default location. Positive values move the baseline up, negative values down.
1874
1986
  #
1875
- # Returns the current text rise value (see Content::GraphicsState#text_rise) when no argument
1876
- # is given. Otherwise sets the text rise using the +amount+ argument and returns self. The
1877
- # setter version can also be called in the text_rise= form.
1987
+ # Returns the current text rise value (see GraphicsState#text_rise) when no argument is given.
1988
+ # Otherwise sets the text rise using the +amount+ argument and returns +self+. The setter
1989
+ # version can also be called in the text_rise= form.
1878
1990
  #
1879
1991
  # If the +amount+ and a block are provided, the changed text rise is only active during the
1880
1992
  # block by saving and restoring the graphics state.
@@ -1900,7 +2012,7 @@ module HexaPDF
1900
2012
  # canvas.text_rise = -10
1901
2013
  # canvas.text("and also down here")
1902
2014
  #
1903
- # See: PDF1.7 s9.3.7
2015
+ # See: PDF2.0 s9.3.7
1904
2016
  def text_rise(amount = nil, &bk)
1905
2017
  gs_getter_setter(:text_rise, :Ts, amount, &bk)
1906
2018
  end
@@ -1909,7 +2021,7 @@ module HexaPDF
1909
2021
  # :call-seq:
1910
2022
  # canvas.begin_text(force_new: false) -> canvas
1911
2023
  #
1912
- # Begins a new text object.
2024
+ # Begins a new text object and returns +self+.
1913
2025
  #
1914
2026
  # If +force+ is +true+ and the current graphics object is already a text object, it is ended
1915
2027
  # and a new text object is begun.
@@ -1917,7 +2029,7 @@ module HexaPDF
1917
2029
  # It is not necessary to invoke this method manually in most cases since it is automatically
1918
2030
  # called when needed by other methods, i.e. the #text method.
1919
2031
  #
1920
- # See: PDF1.7 s9.4.1
2032
+ # See: PDF2.0 s9.4.1, #end_text, #text
1921
2033
  def begin_text(force_new: false)
1922
2034
  raise_unless_at_page_description_level_or_in_text
1923
2035
  end_text if force_new
@@ -1928,12 +2040,12 @@ module HexaPDF
1928
2040
  # :call-seq:
1929
2041
  # canvas.end_text -> canvas
1930
2042
  #
1931
- # Ends the current text object.
2043
+ # Ends the current text object and returns +self+.
1932
2044
  #
1933
2045
  # It is not necessary to invoke this method manually in most cases since it is automatically
1934
2046
  # called when needed by other methods, i.e. when creating a new path.
1935
2047
  #
1936
- # See: PDF1.7 s9.4.1
2048
+ # See: PDF2.0 s9.4.1, #begin_text
1937
2049
  def end_text
1938
2050
  raise_unless_at_page_description_level_or_in_text
1939
2051
  invoke0(:ET) if graphics_object == :text
@@ -1943,7 +2055,12 @@ module HexaPDF
1943
2055
  # :call-seq:
1944
2056
  # canvas.text_matrix(a, b, c, d, e, f) => canvas
1945
2057
  #
1946
- # Sets the text matrix (and the text line matrix) to the given matrix and returns self.
2058
+ # Sets the text matrix (and the text line matrix) to the given matrix and returns +self+.
2059
+ #
2060
+ # The text matrix determines where and how the glyphs are rendered. The most common use is to
2061
+ # translate the text space origin since the text drawing operations always use the text space
2062
+ # origin as starting point for drawing the glyphs. This translation operation can more easily
2063
+ # be specified using #move_text_cursor.
1947
2064
  #
1948
2065
  # The given values are interpreted as a matrix in the following way:
1949
2066
  #
@@ -1965,7 +2082,7 @@ module HexaPDF
1965
2082
  # canvas.text_matrix(2, 1, 3, 0.5, 50, 50)
1966
2083
  # canvas.text("This is some text")
1967
2084
  #
1968
- # See: PDF1.7 s9.4.2
2085
+ # See: PDF2.0 s9.4.2, #move_text_cursor, #text_cursor
1969
2086
  def text_matrix(a, b, c, d, e, f)
1970
2087
  begin_text
1971
2088
  invoke(:Tm, a, b, c, d, e, f)
@@ -1975,7 +2092,7 @@ module HexaPDF
1975
2092
  # :call-seq:
1976
2093
  # canvas.move_text_cursor(offset: nil, absolute: true) -> canvas
1977
2094
  #
1978
- # Moves the text cursor by modifying the text and text line matrices.
2095
+ # Moves the text cursor by modifying the text and text line matrices. Returns +self+.
1979
2096
  #
1980
2097
  # If +offset+ is not specified, the text cursor is moved to the start of the next text line
1981
2098
  # using #leading as vertical offset.
@@ -2010,7 +2127,7 @@ module HexaPDF
2010
2127
  # canvas.move_text_cursor
2011
2128
  # canvas.text("Text on next line with leading=30")
2012
2129
  #
2013
- # See: PDF1.7 s9.4.2, #show_glyphs
2130
+ # See: PDF2.0 s9.4.2, #leading, #text_cursor, #text, #show_glyphs
2014
2131
  def move_text_cursor(offset: nil, absolute: true)
2015
2132
  begin_text
2016
2133
  if offset
@@ -2028,7 +2145,8 @@ module HexaPDF
2028
2145
  # :call-seq:
2029
2146
  # canvas.text_cursor -> [x, y]
2030
2147
  #
2031
- # Returns the position of the text cursor, i.e. the origin of the current text matrix.
2148
+ # Returns the position of the text cursor, i.e. the origin of text space. This is where the
2149
+ # first glyph of the next drawn text will be placed.
2032
2150
  #
2033
2151
  # Note that this method can only be called while the current graphic object is a text object
2034
2152
  # since the text matrix is otherwise undefined.
@@ -2038,10 +2156,11 @@ module HexaPDF
2038
2156
  # #>pdf
2039
2157
  # canvas.font("Helvetica", size: 10)
2040
2158
  # canvas.text("Some sample text", at: [30, 150])
2041
- # pos = canvas.text_cursor # Cursor is directly after the text
2042
- # canvas.line_width(0.5).stroke_color("red").
2043
- # polyline(pos[0], pos[1] + 10, pos[0], pos[1], pos[0] + 10, pos[1]).stroke
2044
- # canvas.text("Last cursor: #{pos.map! {|f| f.round(2)}.join(", ")}", at: [30, 100])
2159
+ # tx, ty = canvas.text_cursor # Cursor is directly after the text
2160
+ # canvas.stroke_color("hp-blue").
2161
+ # circle(tx, ty, 0.5).
2162
+ # circle(tx, ty, 5).stroke
2163
+ # canvas.text("Last cursor: (#{tx.round(2)}, #{ty.round(2)})", at: [30, 100])
2045
2164
  #
2046
2165
  # See: #move_text_cursor
2047
2166
  def text_cursor
@@ -2055,7 +2174,7 @@ module HexaPDF
2055
2174
  #
2056
2175
  # Specifies the font and optional the font size that should be used when showing text.
2057
2176
  #
2058
- # A valid font size need to be provided on the first invocation, otherwise an error is raised
2177
+ # A valid font size needs to be provided on the first invocation, otherwise an error is raised
2059
2178
  # (this is due to how setting a font works with PDFs).
2060
2179
  #
2061
2180
  # If +size+ is specified, the #font_size method is invoked with it as argument.
@@ -2065,8 +2184,9 @@ module HexaPDF
2065
2184
  # specifies the font variant to use, with standard values of :none, :italic, :bold and
2066
2185
  # :bold_italic.
2067
2186
  #
2068
- # Returns the current font object when no argument is given. *Note* that this is the font
2069
- # object itself, not the PDF dictionary representing the font.
2187
+ # Returns the current font object when no argument is given, otherwise returns +self+. *Note*
2188
+ # that this is the font object itself, not the PDF dictionary representing the font that is
2189
+ # stored in the resources.
2070
2190
  #
2071
2191
  # Examples:
2072
2192
  #
@@ -2080,7 +2200,7 @@ module HexaPDF
2080
2200
  # canvas.font("Times", variant: :bold_italic, size: 15)
2081
2201
  # canvas.text("Times bold+italic at size 15", at: [10, 100])
2082
2202
  #
2083
- # See: PDF1.7 s9.2.2, #font_size
2203
+ # See: PDF2.0 s9.2.2, #font_size, #text
2084
2204
  def font(name = nil, size: nil, **options)
2085
2205
  if name
2086
2206
  @font = (name.respond_to?(:pdf_object) ? name : context.document.fonts.add(name, **options))
@@ -2099,16 +2219,16 @@ module HexaPDF
2099
2219
  alias font= font
2100
2220
 
2101
2221
  # :call-seq:
2102
- # canvas.font_size => font_size
2103
- # canvas.font_size(size => canvas
2222
+ # canvas.font_size => font_size
2223
+ # canvas.font_size(size) => canvas
2104
2224
  #
2105
2225
  # Specifies the font size.
2106
2226
  #
2107
2227
  # Note that an error is raised if no font has been set before via #font (this is due to how
2108
2228
  # setting font and font size works in PDF).
2109
2229
  #
2110
- # Returns the current font size when no argument is given. The setter version can also
2111
- # be called in the font_size= form.
2230
+ # Returns the current font size when no argument is given, otherwise returns +self+. The
2231
+ # setter version can also be called in the font_size= form.
2112
2232
  #
2113
2233
  # Examples:
2114
2234
  #
@@ -2124,7 +2244,7 @@ module HexaPDF
2124
2244
  # canvas.text("Text in size #{size}", at: [15, 180 - index * 20])
2125
2245
  # end
2126
2246
  #
2127
- # See: PDF1.7 s9.2.2, #font
2247
+ # See: PDF2.0 s9.2.2, #font, #text
2128
2248
  def font_size(size = nil)
2129
2249
  if size
2130
2250
  unless @font
@@ -2142,7 +2262,7 @@ module HexaPDF
2142
2262
  # canvas.text(text) -> canvas
2143
2263
  # canvas.text(text, at: [x, y]) -> canvas
2144
2264
  #
2145
- # Shows the given text string, either at the current or the provided position.
2265
+ # Shows the given text string, either at the current or the provided position. Returns +self+.
2146
2266
  #
2147
2267
  # If no position is provided, the text is positioned at the current position of the text
2148
2268
  # cursor (see #text_cursor).
@@ -2152,21 +2272,24 @@ module HexaPDF
2152
2272
  # equal to the font size will be set..
2153
2273
  #
2154
2274
  # Note that there are no provisions to make sure that all text is visible! So if the text
2155
- # string is too long, it will just flow off the page and be cut off.
2275
+ # string is too long, it may be outside the cropped page and be cut off.
2156
2276
  #
2157
2277
  # Examples:
2158
2278
  #
2159
2279
  # #>pdf
2160
2280
  # canvas.font('Times', size: 12)
2161
- # canvas.text("This is a \n multiline text", at: [15, 150]) # Sets leading=12
2162
- # canvas.text(". Some more text\nafter the newline.") # Starts right after the last text
2281
+ # # Sets leading=12 because mulitple lines are drawn
2282
+ # canvas.text("This is a \n multiline text", at: [15, 150])
2283
+ # # Starts right after the last text
2284
+ # canvas.text(". Some more text\nafter the newline.")
2163
2285
  #
2164
- # See: #leading, http://www.unicode.org/reports/tr18/#Line_Boundaries
2286
+ # See: #leading, #font, #font_size, #show_glyphs,
2287
+ # http://www.unicode.org/reports/tr18/#Line_Boundaries
2165
2288
  def text(text, at: nil)
2166
2289
  raise_unless_font_set
2167
2290
  move_text_cursor(offset: at) if at
2168
- leading(font_size) if leading == 0
2169
2291
  lines = text.split(/\u{D A}|(?!\u{D A})[\u{A}-\u{D}\u{85}\u{2028}\u{2029}]/, -1)
2292
+ leading(font_size) if leading == 0 && lines.length > 1
2170
2293
  lines.each_with_index do |str, index|
2171
2294
  show_glyphs(@font.decode_utf8(str))
2172
2295
  move_text_cursor unless index == lines.length - 1
@@ -2177,7 +2300,7 @@ module HexaPDF
2177
2300
  # :call-seq:
2178
2301
  # canvas.show_glyphs(glyphs) -> canvas
2179
2302
  #
2180
- # Low-level method for actually showing text on the canvas.
2303
+ # Low-level method for actually showing text on the canvas. Returns +self+.
2181
2304
  #
2182
2305
  # The argument +glyphs+ needs to be a an array of glyph objects valid for the current font,
2183
2306
  # optionally interspersed with numbers for kerning.
@@ -2200,6 +2323,8 @@ module HexaPDF
2200
2323
  # canvas.move_text_cursor(offset: [15, 100])
2201
2324
  # canvas.show_glyphs(glyphs)
2202
2325
  # canvas.text(canvas.text_cursor.map(&:to_i).join(", "), at: [15, 80])
2326
+ #
2327
+ # See: #text, #text_cursor, #text_matrix, #move_text_cursor, #show_glyphs_only
2203
2328
  def show_glyphs(glyphs)
2204
2329
  return if glyphs.empty?
2205
2330
  raise_unless_font_set
@@ -2269,7 +2394,7 @@ module HexaPDF
2269
2394
  # :call-seq:
2270
2395
  # canvas.marked_content_point(tag, property_list: nil) -> canvas
2271
2396
  #
2272
- # Inserts a marked-content point, optionally associated with a property list.
2397
+ # Inserts a marked-content point, optionally associated with a property list. Returns +self+.
2273
2398
  #
2274
2399
  # A marked-content point is used to identify a position in the content stream for later use by
2275
2400
  # other applications. The symbol +tag+ is used to uniquely identify the role of the
@@ -2284,7 +2409,7 @@ module HexaPDF
2284
2409
  # canvas.marked_content_point(:Divider)
2285
2410
  # canvas.marked_content_point(:Divider, property_list: {Key: 'value'})
2286
2411
  #
2287
- # See: PDF1.7 s14.6
2412
+ # See: PDF2.0 s14.6, #marked_content_sequence, #end_marked_content_sequence
2288
2413
  def marked_content_point(tag, property_list: nil)
2289
2414
  raise_unless_at_page_description_level_or_in_text
2290
2415
  if property_list
@@ -2300,7 +2425,8 @@ module HexaPDF
2300
2425
  # canvas.marked_content_sequence(tag, property_list: nil) -> canvas
2301
2426
  # canvas.marked_content_sequence(tag, property_list: nil) { block } -> canvas
2302
2427
  #
2303
- # Inserts a marked-content sequence, optionally associated with a property list.
2428
+ # Inserts a marked-content sequence, optionally associated with a property list. Returns
2429
+ # +self+.
2304
2430
  #
2305
2431
  # A marked-content sequence is used to identify a sequence of complete graphics objects in the
2306
2432
  # content stream for later use by other applications, e.g. for tagged PDF. The symbol +tag+ is
@@ -2327,7 +2453,7 @@ module HexaPDF
2327
2453
  # # Other instructions
2328
2454
  # end
2329
2455
  #
2330
- # See: PDF1.7 s14.6, #end_marked_content_sequence
2456
+ # See: PDF2.0 s14.6, #end_marked_content_sequence, #marked_content_point
2331
2457
  def marked_content_sequence(tag, property_list: nil)
2332
2458
  raise_unless_at_page_description_level
2333
2459
  if property_list
@@ -2346,22 +2472,22 @@ module HexaPDF
2346
2472
  # :call-seq:
2347
2473
  # canvas.end_marked_content_sequence -> canvas
2348
2474
  #
2349
- # Ends a marked-content sequence.
2475
+ # Ends a marked-content sequence and returns +self+.
2350
2476
  #
2351
2477
  # See #marked_content_sequence for details.
2352
2478
  #
2353
- # See: PDF1.7 s14.6, #marked_content_sequence
2479
+ # See: PDF2.0 s14.6, #marked_content_sequence, #marked_content_point
2354
2480
  def end_marked_content_sequence
2355
2481
  raise_unless_at_page_description_level
2356
2482
  invoke0(:EMC)
2357
2483
  self
2358
2484
  end
2359
2485
 
2360
- # Creates a color object from the given color specification. See #stroke_color for details
2361
- # on the possible color specifications.
2486
+ # Creates and returns a color object from the given color specification. See #stroke_color for
2487
+ # details on the possible color specifications.
2362
2488
  #
2363
2489
  # This utility method is meant for use by higher-level methods that need to convert a color
2364
- # specification into a color object for this Canvas object.
2490
+ # specification into a color object.
2365
2491
  def color_from_specification(spec)
2366
2492
  spec = Array(spec)
2367
2493
  if spec.length == 1 && spec[0].kind_of?(String)