hexapdf 0.32.2 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -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/examples/026-optional_content.rb +55 -0
  19. data/examples/027-composer_optional_content.rb +83 -0
  20. data/lib/hexapdf/cli/command.rb +12 -3
  21. data/lib/hexapdf/cli/fonts.rb +1 -1
  22. data/lib/hexapdf/cli/form.rb +5 -5
  23. data/lib/hexapdf/cli/inspect.rb +5 -7
  24. data/lib/hexapdf/composer.rb +106 -53
  25. data/lib/hexapdf/configuration.rb +65 -40
  26. data/lib/hexapdf/content/canvas.rb +445 -267
  27. data/lib/hexapdf/content/color_space.rb +72 -25
  28. data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
  31. data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
  32. data/lib/hexapdf/content/graphic_object.rb +6 -7
  33. data/lib/hexapdf/content/graphics_state.rb +54 -45
  34. data/lib/hexapdf/content/operator.rb +54 -54
  35. data/lib/hexapdf/content/parser.rb +2 -2
  36. data/lib/hexapdf/content/processor.rb +15 -15
  37. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  38. data/lib/hexapdf/content.rb +5 -0
  39. data/lib/hexapdf/dictionary.rb +7 -5
  40. data/lib/hexapdf/dictionary_fields.rb +43 -16
  41. data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
  42. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  43. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
  44. data/lib/hexapdf/digital_signature/signature.rb +6 -6
  45. data/lib/hexapdf/digital_signature/signatures.rb +13 -12
  46. data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
  47. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
  48. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
  49. data/lib/hexapdf/digital_signature/signing.rb +4 -0
  50. data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
  51. data/lib/hexapdf/digital_signature.rb +7 -2
  52. data/lib/hexapdf/document/destinations.rb +12 -11
  53. data/lib/hexapdf/document/files.rb +1 -1
  54. data/lib/hexapdf/document/fonts.rb +1 -1
  55. data/lib/hexapdf/document/layout.rb +170 -39
  56. data/lib/hexapdf/document/pages.rb +4 -3
  57. data/lib/hexapdf/document.rb +96 -55
  58. data/lib/hexapdf/encryption/aes.rb +5 -5
  59. data/lib/hexapdf/encryption/arc4.rb +1 -1
  60. data/lib/hexapdf/encryption/fast_aes.rb +2 -2
  61. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  62. data/lib/hexapdf/encryption/identity.rb +1 -1
  63. data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
  64. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  65. data/lib/hexapdf/encryption/security_handler.rb +31 -24
  66. data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
  67. data/lib/hexapdf/encryption.rb +7 -2
  68. data/lib/hexapdf/error.rb +18 -0
  69. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  70. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  71. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  72. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  73. data/lib/hexapdf/filter/pass_through.rb +1 -1
  74. data/lib/hexapdf/filter/predictor.rb +1 -1
  75. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  76. data/lib/hexapdf/filter.rb +55 -6
  77. data/lib/hexapdf/font/cmap/parser.rb +2 -2
  78. data/lib/hexapdf/font/cmap.rb +1 -1
  79. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  80. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  81. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
  82. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  83. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  84. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
  85. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  86. data/lib/hexapdf/font/invalid_glyph.rb +3 -0
  87. data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
  88. data/lib/hexapdf/font/type1_wrapper.rb +19 -4
  89. data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
  90. data/lib/hexapdf/font_loader/from_file.rb +5 -5
  91. data/lib/hexapdf/font_loader/standard14.rb +3 -3
  92. data/lib/hexapdf/font_loader.rb +3 -0
  93. data/lib/hexapdf/image_loader/jpeg.rb +2 -2
  94. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  95. data/lib/hexapdf/image_loader/png.rb +2 -2
  96. data/lib/hexapdf/image_loader.rb +1 -1
  97. data/lib/hexapdf/importer.rb +13 -0
  98. data/lib/hexapdf/layout/box.rb +32 -5
  99. data/lib/hexapdf/layout/box_fitter.rb +2 -2
  100. data/lib/hexapdf/layout/column_box.rb +20 -5
  101. data/lib/hexapdf/layout/frame.rb +53 -18
  102. data/lib/hexapdf/layout/image_box.rb +5 -0
  103. data/lib/hexapdf/layout/inline_box.rb +21 -9
  104. data/lib/hexapdf/layout/list_box.rb +50 -20
  105. data/lib/hexapdf/layout/page_style.rb +6 -5
  106. data/lib/hexapdf/layout/style.rb +64 -9
  107. data/lib/hexapdf/layout/table_box.rb +684 -0
  108. data/lib/hexapdf/layout/text_box.rb +12 -3
  109. data/lib/hexapdf/layout/text_fragment.rb +29 -3
  110. data/lib/hexapdf/layout/text_layouter.rb +32 -8
  111. data/lib/hexapdf/layout.rb +1 -0
  112. data/lib/hexapdf/name_tree_node.rb +1 -1
  113. data/lib/hexapdf/number_tree_node.rb +1 -1
  114. data/lib/hexapdf/object.rb +18 -7
  115. data/lib/hexapdf/parser.rb +7 -7
  116. data/lib/hexapdf/pdf_array.rb +1 -1
  117. data/lib/hexapdf/rectangle.rb +1 -1
  118. data/lib/hexapdf/reference.rb +1 -1
  119. data/lib/hexapdf/revision.rb +1 -1
  120. data/lib/hexapdf/revisions.rb +3 -3
  121. data/lib/hexapdf/serializer.rb +15 -15
  122. data/lib/hexapdf/stream.rb +5 -4
  123. data/lib/hexapdf/tokenizer.rb +14 -14
  124. data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
  125. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  126. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  127. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  128. data/lib/hexapdf/type/acro_form/form.rb +1 -1
  129. data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
  130. data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
  131. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  132. data/lib/hexapdf/type/acro_form.rb +1 -1
  133. data/lib/hexapdf/type/action.rb +1 -1
  134. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  135. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  136. data/lib/hexapdf/type/actions/launch.rb +1 -1
  137. data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
  138. data/lib/hexapdf/type/actions/uri.rb +1 -1
  139. data/lib/hexapdf/type/actions.rb +2 -1
  140. data/lib/hexapdf/type/annotation.rb +3 -3
  141. data/lib/hexapdf/type/annotations/link.rb +1 -1
  142. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  143. data/lib/hexapdf/type/annotations/text.rb +2 -3
  144. data/lib/hexapdf/type/annotations/widget.rb +2 -2
  145. data/lib/hexapdf/type/annotations.rb +1 -1
  146. data/lib/hexapdf/type/catalog.rb +11 -2
  147. data/lib/hexapdf/type/cid_font.rb +18 -4
  148. data/lib/hexapdf/type/embedded_file.rb +1 -1
  149. data/lib/hexapdf/type/file_specification.rb +2 -2
  150. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  151. data/lib/hexapdf/type/font_simple.rb +2 -2
  152. data/lib/hexapdf/type/font_type0.rb +3 -3
  153. data/lib/hexapdf/type/font_type3.rb +1 -1
  154. data/lib/hexapdf/type/form.rb +76 -6
  155. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  156. data/lib/hexapdf/type/icon_fit.rb +1 -1
  157. data/lib/hexapdf/type/image.rb +1 -1
  158. data/lib/hexapdf/type/info.rb +1 -1
  159. data/lib/hexapdf/type/mark_information.rb +1 -1
  160. data/lib/hexapdf/type/names.rb +2 -2
  161. data/lib/hexapdf/type/object_stream.rb +2 -1
  162. data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
  163. data/lib/hexapdf/type/optional_content_group.rb +370 -0
  164. data/lib/hexapdf/type/optional_content_membership.rb +63 -0
  165. data/lib/hexapdf/type/optional_content_properties.rb +158 -0
  166. data/lib/hexapdf/type/outline.rb +1 -1
  167. data/lib/hexapdf/type/outline_item.rb +1 -1
  168. data/lib/hexapdf/type/page.rb +46 -21
  169. data/lib/hexapdf/type/page_label.rb +5 -9
  170. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  171. data/lib/hexapdf/type/resources.rb +1 -1
  172. data/lib/hexapdf/type/trailer.rb +2 -2
  173. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  174. data/lib/hexapdf/type/xref_stream.rb +2 -2
  175. data/lib/hexapdf/type.rb +4 -0
  176. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
  177. data/lib/hexapdf/version.rb +1 -1
  178. data/lib/hexapdf/writer.rb +4 -4
  179. data/lib/hexapdf/xref_section.rb +2 -2
  180. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
  181. data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
  182. data/test/hexapdf/content/test_canvas.rb +49 -1
  183. data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
  184. data/test/hexapdf/document/test_files.rb +2 -2
  185. data/test/hexapdf/document/test_layout.rb +105 -2
  186. data/test/hexapdf/document/test_pages.rb +6 -6
  187. data/test/hexapdf/encryption/test_security_handler.rb +12 -11
  188. data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
  189. data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
  190. data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
  191. data/test/hexapdf/layout/test_box.rb +14 -5
  192. data/test/hexapdf/layout/test_column_box.rb +65 -21
  193. data/test/hexapdf/layout/test_frame.rb +27 -15
  194. data/test/hexapdf/layout/test_image_box.rb +4 -0
  195. data/test/hexapdf/layout/test_inline_box.rb +17 -3
  196. data/test/hexapdf/layout/test_list_box.rb +84 -33
  197. data/test/hexapdf/layout/test_page_style.rb +3 -2
  198. data/test/hexapdf/layout/test_style.rb +60 -0
  199. data/test/hexapdf/layout/test_table_box.rb +728 -0
  200. data/test/hexapdf/layout/test_text_box.rb +26 -0
  201. data/test/hexapdf/layout/test_text_fragment.rb +33 -0
  202. data/test/hexapdf/layout/test_text_layouter.rb +36 -5
  203. data/test/hexapdf/test_composer.rb +10 -0
  204. data/test/hexapdf/test_dictionary.rb +10 -0
  205. data/test/hexapdf/test_dictionary_fields.rb +4 -1
  206. data/test/hexapdf/test_document.rb +5 -0
  207. data/test/hexapdf/test_filter.rb +8 -0
  208. data/test/hexapdf/test_importer.rb +9 -0
  209. data/test/hexapdf/test_object.rb +16 -5
  210. data/test/hexapdf/test_stream.rb +7 -0
  211. data/test/hexapdf/test_writer.rb +3 -3
  212. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
  213. data/test/hexapdf/type/acro_form/test_form.rb +4 -3
  214. data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
  215. data/test/hexapdf/type/test_catalog.rb +11 -0
  216. data/test/hexapdf/type/test_form.rb +119 -0
  217. data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
  218. data/test/hexapdf/type/test_optional_content_group.rb +158 -0
  219. data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
  220. data/test/hexapdf/type/test_page.rb +20 -6
  221. metadata +28 -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.
@@ -302,11 +317,19 @@ module HexaPDF
302
317
  @context.resources
303
318
  end
304
319
 
320
+ # Returns the position (x,y) transformed by the current transformation matrix.
321
+ #
322
+ # The resulting position should be interpreted in terms of the coordinate system of the
323
+ # context object (e.g. the page or Form XObject).
324
+ def pos(x, y)
325
+ graphics_state.ctm.evaluate(x, y)
326
+ end
327
+
305
328
  # :call-seq:
