hexapdf 0.32.2 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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)