306
329
  # canvas.save_graphics_state => canvas
307
330
  # canvas.save_graphics_state { block } => canvas
308
331
  #
309
- # Saves the current graphics state and returns self.
332
+ # Saves the current graphics state and returns +self+.
310
333
  #
311
334
  # If invoked without a block a corresponding call to #restore_graphics_state must be done to
312
335
  # ensure proper nesting. Otherwise, i.e. when invoked with a block, the graphics state is
@@ -320,7 +343,7 @@ module HexaPDF
320
343
  # #>pdf
321
344
  # # With a block
322
345
  # canvas.save_graphics_state do
323
- # canvas.stroke_color("red") # After the block the color is reset
346
+ # canvas.stroke_color("hp-blue") # After the block the color is reset
324
347
  # canvas.line(20, 20, 70, 180).stroke
325
348
  # end
326
349
  # canvas.line(60, 20, 110, 180).stroke
@@ -332,7 +355,7 @@ module HexaPDF
332
355
  # restore_graphics_state
333
356
  # canvas.line(140, 20, 190, 180).stroke
334
357
  #
335
- # See: PDF1.7 s8.4.2, #restore_graphics_state
358
+ # See: PDF2.0 s8.4.2, #restore_graphics_state
336
359
  def save_graphics_state
337
360
  raise_unless_at_page_description_level
338
361
  invoke0(:q)
@@ -347,11 +370,20 @@ module HexaPDF
347
370
  # :call-seq:
348
371
  # canvas.restore_graphics_state => canvas
349
372
  #
350
- # Restores the current graphics state and returns self.
373
+ # Restores the graphics state to the last saved version and returns +self+.
351
374
  #
352
375
  # Must not be invoked more times than #save_graphics_state.
353
376
  #
354
- # See: PDF1.7 s8.4.2, #save_graphics_state
377
+ # Example:
378
+ #
379
+ # #>pdf
380
+ # canvas.save_graphics_state
381
+ # canvas.circle(100, 100, 50).clip_path.end_path
382
+ # canvas.fill_color("hp-blue").rectangle(0, 0, 100, 100).fill
383
+ # canvas.restore_graphics_state
384
+ # canvas.rectangle(100, 0, 100, 100).fill
385
+ #
386
+ # See: PDF2.0 s8.4.2, #save_graphics_state
355
387
  def restore_graphics_state
356
388
  raise_unless_at_page_description_level
357
389
  invoke0(:Q)
@@ -363,7 +395,7 @@ module HexaPDF
363
395
  # canvas.transform(a, b, c, d, e, f) => canvas
364
396
  # canvas.transform(a, b, c, d, e, f) { block } => canvas
365
397
  #
366
- # Transforms the user space by applying the given matrix to the current transformation
398
+ # Transforms the coordinate system by applying the given matrix to the current transformation
367
399
  # matrix and returns self.
368
400
  #
369
401
  # If invoked with a block, the transformation is only active during the block by saving and
@@ -375,16 +407,16 @@ module HexaPDF
375
407
  # c d 0
376
408
  # e f 1
377
409
  #
378
- # Examples:
410
+ # Example:
379
411
  #
380
412
  # #>pdf
381
413
  # canvas.transform(1, 0, 0, 1, 100, 100) do # Translate origin to (100, 100)
382
- # canvas.stroke_color("red").
414
+ # canvas.stroke_color("hp-blue").
383
415
  # line(0, 0, 100, 50).stroke # Actually from (100, 100) to (200, 150)
384
416
  # end
385
417
  # canvas.line(0, 0, 100, 50).stroke # Really from (0, 0) to (100, 50)
386
418
  #
387
- # See: PDF1.7 s8.3, s8.4.4
419
+ # See: PDF2.0 s8.3, s8.4.4
388
420
  def transform(a, b, c, d, e, f)
389
421
  raise_unless_at_page_description_level
390
422
  save_graphics_state if block_given?
@@ -400,30 +432,31 @@ module HexaPDF
400
432
  # canvas.rotate(angle, origin: nil) => canvas
401
433
  # canvas.rotate(angle, origin: nil) { block } => canvas
402
434
  #
403
- # Rotates the user space +angle+ degrees around the coordinate system origin or around the
404
- # given point and returns self.
435
+ # Rotates the coordinate system +angle+ degrees around the origin or around the given point
436
+ # and returns +self+.
405
437
  #
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.
438
+ # If invoked with a block, the rotation of the coordinate system is only active during the
439
+ # block by saving and restoring the graphics state.
408
440
  #
409
441
  # Note that the origin of the coordinate system itself doesn't change even if the +origin+
410
442
  # argument is given!
411
443
  #
412
444
  # origin::
413
- # The point around which the user space should be rotated.
445
+ # The point around which the coordinate system should be rotated.
414
446
  #
415
447
  # Examples:
416
448
  #
417
449
  # #>pdf-center
418
- # canvas.stroke_color("lightgrey").
419
- # rectangle(0, 0, 60, 40).stroke
450
+ # canvas.stroke_color("hp-gray-light").
451
+ # rectangle(0, 0, 60, 40).stroke # The rectangle that gets rotated
452
+ #
420
453
  # canvas.rotate(45) do # Positive x-axis pointing to top-right corner
421
- # canvas.stroke_color("red").
454
+ # canvas.stroke_color("hp-blue").
422
455
  # rectangle(0, 0, 60, 40).stroke
423
456
  # end
424
457
  #
425
458
  # canvas.rotate(-45, origin: [-50, -50]) do # Rotate around (-50,-50)
426
- # canvas.stroke_color("blue").
459
+ # canvas.stroke_color("hp-orange").
427
460
  # rectangle(0, 0, 60, 40).stroke
428
461
  # end
429
462
  #
@@ -443,8 +476,8 @@ module HexaPDF
443
476
  # canvas.scale(sx, sy = sx, origin: nil) => canvas
444
477
  # canvas.scale(sx, sy = sx, origin: nil) { block } => canvas
445
478
  #
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
479
+ # Scales the coordinate system +sx+ units in the horizontal and +sy+ units in the vertical
480
+ # direction and returns +self+. If the optional +origin+ is specified, scaling is done from
448
481
  # that point.
449
482
  #
450
483
  # If invoked with a block, the scaling is only active during the block by saving and
@@ -454,20 +487,21 @@ module HexaPDF
454
487
  # argument is given!
455
488
  #
456
489
  # origin::
457
- # The point from which the user space should be scaled.
490
+ # The point from which the coordinate system should be scaled.
458
491
  #
459
492
  # Examples:
460
493
  #
461
494
  # #>pdf-center
462
- # canvas.stroke_color("lightgrey").
463
- # rectangle(10, 10, 10, 10).stroke
495
+ # canvas.stroke_color("hp-gray-light").
496
+ # rectangle(10, 10, 10, 10).stroke # The rectangle that gets scaled
497
+ #
464
498
  # canvas.scale(4, 2) do # Scale from origin
465
- # canvas.stroke_color("blue").
499
+ # canvas.stroke_color("hp-blue").
466
500
  # rectangle(10, 10, 10, 10).stroke # Actually (40, 20) to (80, 40)
467
501
  # end
468
502
  #
469
503
  # canvas.scale(-2, 4, origin: [10, 10]) do # Scale from (10, 10)
470
- # canvas.stroke_color("red").
504
+ # canvas.stroke_color("hp-orange").
471
505
  # rectangle(10, 10, 10, 10).stroke # Actually (10, 10) to (-10, 40)
472
506
  # end
473
507
  #
@@ -484,18 +518,19 @@ module HexaPDF
484
518
  # canvas.translate(x, y) => canvas
485
519
  # canvas.translate(x, y) { block } => canvas
486
520
  #
487
- # Translates the user space coordinate system origin to the given +x+ and +y+ coordinates
488
- # and returns self.
521
+ # Translates the coordinate system coordinate system origin to the given +x+ and +y+
522
+ # coordinates and returns +self+.
489
523
  #
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.
524
+ # If invoked with a block, the translation of the coordinate system is only active during the
525
+ # block by saving and restoring the graphics state.
492
526
  #
493
527
  # Examples:
494
528
  #
495
529
  # #>pdf-center
496
- # canvas.rectangle(0, 0, 40, 20).stroke # Rectangle from (0, 0) to (40, 20)
530
+ # canvas.stroke_color("hp-gray-light").
531
+ # rectangle(0, 0, 40, 20).stroke # Rectangle from (0, 0) to (40, 20)
497
532
  # canvas.translate(50, 50) do # Origin is now at (50, 50)
498
- # canvas.stroke_color("red").
533
+ # canvas.stroke_color("hp-blue").
499
534
  # rectangle(0, 0, 40, 20).stroke # Actually (50, 50) to (90, 70)
500
535
  # end
501
536
  #
@@ -508,7 +543,7 @@ module HexaPDF
508
543
  # canvas.skew(a, b, origin: nil) => canvas
509
544
  # canvas.skew(a, b, origin: nil) { block } => canvas
510
545
  #
511
- # Skews the the x-axis by +a+ degrees and the y-axis by +b+ degress and returns self. If the
546
+ # Skews the the x-axis by +a+ degrees and the y-axis by +b+ degress and returns +self+. If the
512
547
  # optional +origin+ is specified, skewing is done from that point.
513
548
  #
514
549
  # If invoked with a block, the skewing is only active during the block by saving and
@@ -522,15 +557,16 @@ module HexaPDF
522
557
  # Examples:
523
558
  #
524
559
  # #>pdf-center
525
- # canvas.stroke_color("lightgrey").
526
- # rectangle(10, 10, 40, 20).stroke
560
+ # canvas.stroke_color("hp-gray-light").
561
+ # rectangle(10, 10, 40, 20).stroke # The rectangle that gets skewed
562
+ #
527
563
  # canvas.skew(0, 30) do # Point (10, 10) is now actually (15, 10)
528
- # canvas.stroke_color("blue").
564
+ # canvas.stroke_color("hp-blue").
529
565
  # rectangle(10, 10, 40, 20).stroke # Now a parallelogram
530
566
  # end
531
567
  #
532
568
  # canvas.skew(30, 30, origin: [-50, 50]) do # Skew from (-50, 50)
533
- # canvas.stroke_color("red").
569
+ # canvas.stroke_color("hp-orange").
534
570
  # rectangle(-50, 50, 20, 20).stroke
535
571
  # end
536
572
  #
@@ -553,9 +589,15 @@ module HexaPDF
553
589
  #
554
590
  # The line width determines the thickness of a stroked path.
555
591
  #
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.
592
+ # Note that half the line width lies on either side of the path. For example, if a path from
593
+ # (0, 0) to (0, 100) is drawn with a line width of 20, the stroked path is actually 20 units
594
+ # wide, from -10 to 10. And if a rectangle is drawn stroked, but not filled, from (20, 20)
595
+ # with a width and height of 20 and a line width of 10, the "inside" of the rectangle would
596
+ # only be from (25, 25) to (35, 35). Also see the examples below.
597
+ #
598
+ # Returns the current line width (see GraphicsState#line_width) when no argument is given.
599
+ # Otherwise sets the line width to the given +width+ and returns +self+. The setter version
600
+ # can also be called in the line_width= form.
559
601
  #
560
602
  # If the +width+ and a block are provided, the changed line width is only active during the
561
603
  # block by saving and restoring the graphics state.
@@ -564,19 +606,22 @@ module HexaPDF
564
606
  #
565
607
  # #>pdf
566
608
  # canvas.line_width(10).
567
- # line(10, 10, 10, 190).stroke
609
+ # line(10, 100, 10, 190).stroke
568
610
  # canvas.line_width # => 10
569
611
  # canvas.line_width = 5 # => 5
570
- # canvas.line(60, 10, 60, 190).stroke
612
+ # canvas.line(60, 100, 60, 190).stroke
571
613
  #
572
614
  # canvas.line_width(10) do
573
615
  # canvas.line_width # => 10
574
- # canvas.line(110, 10, 110, 190).stroke
616
+ # canvas.line(110, 100, 110, 190).stroke
575
617
  # end
576
618
  # canvas.line_width # => 5
577
- # canvas.line(160, 10, 160, 190).stroke
619
+ # canvas.line(160, 100, 160, 190).stroke
578
620
  #
579
- # See: PDF1.7 s8.4.3.2
621
+ # canvas.line_width(10).rectangle(20, 20, 20, 20).stroke # The rectangle
622
+ # canvas.fill_color("hp-blue").rectangle(25, 25, 10, 10).fill # The inside
623
+ #
624
+ # See: PDF2.0 s8.4.3.2
580
625
  def line_width(width = nil, &block)
581
626
  gs_getter_setter(:line_width, :w, width, &block)
582
627
  end
@@ -587,7 +632,7 @@ module HexaPDF
587
632
  # canvas.line_cap_style(style) => canvas
588
633
  # canvas.line_cap_style(style) { block } => canvas
589
634
  #
590
- # The line cap style specifies how the ends of stroked open paths should look like.
635
+ # The line cap style specifies how the ends of stroked, open paths should look like.
591
636
  #
592
637
  # The +style+ parameter can be one of (also see LineCapStyle):
593
638
  #
@@ -598,11 +643,11 @@ module HexaPDF
598
643
  # :projecting_square or 2::
599
644
  # The stroke continues half the line width beyond the endpoint of a path.
600
645
  #
601
- # Note that the return value is always a normalized line cap style.
646
+ # Note that the return value is always a normalized line cap style (i.e. a NamedValue).
602
647
  #
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.
648
+ # Returns the current line cap style (see GraphicsState#line_cap_style) when no argument is
649
+ # given. Otherwise sets the line cap style to the given +style+ and returns +self+. The setter
650
+ # version can also be called in the line_cap_style= form.
606
651
  #
607
652
  # If the +style+ and a block are provided, the changed line cap style is only active during
608
653
  # the block by saving and restoring the graphics state.
@@ -628,7 +673,7 @@ module HexaPDF
628
673
  # line(50 + index * 50, 30, 50 + index * 50, 170).stroke
629
674
  # end
630
675
  #
631
- # See: PDF1.7 s8.4.3.3, Content::LineCapStyle
676
+ # See: PDF2.0 s8.4.3.3, Content::LineCapStyle
632
677
  def line_cap_style(style = nil, &block)
633
678
  gs_getter_setter(:line_cap_style, :J, style && LineCapStyle.normalize(style), &block)
634
679
  end
@@ -651,11 +696,11 @@ module HexaPDF
651
696
  # The two segments are finished with butt caps and the space between the ends is filled
652
697
  # with a triangle.
653
698
  #
654
- # Note that the return value is always a normalized line join style.
699
+ # Note that the return value is always a normalized line join style (i.e. a NamedValue).
655
700
  #
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.
701
+ # Returns the current line join style (see GraphicsState#line_join_style) when no argument is
702
+ # given. Otherwise sets the line join style to the given +style+ and returns +self+. The
703
+ # setter version can also be called in the line_join_style= form.
659
704
  #
660
705
  # If the +style+ and a block are provided, the changed line join style is only active during
661
706
  # the block by saving and restoring the graphics state.
@@ -681,7 +726,7 @@ module HexaPDF
681
726
  # polyline(20 + index * 60, 30, 40 + index * 60, 170, 60 + index * 60, 30).stroke
682
727
  # end
683
728
  #
684
- # See: PDF1.7 s8.4.3.4, Content::LineJoinStyle
729
+ # See: PDF2.0 s8.4.3.4, Content::LineJoinStyle
685
730
  def line_join_style(style = nil, &block)
686
731
  gs_getter_setter(:line_join_style, :j, style && LineJoinStyle.normalize(style), &block)
687
732
  end
@@ -696,9 +741,9 @@ module HexaPDF
696
741
  # mitered line joins (see #line_join_style). When the limit is exceeded, a bevel join is
697
742
  # used instead of a miter join.
698
743
  #
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.
744
+ # Returns the current miter limit (see GraphicsState#miter_limit) when no argument is given.
745
+ # Otherwise sets the miter limit to the given +limit+ and returns +self+. The setter version
746
+ # can also be called in the miter_limit= form.
702
747
  #
703
748
  # If the +limit+ and a block are provided, the changed miter limit is only active during the
704
749
  # block by saving and restoring the graphics state.
@@ -722,7 +767,7 @@ module HexaPDF
722
767
  # 60 + index * 80, 30).stroke
723
768
  # end
724
769
  #
725
- # See: PDF1.7 s8.4.3.5
770
+ # See: PDF2.0 s8.4.3.5
726
771
  def miter_limit(limit = nil, &block)
727
772
  gs_getter_setter(:miter_limit, :M, limit, &block)
728
773
  end
@@ -740,7 +785,7 @@ module HexaPDF
740
785
  #
741
786
  # There are multiple ways to set the line dash pattern:
742
787
  #
743
- # * By providing a Content::LineDashPattern object
788
+ # * By providing a LineDashPattern object
744
789
  # * By providing a single Integer/Float that is used for both dashes and gaps
745
790
  # * By providing an array of Integers/Floats that specify the alternating dashes and gaps
746
791
  #
@@ -749,10 +794,10 @@ module HexaPDF
749
794
  #
750
795
  # A solid line can be achieved by using 0 for the length or by using an empty array.
751
796
  #
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!).
797
+ # Returns the current line dash pattern (a LineDashPattern object, see
798
+ # GraphicsState#line_dash_pattern) when no argument is given. Otherwise sets the line dash
799
+ # pattern using the given arguments and returns +self+. The setter version can also be called
800
+ # in the line_dash_pattern= form (but only without the second argument!).
756
801
  #
757
802
  # If arguments and a block are provided, the changed line dash pattern is only active during
758
803
  # the block by saving and restoring the graphics state.
@@ -778,7 +823,7 @@ module HexaPDF
778
823
  # stroke
779
824
  # end
780
825
  #
781
- # See: PDF1.7 s8.4.3.5, LineDashPattern
826
+ # See: PDF2.0 s8.4.3.5, LineDashPattern
782
827
  def line_dash_pattern(value = nil, phase = 0, &block)
783
828
  gs_getter_setter(:line_dash_pattern, :d, value && LineDashPattern.normalize(value, phase),
784
829
  &block)
@@ -799,9 +844,9 @@ module HexaPDF
799
844
  # * +:Saturation+
800
845
  # * +:Perceptual+
801
846
  #
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.
847
+ # Returns the current rendering intent (see GraphicsState#rendering_intent) when no argument
848
+ # is given. Otherwise sets the rendering intent using the +intent+ argument and returns
849
+ # +self+. The setter version can also be called in the rendering_intent= form.
805
850
  #
806
851
  # If the +intent+ and a block are provided, the changed rendering intent is only active
807
852
  # during the block by saving and restoring the graphics state.
@@ -817,7 +862,7 @@ module HexaPDF
817
862
  # end
818
863
  # canvas.rendering_intent # => :Saturation
819
864
  #
820
- # See: PDF1.7 s8.6.5.8, RenderingIntent
865
+ # See: PDF2.0 s8.6.5.8, RenderingIntent
821
866
  def rendering_intent(intent = nil, &bk)
822
867
  gs_getter_setter(:rendering_intent, :ri, intent && RenderingIntent.normalize(intent), &bk)
823
868
  end
@@ -837,23 +882,30 @@ module HexaPDF
837
882
  #
838
883
  # There are several ways to define the color that should be used:
839
884
  #
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).
885
+ # * A single numeric argument specifies a gray color (see ColorSpace::DeviceGray::Color).
886
+ #
887
+ # * Three numeric arguments specify an RGB color (see ColorSpace::DeviceRGB::Color).
888
+ #
843
889
  # * A string in the format "RRGGBB" where "RR" is the hexadecimal number for the red, "GG"
844
890
  # for the green and "BB" for the blue color value also specifies an RGB color.
891
+ #
845
892
  # * As does a string in the format "RGB" where "RR", "GG" and "BB" would be used as the
846
893
  # 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).
894
+ #
895
+ # * Any other string is treated as a color name. HexaPDF supports CSS Color Module Level 3
896
+ # color names (see https://www.w3.org/TR/css-color-3/#svg-color) as well as HexaPDF design
897
+ # colors.
898
+ #
899
+ # * Four numeric arguments specify a CMYK color (see ColorSpace::DeviceCMYK::Color).
900
+ #
850
901
  # * A color object is used directly (normally used for color spaces other than DeviceRGB,
851
902
  # DeviceCMYK and DeviceGray).
903
+ #
852
904
  # * An array is treated as if its items were specified separately as arguments.
853
905
  #
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.
906
+ # Returns the current stroke color (see GraphicsState#stroke_color) when no argument is given.
907
+ # Otherwise sets the stroke color using the given arguments and returns +self+. The setter
908
+ # version can also be called in the stroke_color= form.
857
909
  #
858
910
  # If the arguments and a block are provided, the changed stroke color is only active during
859
911
  # the block by saving and restoring the graphics state.
@@ -896,7 +948,7 @@ module HexaPDF
896
948
  # canvas.stroke_color # => ColorSpace::DeviceGray.color(0.4)
897
949
  # end
898
950
  #
899
- # See: PDF1.7 s8.6, ColorSpace
951
+ # See: PDF2.0 s8.6, ColorSpace
900
952
  def stroke_color(*color, &block)
901
953
  color_getter_setter(:stroke_color, color, :RG, :G, :K, :CS, :SCN, &block)
902
954
  end
@@ -922,10 +974,10 @@ module HexaPDF
922
974
  # the fill alpha value applies not just to fill values but to all non-stroking operations
923
975
  # (e.g. images, ...).
924
976
  #
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
977
+ # Returns the current fill alpha (see GraphicsState#fill_alpha) and stroke alpha (see
978
+ # GraphicsState#stroke_alpha) values using a hash with the keys +:fill_alpha+ and
927
979
  # +: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.
980
+ # and returns +self+. The setter version can also be called in the #opacity= form.
929
981
  #
930
982
  # If the values are set and a block is provided, the changed alpha values are only active
931
983
  # during the block by saving and restoring the graphics state.
@@ -944,15 +996,15 @@ module HexaPDF
944
996
  # canvas.opacity # => {fill_alpha: 0.4, stroke_alpha: 0.9}
945
997
  #
946
998
  # # 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
999
+ # canvas.opacity(fill_alpha: 1, stroke_alpha: 1)
1000
+ # canvas.fill_color("hp-gray-light"). # background rectangle on right side
1001
+ # rectangle(100, 0, 100, 200).fill
950
1002
  # canvas.opacity(fill_alpha: 0.5, stroke_alpha: 0.8). # foreground rectangle, with a thick
951
1003
  # line_width(20). # stroke that also overlays the
952
- # fill_color("red").stroke_color("blue"). # inside of the rectangle, creating
1004
+ # fill_color("hp-blue").stroke_color("hp-blue"). # inside of the rectangle, creating
953
1005
  # rectangle(20, 20, 160, 160).fill_stroke # multiple shadings due to opacity
954
1006
  #
955
- # See: PDF1.7 s11.6.4.4
1007
+ # See: PDF2.0 s11.6.4.4
956
1008
  def opacity(fill_alpha: nil, stroke_alpha: nil)
957
1009
  if !fill_alpha.nil? || !stroke_alpha.nil?
958
1010
  raise_unless_at_page_description_level_or_in_text
@@ -981,14 +1033,14 @@ module HexaPDF
981
1033
  # canvas.move_to(x, y) => canvas
982
1034
  #
983
1035
  # Begins a new subpath (and possibly a new path) by moving the current point to the given
984
- # point.
1036
+ # point and returns +self+.
985
1037
  #
986
1038
  # Examples:
987
1039
  #
988
1040
  # canvas.move_to(10, 50)
989
1041
  # canvas.current_point # => [10, 50]
990
1042
  #
991
- # See: PDF1.7 s8.5.2.1
1043
+ # See: PDF2.0 s8.5.2.1, #line_to, #curve_to, #rectangle
992
1044
  def move_to(x, y)
993
1045
  raise_unless_at_page_description_level_or_in_path
994
1046
  invoke2(:m, x, y)
@@ -1001,17 +1053,20 @@ module HexaPDF
1001
1053
  # canvas.line_to(x, y) => canvas
1002
1054
  #
1003
1055
  # Appends a straight line segment from the current point to the given point (which becomes the
1004
- # new current point) to the current subpath.
1056
+ # new current point) to the current subpath and returns +self+.
1057
+ #
1058
+ # If there is no current path when the method is invoked, an error is raised since a valid
1059
+ # current point (#current_point) is needed.
1005
1060
  #
1006
1061
  # Examples:
1007
1062
  #
1008
1063
  # #>pdf-center
1009
- # canvas.move_to(10, 50).
1010
- # line_to(80, 80)
1011
- # canvas.current_point # => [80, 80]
1064
+ # canvas.move_to(10, 50)
1065
+ # canvas.line_to(80, 80)
1066
+ # canvas.current_point # => [80, 80]
1012
1067
  # canvas.stroke
1013
1068
  #
1014
- # See: PDF1.7 s8.5.2.1
1069
+ # See: PDF2.0 s8.5.2.1, #move_to, #curve_to, #rectangle
1015
1070
  def line_to(x, y)
1016
1071
  raise_unless_in_path
1017
1072
  invoke2(:l, x, y)
@@ -1025,8 +1080,11 @@ module HexaPDF
1025
1080
  # canvas.curve_to(x, y, p1:) => canvas
1026
1081
  # canvas.curve_to(x, y, p2:) => canvas
1027
1082
  #
1028
- # Appends a cubic Bezier curve to the current subpath starting from the current point. The end
1029
- # point becomes the new current point.
1083
+ # Appends a cubic Bezier curve to the current subpath starting from the current point and
1084
+ # returns +self+. The end point becomes the new current point.
1085
+ #
1086
+ # If there is no current path when the method is invoked, an error is raised since a valid
1087
+ # current point (#current_point) is needed.
1030
1088
  #
1031
1089
  # A Bezier curve consists of the start point, the end point and the two control points +p1+
1032
1090
  # and +p2+. The start point is always the current point and the end point is specified as the
@@ -1047,7 +1105,7 @@ module HexaPDF
1047
1105
  # canvas.current_point # => [-30, 60]
1048
1106
  # canvas.stroke
1049
1107
  #
1050
- # See: PDF1.7 s8.5.2.2
1108
+ # See: PDF2.0 s8.5.2.2, #move_to, #line_to, #rectangle
1051
1109
  def curve_to(x, y, p1: nil, p2: nil)
1052
1110
  raise_unless_in_path
1053
1111
  if p1 && p2
@@ -1069,10 +1127,13 @@ module HexaPDF
1069
1127
  #
1070
1128
  # Appends a rectangle to the current path as a complete subpath (drawn in counterclockwise
1071
1129
  # direction), with the bottom left corner specified by +x+ and +y+ and the given +width+ and
1072
- # +height+.
1130
+ # +height+. Returns +self+.
1073
1131
  #
1074
1132
  # If +radius+ is greater than 0, the corners are rounded with the given radius.
1075
1133
  #
1134
+ # Note that the rectangle degrades to a line if either width or height is zero and to nothing
1135
+ # if both are zero.
1136
+ #
1076
1137
  # If there is no current path when the method is invoked, a new path is automatically begun.
1077
1138
  #
1078
1139
  # The current point is set to the bottom left corner if +radius+ is zero, otherwise it is set
@@ -1083,10 +1144,10 @@ module HexaPDF
1083
1144
  # #>pdf
1084
1145
  # canvas.rectangle(10, 110, 80, 50).stroke
1085
1146
  # 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
1147
+ # canvas.rectangle(10, 90, 80, 0).stroke # Degraded: Just a line
1148
+ # canvas.rectangle(110, 90, 0, 0).stroke # Degraded: Draws nothing
1088
1149
  #
1089
- # See: PDF1.7 s8.5.2.1
1150
+ # See: PDF2.0 s8.5.2.1, #move_to, #line_to, #curve_to
1090
1151
  def rectangle(x, y, width, height, radius: 0)
1091
1152
  raise_unless_at_page_description_level_or_in_path
1092
1153
  if radius == 0
@@ -1103,7 +1164,10 @@ module HexaPDF
1103
1164
  # canvas.close_subpath => canvas
1104
1165
  #
1105
1166
  # 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.
1167
+ # start point of the subpath which also becomes the new current point. Returns +self+.
1168
+ #
1169
+ # If there is no current path when the method is invoked, an error is raised since a valid
1170
+ # current point (#current_point) is needed.
1107
1171
  #
1108
1172
  # Examples:
1109
1173
  #
@@ -1114,7 +1178,7 @@ module HexaPDF
1114
1178
  # close_subpath. # Draws the line from (60, 60) to (10, 10)
1115
1179
  # stroke
1116
1180
  #
1117
- # See: PDF1.7 s8.5.2.1
1181
+ # See: PDF2.0 s8.5.2.1
1118
1182
  def close_subpath
1119
1183
  raise_unless_in_path
1120
1184
  invoke0(:h)
@@ -1126,13 +1190,16 @@ module HexaPDF
1126
1190
  # canvas.line(x0, y0, x1, y1) => canvas
1127
1191
  #
1128
1192
  # Moves the current point to (x0, y0) and appends a line to (x1, y1) to the current path.
1193
+ # Returns +self+.
1129
1194
  #
1130
- # This method is equal to "canvas.move_to(x0, y0).line_to(x1, y1)".
1195
+ # If there is no current path when the method is invoked, a new path is automatically begun.
1131
1196
  #
1132
1197
  # Examples:
1133
1198
  #
1134
1199
  # #>pdf
1135
1200
  # canvas.line(10, 10, 100, 100).stroke
1201
+ #
1202
+ # See: #move_to, #line_to
1136
1203
  def line(x0, y0, x1, y1)
1137
1204
  move_to(x0, y0)
1138
1205
  line_to(x1, y1)
@@ -1143,12 +1210,16 @@ module HexaPDF
1143
1210
  #
1144
1211
  # Moves the current point to (x0, y0) and appends line segments between all given
1145
1212
  # 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.
1213
+ # so on. The last point becomes the new current point. Returns +self+.
1214
+ #
1215
+ # If there is no current path when the method is invoked, a new path is automatically begun.
1147
1216
  #
1148
1217
  # Examples:
1149
1218
  #
1150
1219
  # #>pdf
1151
1220
  # canvas.polyline(50, 50, 150, 50, 150, 150, 50, 150, 50, 50).stroke
1221
+ #
1222
+ # See: #move_to, #line_to, #polygon
1152
1223
  def polyline(*points)
1153
1224
  check_poly_points(points)
1154
1225
  move_to(points[0], points[1])
@@ -1163,8 +1234,8 @@ module HexaPDF
1163
1234
  # :call-seq:
1164
1235
  # canvas.polygon(x0, y0, x1, y1, x2, y2, ..., radius: 0) => canvas
1165
1236
  #
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.
1237
+ # Appends a polygon consisting of the given points to the path as a complete subpath and
1238
+ # returns +self+. The point (x0, y0 + radius) becomes the new current point.
1168
1239
  #
1169
1240
  # If +radius+ is greater than 0, the corners are rounded with the given radius.
1170
1241
  #
@@ -1174,8 +1245,10 @@ module HexaPDF
1174
1245
  #
1175
1246
  # #>pdf
1176
1247
  # canvas.polygon(10, 10, 90, 10, 70, 90, 20, 100).stroke
1177
- # canvas.stroke_color("red").
1248
+ # canvas.stroke_color("hp-blue").
1178
1249
  # polygon(130, 130, 150, 100, 170, 150, 130, 190, radius: 10).stroke
1250
+ #
1251
+ # See: #polyline
1179
1252
  def polygon(*points, radius: 0)
1180
1253
  if radius == 0
1181
1254
  polyline(*points)
@@ -1195,16 +1268,19 @@ module HexaPDF
1195
1268
  #
1196
1269
  # Appends a circle with center (cx, cy) and the given radius (in degrees) to the path as a
1197
1270
  # complete subpath (drawn in counterclockwise direction). The point (center_x + radius,
1198
- # center_y) becomes the new current point.
1271
+ # center_y) becomes the new current point. Returns +self+.
1199
1272
  #
1200
1273
  # If there is no current path when the method is invoked, a new path is automatically begun.
1201
1274
  #
1202
1275
  # Examples:
1203
1276
  #
1204
1277
  # #>pdf
1205
- # canvas.circle(100, 100, 30).stroke
1278
+ # canvas.circle(100, 100, 30)
1279
+ # cp = canvas.current_point
1280
+ # canvas.stroke
1281
+ # canvas.stroke_color("hp-orange").line(*cp, 180, 100).stroke
1206
1282
  #
1207
- # See: #arc (for approximation accuracy)
1283
+ # See: #arc (for approximation accuracy), #ellipse
1208
1284
  def circle(cx, cy, radius)
1209
1285
  arc(cx, cy, a: radius)
1210
1286
  close_subpath
@@ -1215,7 +1291,8 @@ module HexaPDF
1215
1291
  #
1216
1292
  # Appends an ellipse with center (cx, cy), semi-major axis +a+, semi-minor axis +b+ and an
1217
1293
  # 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.
1294
+ # outer-most point on the positive semi-major axis becomes the new current point. Returns
1295
+ # self.
1219
1296
  #
1220
1297
  # If there is no current path when the method is invoked, a new path is automatically begun.
1221
1298
  #
@@ -1225,11 +1302,15 @@ module HexaPDF
1225
1302
  # # Ellipse aligned to x-axis and y-axis
1226
1303
  # canvas.ellipse(50, 50, a: 20, b: 10).stroke
1227
1304
  #
1228
- # # Inclined ellipse
1229
- # canvas.stroke_color("red").
1230
- # ellipse(150, 150, a: 20, b: 10, inclination: 30).stroke
1305
+ # # Inclined ellipse with line from the end point
1306
+ # canvas.stroke_color("hp-blue").
1307
+ # ellipse(150, 150, a: 20, b: 10, inclination: 30)
1308
+ # cp = canvas.current_point
1309
+ # x, y = 2 * canvas.current_point[0] - 150, 2 * canvas.current_point[1] - 150
1310
+ # canvas.stroke.
1311
+ # stroke_color("hp-orange").line(*cp, x, y).stroke
1231
1312
  #
1232
- # See: #arc (for approximation accuracy)
1313
+ # See: #arc (for approximation accuracy), #circle
1233
1314
  def ellipse(cx, cy, a:, b:, inclination: 0)
1234
1315
  arc(cx, cy, a: a, b: b, inclination: inclination)
1235
1316
  close_subpath
@@ -1238,8 +1319,8 @@ module HexaPDF
1238
1319
  # :call-seq:
1239
1320
  # canvas.arc(cx, cy, a:, b: a, start_angle: 0, end_angle: 360, clockwise: false, inclination: 0) => canvas
1240
1321
  #
1241
- # Appends an elliptical arc to the path. The endpoint of the arc becomes the new current
1242
- # point.
1322
+ # Appends an elliptical arc to the path and returns +self+. The endpoint of the arc becomes
1323
+ # the new current point.
1243
1324
  #
1244
1325
  # +cx+::
1245
1326
  # x-coordinate of the center point of the arc
@@ -1271,6 +1352,9 @@ module HexaPDF
1271
1352
  #
1272
1353
  # If there is no current path when the method is invoked, a new path is automatically begun.
1273
1354
  #
1355
+ # This arc does *not* start from the current point (#current_point). If this functionality is
1356
+ # needed, use #draw together with GraphicObject::EndpointArc.
1357
+ #
1274
1358
  # Since PDF doesn't have operators for drawing elliptical or circular arcs, they have to be
1275
1359
  # approximated using Bezier curves (see #curve_to). The accuracy of the approximation can be
1276
1360
  # controlled using the configuration option 'graphic_object.arc.max_curves'.
@@ -1284,19 +1368,19 @@ module HexaPDF
1284
1368
  # canvas.stroke
1285
1369
  #
1286
1370
  # # Circular and elliptical arcs from 30 degrees to 160 degrees
1287
- # canvas.stroke_color("red")
1371
+ # canvas.stroke_color("hp-blue")
1288
1372
  # canvas.arc(50, 100, a: 10, start_angle: 30, end_angle: 160)
1289
1373
  # canvas.arc(100, 100, a: 10, b: 5, start_angle: 30, end_angle: 160)
1290
1374
  # canvas.stroke
1291
1375
  #
1292
1376
  # # Arcs from 135 degrees to 30 degrees, the first in counterclockwise direction (i.e. the
1293
1377
  # # big arc), the other in clockwise direction (i.e. the small arc)
1294
- # canvas.stroke_color("blue")
1378
+ # canvas.stroke_color("hp-orange")
1295
1379
  # canvas.arc(50, 50, a: 10, start_angle: 135, end_angle: 30)
1296
1380
  # canvas.arc(100, 50, a: 10, start_angle: 135, end_angle: 30, clockwise: true)
1297
1381
  # canvas.stroke
1298
1382
  #
1299
- # See: Content::GraphicObject::Arc
1383
+ # See: #arc, #circle, #ellipse, GraphicObject::Arc, GraphicObject::EndpointArc
1300
1384
  def arc(cx, cy, a:, b: a, start_angle: 0, end_angle: 360, clockwise: false, inclination: 0)
1301
1385
  arc = GraphicObject::Arc.configure(cx: cx, cy: cy, a: a, b: b,
1302
1386
  start_angle: start_angle, end_angle: end_angle,
@@ -1314,18 +1398,18 @@ module HexaPDF
1314
1398
  # :call-seq:
1315
1399
  # canvas.line_with_rounded_corner(x0 = current_point[0], y0 = current_point[1], x1, y1, x2, y2, in_radius:, out_radius: in_radius)
1316
1400
  #
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.
1401
+ # Appends a line with a rounded corner at (x1, y1) from the current point and returns +self+.
1402
+ # The end point of the rounded corner (i.e. +out_radius+ units from (x1, y1) in the direction
1403
+ # of (x2, y2)) becomes the current point. In degraded cases the corner point (x1, y1) becomes
1404
+ # the current point.
1321
1405
  #
1322
1406
  # The corner is specified by (x0, y0) which defaults to the #current_point of the path, (x1,
1323
1407
  # y1) and (x2, y2) - all of which need to be different points. The +in_radius+ specifies the
1324
1408
  # corner radius into the corner and the +out_radius+ the one out of the corner. Degraded
1325
1409
  # cases, like with (x0, y0) == (x1, y1), are handled gracefully.
1326
1410
  #
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.
1411
+ # There has to be a current path when this method is invoked, otherwise an error is raised.
1412
+ # For example, the current point could be estabilshed beforehand using #move_to.
1329
1413
  #
1330
1414
  # Examples:
1331
1415
  #
@@ -1338,10 +1422,15 @@ module HexaPDF
1338
1422
  # canvas.line_with_rounded_corner(180, 120, 150, 100, in_radius: 0, out_radius: 10)
1339
1423
  # canvas.stroke
1340
1424
  #
1341
- # # Special effects when (x0, y0) is not the current point, like when the current point
1342
- # # would be equal to the corner point
1425
+ # # Special effects when (x0, y0) is not the current point, like when the current
1426
+ # # point would be equal to the corner point. Rounded rectangle use this method
1427
+ # # internally, as high-lighted by the blue segment.
1343
1428
  # canvas.rectangle(10, 10, 60, 60, radius: 60).stroke
1344
- # canvas.rectangle(110, 10, 60, 60, radius: 70).stroke
1429
+ # canvas.stroke_color("hp-blue").
1430
+ # move_to(70, 10). # Start point at the end of the lower-left rounded corner
1431
+ # line_with_rounded_corner(10, 10, 70, 10, 70, 70, in_radius: 60).stroke
1432
+ # canvas.stroke_color("black").
1433
+ # rectangle(110, 10, 60, 60, radius: 70).stroke
1345
1434
  def line_with_rounded_corner(x0 = current_point[0], y0 = current_point[1], x1, y1, x2, y2,
1346
1435
  in_radius:, out_radius: in_radius)
1347
1436
  if in_radius == 0 || out_radius == 0
@@ -1367,9 +1456,9 @@ module HexaPDF
1367
1456
  #
1368
1457
  # Returns the named graphic object, configured with the given options.
1369
1458
  #
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.
1459
+ # If an object responding to :configure is given, it is used. Otherwise the graphic object is
1460
+ # looked up via the given name in the configuration option 'graphic_object.map'. Either way,
1461
+ # the graphic object is then configured with the given options if at least one is given.
1373
1462
  #
1374
1463
  # Examples:
1375
1464
  #
@@ -1378,7 +1467,7 @@ module HexaPDF
1378
1467
  # outer_a: 50, outer_b: 40, end_angle: 135)
1379
1468
  # canvas.draw(obj).stroke
1380
1469
  #
1381
- # See: Content::GraphicObject
1470
+ # See: #draw, GraphicObject
1382
1471
  def graphic_object(obj, **options)
1383
1472
  unless obj.respond_to?(:configure)
1384
1473
  obj = context.document.config.constantize('graphic_object.map', obj)
@@ -1404,7 +1493,7 @@ module HexaPDF
1404
1493
  # :call-seq:
1405
1494
  # canvas.stroke => canvas
1406
1495
  #
1407
- # Strokes the path.
1496
+ # Strokes the path and returns +self+.
1408
1497
  #
1409
1498
  # Examples:
1410
1499
  #
@@ -1412,7 +1501,7 @@ module HexaPDF
1412
1501
  # canvas.polyline(10, 10, 120, 40, 50, 160)
1413
1502
  # canvas.stroke
1414
1503
  #
1415
- # See: PDF1.7 s8.5.3.1, s8.5.3.2
1504
+ # See: PDF2.0 s8.5.3.1, s8.5.3.2, #close_stroke, #close_fill_stroke
1416
1505
  def stroke
1417
1506
  raise_unless_in_path_or_clipping_path
1418
1507
  invoke0(:S)
@@ -1422,7 +1511,7 @@ module HexaPDF
1422
1511
  # :call-seq:
1423
1512
  # canvas.close_stroke => canvas
1424
1513
  #
1425
- # Closes the last subpath and then strokes the path.
1514
+ # Closes the last subpath and then strokes the path. Returns +self+.
1426
1515
  #
1427
1516
  # Examples:
1428
1517
  #
@@ -1430,7 +1519,7 @@ module HexaPDF
1430
1519
  # canvas.polyline(10, 10, 120, 40, 50, 160) # No line from the top to the left
1431
1520
  # canvas.close_stroke
1432
1521
  #
1433
- # See: PDF1.7 s8.5.3.1, s8.5.3.2
1522
+ # See: PDF2.0 s8.5.3.1, s8.5.3.2, #stroke, #close_fill_stroke
1434
1523
  def close_stroke
1435
1524
  raise_unless_in_path_or_clipping_path
1436
1525
  invoke0(:s)
@@ -1440,25 +1529,26 @@ module HexaPDF
1440
1529
  # :call-seq:
1441
1530
  # canvas.fill(rule = :nonzero) => canvas
1442
1531
  #
1443
- # Fills the path using the given rule.
1532
+ # Fills the path using the given rule and returns +self+.
1444
1533
  #
1445
1534
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1446
1535
  # +: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.
1536
+ # how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via Internet search.
1448
1537
  #
1449
1538
  # Any open subpaths are implicitly closed before being filled.
1450
1539
  #
1451
1540
  # Examples:
1452
1541
  #
1453
1542
  # #>pdf
1454
- # canvas.polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90)
1455
- # canvas.fill
1543
+ # canvas.fill_color("hp-blue").
1544
+ # polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90).
1545
+ # fill
1456
1546
  #
1457
- # canvas.fill_color("red")
1458
- # canvas.polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190)
1459
- # canvas.fill(:even_odd)
1547
+ # canvas.fill_color("hp-orange").
1548
+ # polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190).
1549
+ # fill(:even_odd)
1460
1550
  #
1461
- # See: PDF1.7 s8.5.3.1, s8.5.3.3
1551
+ # See: PDF2.0 s8.5.3.1, s8.5.3.3, #fill_stroke, #close_fill_stroke
1462
1552
  def fill(rule = :nonzero)
1463
1553
  raise_unless_in_path_or_clipping_path
1464
1554
  invoke0(rule == :nonzero ? :f : :'f*')
@@ -1468,26 +1558,27 @@ module HexaPDF
1468
1558
  # :call-seq:
1469
1559
  # canvas.fill_stroke(rule = :nonzero) => canvas
1470
1560
  #
1471
- # Fills and then strokes the path using the given rule.
1561
+ # Fills and then strokes the path using the given rule. Returns +self+.
1472
1562
  #
1473
1563
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1474
1564
  # +: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.
1565
+ # how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via Internet search.
1476
1566
  #
1477
- # Note that any open subpaths are *not* closed!
1567
+ # Note that any open subpaths are *not* closed concerning the stroking operation.
1478
1568
  #
1479
1569
  # Examples:
1480
1570
  #
1481
1571
  # #>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
1572
+ # canvas.stroke_color("hp-orange").line_width(3)
1573
+ # canvas.fill_color("hp-blue").
1574
+ # polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90).
1575
+ # fill_stroke # Note the missing stroke from the top corner
1485
1576
  #
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
1577
+ # canvas.fill_color("hp-teal").
1578
+ # polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190).
1579
+ # fill_stroke(:even_odd) # Note the missing stroke from the top corner
1489
1580
  #
1490
- # See: PDF1.7 s8.5.3.1, s8.5.3.3
1581
+ # See: PDF2.0 s8.5.3.1, s8.5.3.3, #fill, #close_fill_stroke
1491
1582
  def fill_stroke(rule = :nonzero)
1492
1583
  raise_unless_in_path_or_clipping_path
1493
1584
  invoke0(rule == :nonzero ? :B : :'B*')
@@ -1497,24 +1588,26 @@ module HexaPDF
1497
1588
  # :call-seq:
1498
1589
  # canvas.close_fill_stroke(rule = :nonzero) => canvas
1499
1590
  #
1500
- # Closes the last subpath and then fills and strokes the path using the given rule.
1591
+ # Closes the last subpath and then fills and strokes the path using the given rule. Returns
1592
+ # +self+.
1501
1593
  #
1502
1594
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1503
1595
  # +: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.
1596
+ # how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via Internet search.
1505
1597
  #
1506
1598
  # Examples:
1507
1599
  #
1508
1600
  # #>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
1601
+ # canvas.stroke_color("hp-orange").line_width(3)
1602
+ # canvas.fill_color("hp-blue").
1603
+ # polyline(20, 10, 90, 60, 10, 60, 80, 10, 50, 90).
1604
+ # close_fill_stroke
1512
1605
  #
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)
1606
+ # canvas.fill_color("hp-teal").
1607
+ # polyline(120, 110, 190, 160, 110, 160, 180, 110, 150, 190).
1608
+ # close_fill_stroke(:even_odd)
1516
1609
  #
1517
- # See: PDF1.7 s8.5.3
1610
+ # See: PDF2.0 s8.5.3, #fill, #fill_stroke
1518
1611
  def close_fill_stroke(rule = :nonzero)
1519
1612
  raise_unless_in_path_or_clipping_path
1520
1613
  invoke0(rule == :nonzero ? :b : :'b*')
@@ -1524,17 +1617,17 @@ module HexaPDF
1524
1617
  # :call-seq:
1525
1618
  # canvas.end_path => canvas
1526
1619
  #
1527
- # Ends the path without stroking or filling it.
1620
+ # Ends the path without stroking or filling it and returns +self+.
1528
1621
  #
1529
- # This method is normally used in conjunction with the clipping path methods to define the
1530
- # clipping.
1622
+ # This method is usually used in conjunction with the clipping path methods to define the
1623
+ # clipping path.
1531
1624
  #
1532
1625
  # Examples:
1533
1626
  #
1534
1627
  # canvas.line(10, 10, 100, 100)
1535
1628
  # canvas.end_path # Nothing to see here!
1536
1629
  #
1537
- # See: PDF1.7 s8.5.3.1, #clip_path
1630
+ # See: PDF2.0 s8.5.3.1, #clip_path
1538
1631
  def end_path
1539
1632
  raise_unless_in_path_or_clipping_path
1540
1633
  invoke0(:n)
@@ -1544,11 +1637,11 @@ module HexaPDF
1544
1637
  # :call-seq:
1545
1638
  # canvas.clip_path(rule = :nonzero) => canvas
1546
1639
  #
1547
- # Modifies the clipping path by intersecting it with the current path.
1640
+ # Modifies the clipping path by intersecting it with the current path. Returns +self+.
1548
1641
  #
1549
1642
  # The argument +rule+ may either be +:nonzero+ to use the nonzero winding number rule or
1550
1643
  # +: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
1644
+ # path. Details on how these rules work are found in the PDF 2.0 spec section 8.5.3.3 or via
1552
1645
  # Internet search.
1553
1646
  #
1554
1647
  # The initial clipping path includes the entire canvas. Once the clipping path is reduced to a
@@ -1566,7 +1659,7 @@ module HexaPDF
1566
1659
  # clip_path(:even_odd).end_path
1567
1660
  # canvas.rectangle(0, 0, 200, 200).fill # Fills everything inside the clipping path
1568
1661
  #
1569
- # See: PDF1.7 s8.5.4
1662
+ # See: PDF2.0 s8.5.4, #end_path
1570
1663
  def clip_path(rule = :nonzero)
1571
1664
  raise_unless_in_path
1572
1665
  invoke0(rule == :nonzero ? :W : :'W*')
@@ -1583,8 +1676,8 @@ module HexaPDF
1583
1676
  # position and returns the XObject.
1584
1677
  #
1585
1678
  # 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.
1679
+ # the configuration option 'image_loader' can be used. PNG (lossless), JPEG (lossy) and PDF
1680
+ # (vector) images are supported out of the box.
1588
1681
  #
1589
1682
  # If the filename or the IO specifies a PDF file, the first page of this file is used to
1590
1683
  # create a form XObject which is then drawn.
@@ -1608,18 +1701,18 @@ module HexaPDF
1608
1701
  # #>pdf
1609
1702
  # canvas.xobject(machu_picchu, at: [10, 10], width: 90) # bottom left
1610
1703
  #
1611
- # file = File.new(machu_picchu, 'rb') # top left
1704
+ # file = File.new(machu_picchu, 'rb') # top left
1612
1705
  # canvas.xobject(file, at: [10, 110], height: 50)
1613
1706
  #
1614
1707
  # image = doc.images.add(machu_picchu)
1615
1708
  # canvas.xobject(image, at: [110, 10], width: 50, height: 90) # bottom right
1616
1709
  #
1617
1710
  # form = doc.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, 100, 100]})
1618
- # form.canvas.line(10, 10, 90, 90).stroke
1711
+ # form.canvas.stroke_color("hp-blue").line(10, 10, 90, 90).stroke
1619
1712
  # canvas.line_width = 20
1620
1713
  # canvas.xobject(form, at: [100, 100]) # top right
1621
1714
  #
1622
- # See: PDF1.7 s8.8, s.8.10.1
1715
+ # See: PDF2.0 s8.8, s.8.10.1, HexaPDF::Type::Image, HexaPDF::Type::Form, HexaPDF::ImageLoader
1623
1716
  def xobject(obj, at:, width: nil, height: nil)
1624
1717
  unless obj.kind_of?(HexaPDF::Stream)
1625
1718
  obj = context.document.images.add(obj)
@@ -1653,14 +1746,18 @@ module HexaPDF
1653
1746
  # canvas.character_spacing(amount) => canvas
1654
1747
  # canvas.character_spacing(amount) { block } => canvas
1655
1748
  #
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
1749
+ # The character spacing determines how much additional space is added after each character
1750
+ # (or, more correctly, after each glyph). For horizontal writing positive values increase the
1751
+ # distance between two characters, whereas for vertical writing negative values increase the
1659
1752
  # distance.
1660
1753
  #
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.
1754
+ # Note that the character spacing is applied to all characters that are rendered. This has the
1755
+ # effect that there is also a space after the last character which might not be wanted in
1756
+ # certain cases (e.g. when justifying text).
1757
+ #
1758
+ # Returns the current character spacing value (see GraphicsState#character_spacing) when no
1759
+ # argument is given. Otherwise sets the character spacing using the +amount+ argument and
1760
+ # returns +self+. The setter version can also be called in the character_spacing= form.
1664
1761
  #
1665
1762
  # If the +amount+ and a block are provided, the changed character spacing is only active
1666
1763
  # during the block by saving and restoring the graphics state.
@@ -1680,11 +1777,18 @@ module HexaPDF
1680
1777
  # # visual example
1681
1778
  # canvas.font("Helvetica", size: 10)
1682
1779
  # 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
1780
+ # canvas.text("This is an example", at: [10, 150])
1781
+ # # show that the text cursor is directly after the last glyph
1782
+ # x, y = canvas.text_cursor
1783
+ # canvas.stroke_color("hp-blue").line(x, y, x, y + 10).stroke
1784
+ #
1785
+ # canvas.character_spacing = 5
1786
+ # canvas.text("This is an example", at: [10, 100])
1787
+ # # visualize the spacing after the last glyph
1788
+ # x, y = canvas.text_cursor
1789
+ # canvas.stroke_color("hp-blue").line(x, y, x, y + 10).stroke
1790
+ #
1791
+ # See: PDF2.0 s9.3.2, #word_spacing, #horizontal_scaling
1688
1792
  def character_spacing(amount = nil, &bk)
1689
1793
  gs_getter_setter(:character_spacing, :Tc, amount, &bk)
1690
1794
  end
@@ -1703,9 +1807,9 @@ module HexaPDF
1703
1807
  # *Important*: In HexaPDF only the standard 14 PDF Type1 fonts support this property! When
1704
1808
  # using any other font, for example a TrueType font, this property has no effect.
1705
1809
  #
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.
1810
+ # Returns the current word spacing value (see GraphicsState#word_spacing) when no argument is
1811
+ # given. Otherwise sets the word spacing using the +amount+ argument and returns +self+. The
1812
+ # setter version can also be called in the word_spacing= form.
1709
1813
  #
1710
1814
  # If the +amount+ and a block are provided, the changed word spacing is only active during
1711
1815
  # the block by saving and restoring the graphics state.
@@ -1729,7 +1833,7 @@ module HexaPDF
1729
1833
  # canvas.word_spacing = 10
1730
1834
  # canvas.text("This is an example text.", at: [10, 100])
1731
1835
  #
1732
- # See: PDF1.7 s9.3.3
1836
+ # See: PDF2.0 s9.3.3, #character_spacing, #horizontal_scaling
1733
1837
  def word_spacing(amount = nil, &bk)
1734
1838
  gs_getter_setter(:word_spacing, :Tw, amount, &bk)
1735
1839
  end
@@ -1744,10 +1848,9 @@ module HexaPDF
1744
1848
  # compressing them in the horizontal direction. The value is specified as percent of the
1745
1849
  # normal width, so 100 means no scaling.
1746
1850
  #
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.
1851
+ # Returns the current horizontal scaling value (see GraphicsState#horizontal_scaling) when no
1852
+ # argument is given. Otherwise sets the horizontal scaling using the +percent+ argument and
1853
+ # returns +self+. The setter version can also be called in the horizontal_scaling= form.
1751
1854
  #
1752
1855
  # If the +percent+ and a block are provided, the changed horizontal scaling is only active
1753
1856
  # during the block by saving and restoring the graphics state.
@@ -1771,7 +1874,7 @@ module HexaPDF
1771
1874
  # canvas.horizontal_scaling = 50
1772
1875
  # canvas.text("This is an example text.", at: [10, 100])
1773
1876
  #
1774
- # See: PDF1.7 s9.3.4
1877
+ # See: PDF2.0 s9.3.4, #character_spacing, #word_spacing
1775
1878
  def horizontal_scaling(amount = nil, &bk)
1776
1879
  gs_getter_setter(:horizontal_scaling, :Tz, amount, &bk)
1777
1880
  end
@@ -1789,8 +1892,8 @@ module HexaPDF
1789
1892
  # There are other PDF content stream operators that would be effected but those are not used
1790
1893
  # by the canvas.
1791
1894
  #
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
1895
+ # Returns the current leading value (see GraphicsState#leading) when no argument is given.
1896
+ # Otherwise sets the leading using the +amount+ argument and returns +self+. The setter
1794
1897
  # version can also be called in the leading= form.
1795
1898
  #
1796
1899
  # If the +amount+ and a block are provided, the changed leading is only active during the
@@ -1813,7 +1916,7 @@ module HexaPDF
1813
1916
  # canvas.leading = 15
1814
1917
  # canvas.text("This is an example text.\nwith a second\nand thrid line", at: [10, 150])
1815
1918
  #
1816
- # See: PDF1.7 s9.3.5, #move_text_cursor
1919
+ # See: PDF2.0 s9.3.5, #move_text_cursor
1817
1920
  def leading(amount = nil, &bk)
1818
1921
  gs_getter_setter(:leading, :TL, amount, &bk)
1819
1922
  end
@@ -1824,16 +1927,33 @@ module HexaPDF
1824
1927
  # canvas.text_rendering_mode(mode) => canvas
1825
1928
  # canvas.text_rendering_mode(mode) { block } => canvas
1826
1929
  #
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.
1930
+ # The text rendering mode determines if and how glyphs are rendered.
1931
+ #
1932
+ # The +mode+ parameter can be one of the following (also see TextRenderingMode):
1933
+ #
1934
+ # :fill or 0::
1935
+ # The text is filled (default)
1936
+ # :stroke or 1::
1937
+ # The text is stroked.
1938
+ # :fill_stroke or 2::
1939
+ # The test is filled, then stroked.
1940
+ # :invisible or 3::
1941
+ # The text is neither filled nor stroked.
1942
+ # :fill_clip or 4::
1943
+ # The text is filled and added to the clipping path.
1944
+ # :stroke_clip or 5::
1945
+ # The text is stroked and added to the clipping path.
1946
+ # :fill_stroke_clip or 6::
1947
+ # The text is filled, then stroked and added to the clipping path.
1948
+ # :clip or 7::
1949
+ # The text is added to the clipping path.
1950
+ # either be a valid integer or one of the symbols +:fill+, +:stroke+,
1951
+ #
1952
+ # Note that the return value is always a normalized text rendering mode value.
1953
+ #
1954
+ # Returns the current text rendering mode value (see GraphicsState#text_rendering_mode) when
1955
+ # no argument is given. Otherwise sets the text rendering mode using the +mode+ argument and
1956
+ # returns +self+. The setter version can also be called in the text_rendering_mode= form.
1837
1957
  #
1838
1958
  # If the +mode+ and a block are provided, the changed text rendering mode is only active
1839
1959
  # during the block by saving and restoring the graphics state.
@@ -1858,7 +1978,7 @@ module HexaPDF
1858
1978
  # canvas.text("#{trm} text.", at: [20, 150 - 30 * index])
1859
1979
  # end
1860
1980
  #
1861
- # See: PDF1.7 s9.3.6, Content::GraphicsState::TextRenderingMode
1981
+ # See: PDF2.0 s9.3.6, GraphicsState::TextRenderingMode
1862
1982
  def text_rendering_mode(m = nil, &bk)
1863
1983
  gs_getter_setter(:text_rendering_mode, :Tr, m && TextRenderingMode.normalize(m), &bk)
1864
1984
  end
@@ -1872,9 +1992,9 @@ module HexaPDF
1872
1992
  # The text rise specifies the vertical distance to move the baseline up or down from its
1873
1993
  # default location. Positive values move the baseline up, negative values down.
1874
1994
  #
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.
1995
+ # Returns the current text rise value (see GraphicsState#text_rise) when no argument is given.
1996
+ # Otherwise sets the text rise using the +amount+ argument and returns +self+. The setter
1997
+ # version can also be called in the text_rise= form.
1878
1998
  #
1879
1999
  # If the +amount+ and a block are provided, the changed text rise is only active during the
1880
2000
  # block by saving and restoring the graphics state.
@@ -1900,7 +2020,7 @@ module HexaPDF
1900
2020
  # canvas.text_rise = -10
1901
2021
  # canvas.text("and also down here")
1902
2022
  #
1903
- # See: PDF1.7 s9.3.7
2023
+ # See: PDF2.0 s9.3.7
1904
2024
  def text_rise(amount = nil, &bk)
1905
2025
  gs_getter_setter(:text_rise, :Ts, amount, &bk)
1906
2026
  end
@@ -1909,7 +2029,7 @@ module HexaPDF
1909
2029
  # :call-seq:
1910
2030
  # canvas.begin_text(force_new: false) -> canvas
1911
2031
  #
1912
- # Begins a new text object.
2032
+ # Begins a new text object and returns +self+.
1913
2033
  #
1914
2034
  # If +force+ is +true+ and the current graphics object is already a text object, it is ended
1915
2035
  # and a new text object is begun.
@@ -1917,7 +2037,7 @@ module HexaPDF
1917
2037
  # It is not necessary to invoke this method manually in most cases since it is automatically
1918
2038
  # called when needed by other methods, i.e. the #text method.
1919
2039
  #
1920
- # See: PDF1.7 s9.4.1
2040
+ # See: PDF2.0 s9.4.1, #end_text, #text
1921
2041
  def begin_text(force_new: false)
1922
2042
  raise_unless_at_page_description_level_or_in_text
1923
2043
  end_text if force_new
@@ -1928,12 +2048,12 @@ module HexaPDF
1928
2048
  # :call-seq:
1929
2049
  # canvas.end_text -> canvas
1930
2050
  #
1931
- # Ends the current text object.
2051
+ # Ends the current text object and returns +self+.
1932
2052
  #
1933
2053
  # It is not necessary to invoke this method manually in most cases since it is automatically
1934
2054
  # called when needed by other methods, i.e. when creating a new path.
1935
2055
  #
1936
- # See: PDF1.7 s9.4.1
2056
+ # See: PDF2.0 s9.4.1, #begin_text
1937
2057
  def end_text
1938
2058
  raise_unless_at_page_description_level_or_in_text
1939
2059
  invoke0(:ET) if graphics_object == :text
@@ -1943,7 +2063,12 @@ module HexaPDF
1943
2063
  # :call-seq:
1944
2064
  # canvas.text_matrix(a, b, c, d, e, f) => canvas
1945
2065
  #
1946
- # Sets the text matrix (and the text line matrix) to the given matrix and returns self.
2066
+ # Sets the text matrix (and the text line matrix) to the given matrix and returns +self+.
2067
+ #
2068
+ # The text matrix determines where and how the glyphs are rendered. The most common use is to
2069
+ # translate the text space origin since the text drawing operations always use the text space
2070
+ # origin as starting point for drawing the glyphs. This translation operation can more easily
2071
+ # be specified using #move_text_cursor.
1947
2072
  #
1948
2073
  # The given values are interpreted as a matrix in the following way:
1949
2074
  #
@@ -1965,7 +2090,7 @@ module HexaPDF
1965
2090
  # canvas.text_matrix(2, 1, 3, 0.5, 50, 50)
1966
2091
  # canvas.text("This is some text")
1967
2092
  #
1968
- # See: PDF1.7 s9.4.2
2093
+ # See: PDF2.0 s9.4.2, #move_text_cursor, #text_cursor
1969
2094
  def text_matrix(a, b, c, d, e, f)
1970
2095
  begin_text
1971
2096
  invoke(:Tm, a, b, c, d, e, f)
@@ -1975,7 +2100,7 @@ module HexaPDF
1975
2100
  # :call-seq:
1976
2101
  # canvas.move_text_cursor(offset: nil, absolute: true) -> canvas
1977
2102
  #
1978
- # Moves the text cursor by modifying the text and text line matrices.
2103
+ # Moves the text cursor by modifying the text and text line matrices. Returns +self+.
1979
2104
  #
1980
2105
  # If +offset+ is not specified, the text cursor is moved to the start of the next text line
1981
2106
  # using #leading as vertical offset.
@@ -2010,7 +2135,7 @@ module HexaPDF
2010
2135
  # canvas.move_text_cursor
2011
2136
  # canvas.text("Text on next line with leading=30")
2012
2137
  #
2013
- # See: PDF1.7 s9.4.2, #show_glyphs
2138
+ # See: PDF2.0 s9.4.2, #leading, #text_cursor, #text, #show_glyphs
2014
2139
  def move_text_cursor(offset: nil, absolute: true)
2015
2140
  begin_text
2016
2141
  if offset
@@ -2028,7 +2153,8 @@ module HexaPDF
2028
2153
  # :call-seq:
2029
2154
  # canvas.text_cursor -> [x, y]
2030
2155
  #
2031
- # Returns the position of the text cursor, i.e. the origin of the current text matrix.
2156
+ # Returns the position of the text cursor, i.e. the origin of text space. This is where the
2157
+ # first glyph of the next drawn text will be placed.
2032
2158
  #
2033
2159
  # Note that this method can only be called while the current graphic object is a text object
2034
2160
  # since the text matrix is otherwise undefined.
@@ -2038,10 +2164,11 @@ module HexaPDF
2038
2164
  # #>pdf
2039
2165
  # canvas.font("Helvetica", size: 10)
2040
2166
  # 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])
2167
+ # tx, ty = canvas.text_cursor # Cursor is directly after the text
2168
+ # canvas.stroke_color("hp-blue").
2169
+ # circle(tx, ty, 0.5).
2170
+ # circle(tx, ty, 5).stroke
2171
+ # canvas.text("Last cursor: (#{tx.round(2)}, #{ty.round(2)})", at: [30, 100])
2045
2172
  #
2046
2173
  # See: #move_text_cursor
2047
2174
  def text_cursor
@@ -2055,7 +2182,7 @@ module HexaPDF
2055
2182
  #
2056
2183
  # Specifies the font and optional the font size that should be used when showing text.
2057
2184
  #
2058
- # A valid font size need to be provided on the first invocation, otherwise an error is raised
2185
+ # A valid font size needs to be provided on the first invocation, otherwise an error is raised
2059
2186
  # (this is due to how setting a font works with PDFs).
2060
2187
  #
2061
2188
  # If +size+ is specified, the #font_size method is invoked with it as argument.
@@ -2065,8 +2192,9 @@ module HexaPDF
2065
2192
  # specifies the font variant to use, with standard values of :none, :italic, :bold and
2066
2193
  # :bold_italic.
2067
2194
  #
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.
2195
+ # Returns the current font object when no argument is given, otherwise returns +self+. *Note*
2196
+ # that this is the font object itself, not the PDF dictionary representing the font that is
2197
+ # stored in the resources.
2070
2198
  #
2071
2199
  # Examples:
2072
2200
  #
@@ -2080,7 +2208,7 @@ module HexaPDF
2080
2208
  # canvas.font("Times", variant: :bold_italic, size: 15)
2081
2209
  # canvas.text("Times bold+italic at size 15", at: [10, 100])
2082
2210
  #
2083
- # See: PDF1.7 s9.2.2, #font_size
2211
+ # See: PDF2.0 s9.2.2, #font_size, #text
2084
2212
  def font(name = nil, size: nil, **options)
2085
2213
  if name
2086
2214
  @font = (name.respond_to?(:pdf_object) ? name : context.document.fonts.add(name, **options))
@@ -2099,16 +2227,16 @@ module HexaPDF
2099
2227
  alias font= font
2100
2228
 
2101
2229
  # :call-seq:
2102
- # canvas.font_size => font_size
2103
- # canvas.font_size(size => canvas
2230
+ # canvas.font_size => font_size
2231
+ # canvas.font_size(size) => canvas
2104
2232
  #
2105
2233
  # Specifies the font size.
2106
2234
  #
2107
2235
  # Note that an error is raised if no font has been set before via #font (this is due to how
2108
2236
  # setting font and font size works in PDF).
2109
2237
  #
2110
- # Returns the current font size when no argument is given. The setter version can also
2111
- # be called in the font_size= form.
2238
+ # Returns the current font size when no argument is given, otherwise returns +self+. The
2239
+ # setter version can also be called in the font_size= form.
2112
2240
  #
2113
2241
  # Examples:
2114
2242
  #
@@ -2124,7 +2252,7 @@ module HexaPDF
2124
2252
  # canvas.text("Text in size #{size}", at: [15, 180 - index * 20])
2125
2253
  # end
2126
2254
  #
2127
- # See: PDF1.7 s9.2.2, #font
2255
+ # See: PDF2.0 s9.2.2, #font, #text
2128
2256
  def font_size(size = nil)
2129
2257
  if size
2130
2258
  unless @font
@@ -2142,7 +2270,7 @@ module HexaPDF
2142
2270
  # canvas.text(text) -> canvas
2143
2271
  # canvas.text(text, at: [x, y]) -> canvas
2144
2272
  #
2145
- # Shows the given text string, either at the current or the provided position.
2273
+ # Shows the given text string, either at the current or the provided position. Returns +self+.
2146
2274
  #
2147
2275
  # If no position is provided, the text is positioned at the current position of the text
2148
2276
  # cursor (see #text_cursor).
@@ -2152,21 +2280,24 @@ module HexaPDF
2152
2280
  # equal to the font size will be set..
2153
2281
  #
2154
2282
  # 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.
2283
+ # string is too long, it may be outside the cropped page and be cut off.
2156
2284
  #
2157
2285
  # Examples:
2158
2286
  #
2159
2287
  # #>pdf
2160
2288
  # 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
2289
+ # # Sets leading=12 because mulitple lines are drawn
2290
+ # canvas.text("This is a \n multiline text", at: [15, 150])
2291
+ # # Starts right after the last text
2292
+ # canvas.text(". Some more text\nafter the newline.")
2163
2293
  #
2164
- # See: #leading, http://www.unicode.org/reports/tr18/#Line_Boundaries
2294
+ # See: #leading, #font, #font_size, #show_glyphs,
2295
+ # http://www.unicode.org/reports/tr18/#Line_Boundaries
2165
2296
  def text(text, at: nil)
2166
2297
  raise_unless_font_set
2167
2298
  move_text_cursor(offset: at) if at
2168
- leading(font_size) if leading == 0
2169
2299
  lines = text.split(/\u{D A}|(?!\u{D A})[\u{A}-\u{D}\u{85}\u{2028}\u{2029}]/, -1)
2300
+ leading(font_size) if leading == 0 && lines.length > 1
2170
2301
  lines.each_with_index do |str, index|
2171
2302
  show_glyphs(@font.decode_utf8(str))
2172
2303
  move_text_cursor unless index == lines.length - 1
@@ -2177,7 +2308,7 @@ module HexaPDF
2177
2308
  # :call-seq:
2178
2309
  # canvas.show_glyphs(glyphs) -> canvas
2179
2310
  #
2180
- # Low-level method for actually showing text on the canvas.
2311
+ # Low-level method for actually showing text on the canvas. Returns +self+.
2181
2312
  #
2182
2313
  # The argument +glyphs+ needs to be a an array of glyph objects valid for the current font,
2183
2314
  # optionally interspersed with numbers for kerning.
@@ -2200,6 +2331,8 @@ module HexaPDF
2200
2331
  # canvas.move_text_cursor(offset: [15, 100])
2201
2332
  # canvas.show_glyphs(glyphs)
2202
2333
  # canvas.text(canvas.text_cursor.map(&:to_i).join(", "), at: [15, 80])
2334
+ #
2335
+ # See: #text, #text_cursor, #text_matrix, #move_text_cursor, #show_glyphs_only
2203
2336
  def show_glyphs(glyphs)
2204
2337
  return if glyphs.empty?
2205
2338
  raise_unless_font_set
@@ -2269,7 +2402,7 @@ module HexaPDF
2269
2402
  # :call-seq:
2270
2403
  # canvas.marked_content_point(tag, property_list: nil) -> canvas
2271
2404
  #
2272
- # Inserts a marked-content point, optionally associated with a property list.
2405
+ # Inserts a marked-content point, optionally associated with a property list. Returns +self+.
2273
2406
  #
2274
2407
  # A marked-content point is used to identify a position in the content stream for later use by
2275
2408
  # other applications. The symbol +tag+ is used to uniquely identify the role of the
@@ -2284,7 +2417,7 @@ module HexaPDF
2284
2417
  # canvas.marked_content_point(:Divider)
2285
2418
  # canvas.marked_content_point(:Divider, property_list: {Key: 'value'})
2286
2419
  #
2287
- # See: PDF1.7 s14.6
2420
+ # See: PDF2.0 s14.6, #marked_content_sequence, #end_marked_content_sequence
2288
2421
  def marked_content_point(tag, property_list: nil)
2289
2422
  raise_unless_at_page_description_level_or_in_text
2290
2423
  if property_list
@@ -2300,7 +2433,8 @@ module HexaPDF
2300
2433
  # canvas.marked_content_sequence(tag, property_list: nil) -> canvas
2301
2434
  # canvas.marked_content_sequence(tag, property_list: nil) { block } -> canvas
2302
2435
  #
2303
- # Inserts a marked-content sequence, optionally associated with a property list.
2436
+ # Inserts a marked-content sequence, optionally associated with a property list. Returns
2437
+ # +self+.
2304
2438
  #
2305
2439
  # A marked-content sequence is used to identify a sequence of complete graphics objects in the
2306
2440
  # content stream for later use by other applications, e.g. for tagged PDF. The symbol +tag+ is
@@ -2327,7 +2461,7 @@ module HexaPDF
2327
2461
  # # Other instructions
2328
2462
  # end
2329
2463
  #
2330
- # See: PDF1.7 s14.6, #end_marked_content_sequence
2464
+ # See: PDF2.0 s14.6, #end_marked_content_sequence, #marked_content_point
2331
2465
  def marked_content_sequence(tag, property_list: nil)
2332
2466
  raise_unless_at_page_description_level
2333
2467
  if property_list
@@ -2346,22 +2480,66 @@ module HexaPDF
2346
2480
  # :call-seq:
2347
2481
  # canvas.end_marked_content_sequence -> canvas
2348
2482
  #
2349
- # Ends a marked-content sequence.
2483
+ # Ends a marked-content sequence and returns +self+.
2350
2484
  #
2351
2485
  # See #marked_content_sequence for details.
2352
2486
  #
2353
- # See: PDF1.7 s14.6, #marked_content_sequence
2487
+ # See: PDF2.0 s14.6, #marked_content_sequence, #marked_content_point
2354
2488
  def end_marked_content_sequence
2355
2489
  raise_unless_at_page_description_level
2356
2490
  invoke0(:EMC)
2357
2491
  self
2358
2492
  end
2359
2493
 
2360
- # Creates a color object from the given color specification. See #stroke_color for details
2361
- # on the possible color specifications.
2494
+ # :call-seq:
2495
+ # canvas.optional_content(ocg, &block) -> canvas
2496
+ # canvas.optional_content(name, use_existing_ocg: true, &block) -> canvas
2497
+ #
2498
+ # Inserts an optional content sequence. Returns +self+.
2499
+ #
2500
+ # An optional content sequence marks part of the content stream as belonging to the given
2501
+ # optional content group. See HexaPDF::Type::OptionalContentProperties for details.
2502
+ #
2503
+ # If the first argument is already an optional content group dictionary, it is used.
2504
+ # Otherwise, the first argument needs to be the name of the optional content group. In that
2505
+ # case, the +use_existing_ocg+ specifies whether the first found optional content group with
2506
+ # that name should be used or whether a new OCG should always be created.
2507
+ #
2508
+ # If invoked without a block, a corresponding call to #end_optional_content must be done.
2509
+ # Otherwise the optional content sequence automatically ends when the block is finished.
2510
+ #
2511
+ # Examples:
2512
+ #
2513
+ # canvas.optional_content('Hints')
2514
+ # # Other instructions
2515
+ # canvas.end_optional_content
2516
+ #
2517
+ # canvas.optional_content('Hints', use_existing_ocg: false) do
2518
+ # # Other instructions
2519
+ # end
2520
+ #
2521
+ # See: PDF2.0 s8.11, #end_optional_content, HexaPDF::Type::OptionalContentProperties
2522
+ def optional_content(ocg, use_existing_ocg: true, &block)
2523
+ ocg = if ocg.kind_of?(HexaPDF::Dictionary) || !use_existing_ocg
2524
+ context.document.optional_content.add_ocg(ocg)
2525
+ else
2526
+ context.document.optional_content.ocg(ocg, create: true)
2527
+ end
2528
+ marked_content_sequence(:OC, property_list: ocg, &block)
2529
+ end
2530
+
2531
+ # Ends an optional content sequence and returns +self+.
2532
+ #
2533
+ # See #optional_content for details.
2534
+ #
2535
+ # See: PDF2.0 s8.11
2536
+ alias :end_optional_content :end_marked_content_sequence
2537
+
2538
+ # Creates and returns a color object from the given color specification. See #stroke_color for
2539
+ # details on the possible color specifications.
2362
2540
  #
2363
2541
  # 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.
2542
+ # specification into a color object.
2365
2543
  def color_from_specification(spec)
2366
2544
  spec = Array(spec)
2367
2545
  if spec.length == 1 && spec[0].kind_of?(String)