hexapdf 0.17.1 → 0.17.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -0
  3. data/LICENSE +29 -0
  4. data/README.md +129 -0
  5. data/Rakefile +109 -0
  6. data/agpl-3.0.txt +661 -0
  7. data/examples/001-hello_world.rb +16 -0
  8. data/examples/002-graphics.rb +275 -0
  9. data/examples/003-arcs.rb +50 -0
  10. data/examples/004-optimizing.rb +23 -0
  11. data/examples/005-merging.rb +27 -0
  12. data/examples/006-standard_pdf_fonts.rb +73 -0
  13. data/examples/007-truetype.rb +42 -0
  14. data/examples/008-show_char_bboxes.rb +55 -0
  15. data/examples/009-text_layouter_alignment.rb +47 -0
  16. data/examples/010-text_layouter_inline_boxes.rb +64 -0
  17. data/examples/011-text_layouter_line_wrapping.rb +57 -0
  18. data/examples/012-text_layouter_styling.rb +122 -0
  19. data/examples/013-text_layouter_shapes.rb +176 -0
  20. data/examples/014-text_in_polygon.rb +60 -0
  21. data/examples/015-boxes.rb +76 -0
  22. data/examples/016-frame_automatic_box_placement.rb +90 -0
  23. data/examples/017-frame_text_flow.rb +60 -0
  24. data/examples/018-composer.rb +44 -0
  25. data/examples/019-acro_form.rb +88 -0
  26. data/examples/emoji-smile.png +0 -0
  27. data/examples/emoji-wink.png +0 -0
  28. data/examples/machupicchu.jpg +0 -0
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
  31. data/lib/hexapdf/version.rb +1 -1
  32. data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
  33. data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
  34. data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
  35. data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
  36. data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
  37. data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
  38. data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
  39. data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
  40. data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
  41. data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
  42. data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
  43. data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
  44. data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
  45. data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
  46. data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
  47. data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
  48. data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
  49. data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
  50. data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
  51. data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
  52. data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
  53. data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
  54. data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
  55. data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
  56. data/test/data/fonts/Ubuntu-Title.ttf +0 -0
  57. data/test/data/images/cmyk.jpg +0 -0
  58. data/test/data/images/fillbytes.jpg +0 -0
  59. data/test/data/images/gray.jpg +0 -0
  60. data/test/data/images/greyscale-1bit.png +0 -0
  61. data/test/data/images/greyscale-2bit.png +0 -0
  62. data/test/data/images/greyscale-4bit.png +0 -0
  63. data/test/data/images/greyscale-8bit.png +0 -0
  64. data/test/data/images/greyscale-alpha-8bit.png +0 -0
  65. data/test/data/images/greyscale-trns-8bit.png +0 -0
  66. data/test/data/images/greyscale-with-gamma1.0.png +0 -0
  67. data/test/data/images/greyscale-with-gamma1.5.png +0 -0
  68. data/test/data/images/indexed-1bit.png +0 -0
  69. data/test/data/images/indexed-2bit.png +0 -0
  70. data/test/data/images/indexed-4bit.png +0 -0
  71. data/test/data/images/indexed-8bit.png +0 -0
  72. data/test/data/images/indexed-alpha-4bit.png +0 -0
  73. data/test/data/images/indexed-alpha-8bit.png +0 -0
  74. data/test/data/images/rgb.jpg +0 -0
  75. data/test/data/images/truecolour-8bit.png +0 -0
  76. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  77. data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
  78. data/test/data/images/truecolour-srgb-8bit.png +0 -0
  79. data/test/data/images/ycck.jpg +0 -0
  80. data/test/data/minimal.pdf +44 -0
  81. data/test/data/standard-security-handler/README +9 -0
  82. data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
  83. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
  84. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
  85. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
  86. data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
  87. data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
  88. data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
  89. data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
  90. data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
  91. data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
  92. data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
  93. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
  94. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
  95. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
  96. data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
  97. data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
  98. data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
  99. data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
  100. data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
  101. data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
  102. data/test/hexapdf/common_tokenizer_tests.rb +236 -0
  103. data/test/hexapdf/content/common.rb +39 -0
  104. data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
  105. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
  106. data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
  107. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
  108. data/test/hexapdf/content/test_canvas.rb +1279 -0
  109. data/test/hexapdf/content/test_color_space.rb +176 -0
  110. data/test/hexapdf/content/test_graphics_state.rb +151 -0
  111. data/test/hexapdf/content/test_operator.rb +619 -0
  112. data/test/hexapdf/content/test_parser.rb +99 -0
  113. data/test/hexapdf/content/test_processor.rb +163 -0
  114. data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
  115. data/test/hexapdf/document/test_files.rb +72 -0
  116. data/test/hexapdf/document/test_fonts.rb +60 -0
  117. data/test/hexapdf/document/test_images.rb +72 -0
  118. data/test/hexapdf/document/test_pages.rb +130 -0
  119. data/test/hexapdf/encryption/common.rb +87 -0
  120. data/test/hexapdf/encryption/test_aes.rb +129 -0
  121. data/test/hexapdf/encryption/test_arc4.rb +39 -0
  122. data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
  123. data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
  124. data/test/hexapdf/encryption/test_identity.rb +21 -0
  125. data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
  126. data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
  127. data/test/hexapdf/encryption/test_security_handler.rb +380 -0
  128. data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
  129. data/test/hexapdf/filter/common.rb +53 -0
  130. data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
  131. data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
  132. data/test/hexapdf/filter/test_crypt.rb +21 -0
  133. data/test/hexapdf/filter/test_encryption.rb +24 -0
  134. data/test/hexapdf/filter/test_flate_decode.rb +44 -0
  135. data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
  136. data/test/hexapdf/filter/test_predictor.rb +219 -0
  137. data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
  138. data/test/hexapdf/font/cmap/test_parser.rb +102 -0
  139. data/test/hexapdf/font/cmap/test_writer.rb +66 -0
  140. data/test/hexapdf/font/encoding/test_base.rb +45 -0
  141. data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
  142. data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
  143. data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
  144. data/test/hexapdf/font/test_cmap.rb +104 -0
  145. data/test/hexapdf/font/test_encoding.rb +27 -0
  146. data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
  147. data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
  148. data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
  149. data/test/hexapdf/font/true_type/common.rb +17 -0
  150. data/test/hexapdf/font/true_type/table/common.rb +27 -0
  151. data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
  152. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
  153. data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
  154. data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
  155. data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
  156. data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
  157. data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
  158. data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
  159. data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
  160. data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
  161. data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
  162. data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
  163. data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
  164. data/test/hexapdf/font/true_type/test_builder.rb +42 -0
  165. data/test/hexapdf/font/true_type/test_font.rb +116 -0
  166. data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
  167. data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
  168. data/test/hexapdf/font/true_type/test_table.rb +48 -0
  169. data/test/hexapdf/font/type1/common.rb +6 -0
  170. data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
  171. data/test/hexapdf/font/type1/test_font.rb +104 -0
  172. data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
  173. data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
  174. data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
  175. data/test/hexapdf/font_loader/test_from_file.rb +36 -0
  176. data/test/hexapdf/font_loader/test_standard14.rb +33 -0
  177. data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
  178. data/test/hexapdf/image_loader/test_pdf.rb +47 -0
  179. data/test/hexapdf/image_loader/test_png.rb +259 -0
  180. data/test/hexapdf/layout/test_box.rb +154 -0
  181. data/test/hexapdf/layout/test_frame.rb +350 -0
  182. data/test/hexapdf/layout/test_image_box.rb +73 -0
  183. data/test/hexapdf/layout/test_inline_box.rb +71 -0
  184. data/test/hexapdf/layout/test_line.rb +206 -0
  185. data/test/hexapdf/layout/test_style.rb +790 -0
  186. data/test/hexapdf/layout/test_text_box.rb +140 -0
  187. data/test/hexapdf/layout/test_text_fragment.rb +375 -0
  188. data/test/hexapdf/layout/test_text_layouter.rb +758 -0
  189. data/test/hexapdf/layout/test_text_shaper.rb +62 -0
  190. data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
  191. data/test/hexapdf/task/test_dereference.rb +51 -0
  192. data/test/hexapdf/task/test_optimize.rb +162 -0
  193. data/test/hexapdf/test_composer.rb +258 -0
  194. data/test/hexapdf/test_configuration.rb +93 -0
  195. data/test/hexapdf/test_data_dir.rb +32 -0
  196. data/test/hexapdf/test_dictionary.rb +340 -0
  197. data/test/hexapdf/test_dictionary_fields.rb +269 -0
  198. data/test/hexapdf/test_document.rb +641 -0
  199. data/test/hexapdf/test_filter.rb +100 -0
  200. data/test/hexapdf/test_importer.rb +106 -0
  201. data/test/hexapdf/test_object.rb +258 -0
  202. data/test/hexapdf/test_parser.rb +645 -0
  203. data/test/hexapdf/test_pdf_array.rb +169 -0
  204. data/test/hexapdf/test_rectangle.rb +73 -0
  205. data/test/hexapdf/test_reference.rb +50 -0
  206. data/test/hexapdf/test_revision.rb +188 -0
  207. data/test/hexapdf/test_revisions.rb +196 -0
  208. data/test/hexapdf/test_serializer.rb +195 -0
  209. data/test/hexapdf/test_stream.rb +274 -0
  210. data/test/hexapdf/test_tokenizer.rb +80 -0
  211. data/test/hexapdf/test_type.rb +18 -0
  212. data/test/hexapdf/test_writer.rb +140 -0
  213. data/test/hexapdf/test_xref_section.rb +61 -0
  214. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
  215. data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
  216. data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
  217. data/test/hexapdf/type/acro_form/test_field.rb +259 -0
  218. data/test/hexapdf/type/acro_form/test_form.rb +357 -0
  219. data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
  220. data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
  221. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
  222. data/test/hexapdf/type/actions/test_launch.rb +24 -0
  223. data/test/hexapdf/type/actions/test_uri.rb +23 -0
  224. data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
  225. data/test/hexapdf/type/annotations/test_text.rb +34 -0
  226. data/test/hexapdf/type/annotations/test_widget.rb +225 -0
  227. data/test/hexapdf/type/test_annotation.rb +97 -0
  228. data/test/hexapdf/type/test_catalog.rb +48 -0
  229. data/test/hexapdf/type/test_cid_font.rb +61 -0
  230. data/test/hexapdf/type/test_file_specification.rb +141 -0
  231. data/test/hexapdf/type/test_font.rb +67 -0
  232. data/test/hexapdf/type/test_font_descriptor.rb +61 -0
  233. data/test/hexapdf/type/test_font_simple.rb +176 -0
  234. data/test/hexapdf/type/test_font_true_type.rb +31 -0
  235. data/test/hexapdf/type/test_font_type0.rb +120 -0
  236. data/test/hexapdf/type/test_font_type1.rb +142 -0
  237. data/test/hexapdf/type/test_font_type3.rb +26 -0
  238. data/test/hexapdf/type/test_form.rb +120 -0
  239. data/test/hexapdf/type/test_image.rb +261 -0
  240. data/test/hexapdf/type/test_info.rb +9 -0
  241. data/test/hexapdf/type/test_object_stream.rb +117 -0
  242. data/test/hexapdf/type/test_page.rb +598 -0
  243. data/test/hexapdf/type/test_page_tree_node.rb +315 -0
  244. data/test/hexapdf/type/test_resources.rb +209 -0
  245. data/test/hexapdf/type/test_trailer.rb +116 -0
  246. data/test/hexapdf/type/test_xref_stream.rb +143 -0
  247. data/test/hexapdf/utils/test_bit_field.rb +63 -0
  248. data/test/hexapdf/utils/test_bit_stream.rb +69 -0
  249. data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
  250. data/test/hexapdf/utils/test_lru_cache.rb +22 -0
  251. data/test/hexapdf/utils/test_object_hash.rb +120 -0
  252. data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
  253. data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
  254. data/test/test_helper.rb +58 -0
  255. metadata +263 -3
@@ -0,0 +1,275 @@
1
+ # # Graphics Primitives
2
+ #
3
+ # This example shows many of the operations that the canvas implementation
4
+ # allows.
5
+ #
6
+ # Note that the PDF canvas has its origin in the bottom left corner of the page.
7
+ # This means the coordinate (100, 50) is 100 PDF points from the left side and
8
+ # 50 PDF points from the bottom. One PDF point is equal to 1/72 inch.
9
+ #
10
+ # Usage:
11
+ # : `ruby graphics.rb`
12
+ #
13
+
14
+ require 'hexapdf'
15
+
16
+ doc = HexaPDF::Document.new
17
+ page = doc.pages.add
18
+ canvas = page.canvas
19
+
20
+ # Draws the shape that is used to showcase the transformations in the given
21
+ # color.
22
+ def transformation_shape(canvas, *color)
23
+ canvas.stroke_color(*color)
24
+ canvas.polygon(0, 0, 0, 80, 30, 50, 60, 80, 60, 0, 30, 30)
25
+ canvas.line(-30, 0, 30, 0)
26
+ canvas.line(0, 30, 0, -30)
27
+ canvas.stroke
28
+ end
29
+
30
+ # Basic transformations: translate, scale, rotate, skew
31
+ canvas.translate(0, 710) do
32
+ normal_color = [0.7, 0.7, 0.3]
33
+ transformed_color = [0.3, 0.7, 0.7]
34
+
35
+ canvas.translate(50, 0) do
36
+ transformation_shape(canvas, normal_color)
37
+ canvas.translate(40, 40) { transformation_shape(canvas, transformed_color) }
38
+ end
39
+
40
+ canvas.translate(180, 0) do
41
+ transformation_shape(canvas, normal_color)
42
+ canvas.scale(1.7, 1.3) { transformation_shape(canvas, transformed_color) }
43
+ end
44
+
45
+ canvas.translate(330, 0) do
46
+ transformation_shape(canvas, normal_color)
47
+ canvas.rotate(30) { transformation_shape(canvas, transformed_color) }
48
+ end
49
+
50
+ canvas.translate(430, 0) do
51
+ transformation_shape(canvas, normal_color)
52
+ canvas.skew(15, 30) { transformation_shape(canvas, transformed_color) }
53
+ end
54
+ end
55
+
56
+ # Draws a thin white line over a thick black line.
57
+ def dual_lines(canvas)
58
+ canvas.stroke_color(0)
59
+ canvas.line_width = 15
60
+ yield
61
+ canvas.stroke
62
+ canvas.stroke_color(1.0)
63
+ canvas.line_width = 1
64
+ yield
65
+ canvas.stroke
66
+ end
67
+
68
+ # Graphics state: line width, line cap style, line join style, miter limit,
69
+ # line dash pattern
70
+ canvas.translate(0, 550) do
71
+ canvas.translate(50, 0) do
72
+ [1, 5, 10, 15].each_with_index do |i, index|
73
+ canvas.stroke_color(0)
74
+ canvas.line_width(i)
75
+ canvas.line(20 * index, 0, 20 * index, 100)
76
+ canvas.stroke
77
+ end
78
+ end
79
+
80
+ canvas.translate(150, 0) do
81
+ 0.upto(2) do |i|
82
+ canvas.line_cap_style = i
83
+ dual_lines(canvas) { canvas.line(20 * i, 0, 20 * i, 100) }
84
+ end
85
+ end
86
+
87
+ canvas.translate(230, 0) do
88
+ 0.upto(2) do |i|
89
+ canvas.line_join_style = i
90
+ dual_lines(canvas) { canvas.polyline(0, 30 * i, 40, 50 + 30 * i, 80, 30 * i) }
91
+ end
92
+ end
93
+
94
+ canvas.translate(350, 0) do
95
+ canvas.line_join_style = :miter
96
+ canvas.miter_limit = 1
97
+ dual_lines(canvas) { canvas.polyline(0, 0, 20, 80, 40, 0) }
98
+ canvas.miter_limit = 10
99
+ dual_lines(canvas) { canvas.polyline(60, 0, 80, 80, 100, 0) }
100
+ end
101
+
102
+ canvas.translate(490, 0) do
103
+ canvas.line_width(1)
104
+ [[[1, 1]],
105
+ [[3, 1]],
106
+ [[3, 3]],
107
+ [[5, 1, 1, 1, 1, 1]],
108
+ [[3, 5], 6]].each_with_index do |(value, phase), index|
109
+ canvas.line_dash_pattern(value, phase || 0)
110
+ canvas.line(20 * index, 0, 20 * index, 100)
111
+ canvas.stroke
112
+ end
113
+ end
114
+ end
115
+
116
+ # Basic shapes: line, polyline, (rounded) rectangle, (rounded) polygon, circle, ellipse
117
+ canvas.translate(0, 420) do
118
+ canvas.line(50, 0, 50, 100)
119
+ canvas.polyline(80, 0, 80, 20, 70, 30, 90, 40, 70, 50, 90, 60, 70, 70, 80, 80, 80, 100)
120
+ canvas.rectangle(110, 0, 50, 100)
121
+ canvas.rectangle(180, 0, 50, 100, radius: 20)
122
+ canvas.polygon(250, 0, 250, 100, 280, 70, 310, 100, 310, 0, 280, 30)
123
+ canvas.polygon(330, 0, 330, 100, 360, 70, 390, 100, 390, 0, 360, 30, radius: 20)
124
+ canvas.circle(440, 50, 30)
125
+ canvas.ellipse(520, 50, a: 30, b: 15, inclination: 45)
126
+ canvas.stroke
127
+ end
128
+
129
+ # Various arcs w/wo filling, using the Canvas#arc method as well as directly
130
+ # working with the arc objects
131
+ canvas.translate(0, 320) do
132
+ canvas.arc(50, 50, a: 10, start_angle: -60, end_angle: 115)
133
+ canvas.arc(100, 50, a: 40, b: 20, start_angle: -60, end_angle: 115)
134
+ canvas.arc(180, 50, a: 40, b: 20, start_angle: -60, end_angle: 115, inclination: 45)
135
+ canvas.stroke
136
+
137
+ canvas.fill_color(0.4, 0.3, 0.4)
138
+ canvas.arc(250, 50, a: 10, start_angle: -60, end_angle: 115)
139
+ canvas.arc(300, 50, a: 40, b: 20, start_angle: -60, end_angle: 115)
140
+ canvas.arc(380, 50, a: 40, b: 20, start_angle: -60, end_angle: 115, inclination: 45)
141
+ canvas.fill
142
+
143
+ arc = canvas.graphic_object(:arc, cx: 450, cy: 50, a: 30, b: 30,
144
+ start_angle: -30, end_angle: 105)
145
+ canvas.fill_color(0.4, 0.3, 0.4)
146
+ canvas.move_to(450, 50)
147
+ canvas.line_to(*arc.start_point)
148
+ arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
149
+ canvas.fill
150
+ arc.configure(start_angle: 105, end_angle: -30)
151
+ canvas.fill_color(0.3, 0.7, 0.7)
152
+ canvas.move_to(450, 50)
153
+ canvas.line_to(*arc.start_point)
154
+ arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
155
+ canvas.fill
156
+
157
+ arc = canvas.graphic_object(:arc, cx: 530, cy: 50, a: 40, b: 20,
158
+ start_angle: -30, end_angle: 105)
159
+ canvas.fill_color(0.4, 0.3, 0.4)
160
+ canvas.move_to(530, 50)
161
+ canvas.line_to(*arc.start_point)
162
+ arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
163
+ canvas.fill
164
+ arc.configure(start_angle: 105, end_angle: -30)
165
+ canvas.fill_color(0.7, 0.7, 0.3)
166
+ canvas.move_to(530, 50)
167
+ canvas.line_to(*arc.start_point)
168
+ arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
169
+ canvas.fill
170
+ end
171
+
172
+ # Draws a circle and two half circles inside with different directions.
173
+ def shapes_to_paint(canvas)
174
+ canvas.line_width = 2
175
+ canvas.arc(50, 50, a: 50)
176
+ canvas.arc(50, 60, a: 25, end_angle: 180, clockwise: false)
177
+ canvas.arc(50, 40, a: 25, end_angle: 180, clockwise: true)
178
+ end
179
+
180
+ # Draws arrows showing the direction of the #shapes_to_paint
181
+ def arrows(canvas)
182
+ canvas.line_width = 1
183
+ canvas.polyline(95, 45, 100, 50, 105, 45)
184
+ canvas.polyline(55, 105, 50, 100, 55, 95)
185
+ canvas.polyline(-5, 55, 0, 50, 5, 55)
186
+ canvas.polyline(45, 5, 50, 0, 45, -5)
187
+ canvas.polyline(55, 90, 50, 85, 55, 80)
188
+ canvas.polyline(55, 20, 50, 15, 55, 10)
189
+ canvas.stroke
190
+ end
191
+
192
+ # Path painting and clipping operations: stroke, close and stroke, fill nonzero,
193
+ # fill even-odd, fill nonzero and stroke, fill even-odd and stroke, close and
194
+ # fill nonzero and stroke, close fill even-odd and stroke, clip even-odd, clip
195
+ # nonzero
196
+ canvas.translate(0, 190) do
197
+ canvas.fill_color(0.3, 0.7, 0.7)
198
+
199
+ [
200
+ [:stroke], [:close_stroke], [:fill, :nonzero], [:fill, :even_odd],
201
+ [:fill_stroke, :nonzero], [:fill_stroke, :even_odd],
202
+ [:close_fill_stroke, :nonzero], [:close_fill_stroke, :even_odd]
203
+ ].each_with_index do |op, index|
204
+ row = (1 - (index / 4))
205
+ column = index % 4
206
+ x = 50 + 80 * column
207
+ y = 80 * row
208
+ canvas.transform(0.6, 0, 0, 0.6, x, y) do
209
+ shapes_to_paint(canvas)
210
+ canvas.send(*op)
211
+ arrows(canvas)
212
+ end
213
+ end
214
+
215
+ canvas.fill_color(0.7, 0.7, 0.3)
216
+
217
+ [:even_odd, :nonzero].each_with_index do |op, index|
218
+ canvas.translate(370 + 110 * index, 20) do
219
+ canvas.circle(50, 50, 50)
220
+ canvas.circle(50, 50, 20)
221
+ canvas.clip_path(op)
222
+ canvas.end_path
223
+ canvas.rectangle(0, 0, 100, 100, radius: 100)
224
+ canvas.fill_stroke
225
+ end
226
+ end
227
+ end
228
+
229
+ # Some composite shapes, an image and a form XObject
230
+ canvas.translate(0, 80) do
231
+ canvas.fill_color(0.3, 0.7, 0.7)
232
+ canvas.rectangle(50, 0, 80, 80, radius: 80)
233
+ canvas.fill
234
+
235
+ solid = canvas.graphic_object(:solid_arc, cx: 190, cy: 40, inner_a: 20, inner_b: 15,
236
+ outer_a: 40, outer_b: 30, start_angle: 10, end_angle: 130)
237
+
238
+ canvas.line_width(0.5)
239
+ canvas.opacity(fill_alpha: 0.5, stroke_alpha: 0.2) do
240
+ canvas.fill_color('AA8888').draw(solid).fill_stroke
241
+ canvas.fill_color('88AA88').draw(solid, start_angle: 130, end_angle: 220).fill_stroke
242
+ canvas.fill_color('8888AA').draw(solid, start_angle: 220, end_angle: 10).fill_stroke
243
+
244
+ solid.configure(inner_a: 0, inner_b: 0, outer_a: 40, outer_b: 40, cx: 290)
245
+ canvas.fill_color('AA8888').draw(solid, start_angle: 10, end_angle: 130).fill_stroke
246
+ canvas.fill_color('88AA88').draw(solid, start_angle: 130, end_angle: 220).fill_stroke
247
+ canvas.fill_color('8888AA').draw(solid, start_angle: 220, end_angle: 10).fill_stroke
248
+
249
+ canvas.image(File.join(__dir__, 'machupicchu.jpg'), at: [350, 0], height: 80)
250
+ end
251
+ end
252
+
253
+ # A simple rainbow color band
254
+ canvas.translate(0, 20) do
255
+ canvas.line_width = 6
256
+ freq = 0.1
257
+ 0.upto(100) do |i|
258
+ r = Math.sin(freq * i) * 127 + 128
259
+ g = Math.sin(freq * i + 2) * 127 + 128
260
+ b = Math.sin(freq * i + 4) * 127 + 128
261
+ canvas.stroke_color(r.to_i, g.to_i, b.to_i)
262
+ canvas.line(50 + i * 5, 0, 50 + i * 5, 40)
263
+ canvas.stroke
264
+ end
265
+ end
266
+
267
+ # Reusing the already draw graphics for an XObject
268
+ # Note that converting the page to a form XObject automatically closes all open
269
+ # graphics states, therefore this can't be inside the above Canvas#translate
270
+ # call
271
+ form = doc.add(page.to_form_xobject(reference: false))
272
+ canvas.rectangle(480, 80, form.box.width * (100 / form.box.height.to_f), 100).stroke
273
+ canvas.xobject(form, at: [480, 80], height: 100)
274
+
275
+ doc.write('graphics.pdf', optimize: true)
@@ -0,0 +1,50 @@
1
+ # # Arcs and Solid Arcs
2
+ #
3
+ # This example shows how to use the graphic objects `:arc` and `:solid_arc` to
4
+ # draw simple pie charts.
5
+ #
6
+ # Usage:
7
+ # : `ruby arcs.rb`
8
+ #
9
+
10
+ require 'hexapdf'
11
+
12
+ doc = HexaPDF::Document.new
13
+ page = doc.pages.add
14
+ canvas = page.canvas
15
+
16
+ radius = 75
17
+
18
+ # Left pie chart
19
+ center = [page.box.width * 0.25, page.box.height * 0.85]
20
+ pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
21
+ outer_a: radius, outer_b: radius)
22
+ canvas.fill_color('ddddff')
23
+ canvas.draw(pie, start_angle: 30, end_angle: 110).fill
24
+ canvas.fill_color('ffdddd')
25
+ canvas.draw(pie, start_angle: 110, end_angle: 130).fill
26
+ canvas.fill_color('ddffdd')
27
+ canvas.draw(pie, start_angle: 130, end_angle: 30).fill
28
+
29
+ arc = canvas.graphic_object(:arc, cx: center[0], cy: center[1],
30
+ a: radius, b: radius)
31
+ canvas.stroke_color('0000ff')
32
+ canvas.draw(arc, start_angle: 30, end_angle: 110).stroke
33
+ canvas.stroke_color('ff0000')
34
+ canvas.draw(arc, start_angle: 110, end_angle: 130).stroke
35
+ canvas.stroke_color('00ff00')
36
+ canvas.draw(arc, start_angle: 130, end_angle: 30).stroke
37
+
38
+ # Right pie chart
39
+ center = [page.box.width * 0.75, page.box.height * 0.85]
40
+ canvas.stroke_color('777777')
41
+ pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
42
+ outer_a: radius, outer_b: radius)
43
+ canvas.fill_color('ddddff')
44
+ canvas.draw(pie, start_angle: 30, end_angle: 110).fill_stroke
45
+ canvas.fill_color('ffdddd')
46
+ canvas.draw(pie, start_angle: 110, end_angle: 130).fill_stroke
47
+ canvas.fill_color('ddffdd')
48
+ canvas.draw(pie, start_angle: 130, end_angle: 30).fill_stroke
49
+
50
+ doc.write('arcs.pdf', optimize: true)
@@ -0,0 +1,23 @@
1
+ # # Optimizing a PDF File
2
+ #
3
+ # This example shows how to read a PDF file, optimize it and write it
4
+ # out again.
5
+ #
6
+ # The heavy work is done by the `:optimize` task which allows control
7
+ # over which aspects should be optimized. See [HexaPDF::Task::Optimize]
8
+ # for detailed information.
9
+ #
10
+ # The hexapdf binary provides an optimization command which does some
11
+ # additional operations like optimizing the page tree.
12
+ #
13
+ # Usage:
14
+ # : `ruby optimizing.rb INPUT.PDF`
15
+ #
16
+
17
+ require 'hexapdf'
18
+
19
+ HexaPDF::Document.open(ARGV.shift) do |doc|
20
+ doc.task(:optimize, compact: true, object_streams: :generate,
21
+ compress_pages: false)
22
+ doc.write('optimizing.pdf')
23
+ end
@@ -0,0 +1,27 @@
1
+ # # Merging PDF Files
2
+ #
3
+ # Merging of PDF files can be done in various ways of sophistication.
4
+ #
5
+ # The easiest way, which this example shows, just imports the pages of
6
+ # the source files into the target file. This preserves the page
7
+ # contents themselves but nothing else.
8
+ #
9
+ # For example, named destinations are not properly handled by the code.
10
+ # Sometimes other things like attached files or a document outline
11
+ # should also be preserved.
12
+ #
13
+ # The hexapdf binary provides a command for merging files which does
14
+ # the merging in a more sophisticated way.
15
+ #
16
+ # Usage:
17
+ # : `ruby merging.rb INPUT1.PDF INPUT2.PDF ...`
18
+ #
19
+
20
+ require 'hexapdf'
21
+
22
+ target = HexaPDF::Document.new
23
+ ARGV.each do |file|
24
+ pdf = HexaPDF::Document.open(file)
25
+ pdf.pages.each {|page| target.pages << target.import(page)}
26
+ end
27
+ target.write("2.merging.pdf", optimize: true)
@@ -0,0 +1,73 @@
1
+ # # Standard PDF Fonts
2
+ #
3
+ # This example shows all characters that are available in the standard 14 PDF
4
+ # fonts.
5
+ #
6
+ # The standard 14 PDF fonts are those fonts that all PDF reading/viewing
7
+ # applications need to support. They only provide a limited set of glyphs but
8
+ # have the advantage that they don't need to be embedded.
9
+ #
10
+ # Usage:
11
+ # : `ruby standard_pdf_fonts.rb`
12
+ #
13
+
14
+ require 'hexapdf'
15
+
16
+ def base_encoding_for_font(font)
17
+ case font.font_name
18
+ when 'Symbol', 'ZapfDingbats'
19
+ font.encoding
20
+ else
21
+ HexaPDF::Font::Encoding.for_name(:WinAnsiEncoding)
22
+ end
23
+ end
24
+
25
+ doc = HexaPDF::Document.new
26
+
27
+ HexaPDF::FontLoader::Standard14::MAPPING.each do |font_name, mapping|
28
+ mapping.each_key do |variant|
29
+ canvas = doc.pages.add.canvas
30
+ canvas.font("Helvetica", size: 14)
31
+ canvas.text("#{font_name} #{variant != :none ? variant : ''}", at: [100, 800])
32
+
33
+ canvas.font(font_name, size: 14, variant: variant)
34
+ canvas.leading = 20
35
+ font = canvas.font
36
+ encoding = base_encoding_for_font(font.wrapped_font)
37
+ used_glyphs = []
38
+
39
+ # Showing the glyphs of the WinAnsi or built-in encoding
40
+ canvas.move_text_cursor(offset: [100, 750])
41
+ (2..15).each do |y|
42
+ data = []
43
+ (0..15).each do |x|
44
+ code = y * 16 + x
45
+ glyph = font.glyph(encoding.name(code))
46
+ glyph = font.glyph(:space) if glyph.id == font.wrapped_font.missing_glyph_id
47
+ used_glyphs << glyph.name
48
+ data << glyph << -(2000 - glyph.width)
49
+ end
50
+ canvas.show_glyphs(data)
51
+ canvas.move_text_cursor
52
+ end
53
+
54
+ # Showing the remaining glyphs
55
+ canvas.move_text_cursor(offset: [0, -40], absolute: false)
56
+ glyphs = font.wrapped_font.metrics.character_metrics.keys.select do |k|
57
+ Symbol === k
58
+ end.sort - used_glyphs
59
+ canvas.font(font_name, size: 14, variant: variant, custom_encoding: true)
60
+ font = canvas.font
61
+ glyphs.each_slice(16).with_index do |slice, index|
62
+ data = []
63
+ slice.each do |name|
64
+ glyph = font.glyph(name)
65
+ data << glyph << -(2000 - glyph.width)
66
+ end
67
+ canvas.show_glyphs(data)
68
+ canvas.move_text_cursor
69
+ end
70
+ end
71
+ end
72
+
73
+ doc.write("standard_pdf_fonts.pdf", optimize: true)
@@ -0,0 +1,42 @@
1
+ # # TrueType Fonts
2
+ #
3
+ # This example displays all glyphs of a TrueType font and shows that using a
4
+ # TrueType font with HexaPDF is very similar to using one of the standard PDF
5
+ # fonts.
6
+ #
7
+ # Before a TrueType font can be used, HexaPDF needs to be made aware of it. This
8
+ # is done by setting the configuration option 'font.map'. For one-off usage of a
9
+ # font file, the file name itself can also be used.
10
+ #
11
+ # Once that is done the [HexaPDF::Content::Canvas#font] method can be used as
12
+ # usual.
13
+ #
14
+ # Usage:
15
+ # : `ruby truetype.rb [FONT_FILE]`
16
+ #
17
+
18
+ require 'hexapdf'
19
+
20
+ doc = HexaPDF::Document.new
21
+ font_file = ARGV.shift || File.join(__dir__, '../test/data/fonts/Ubuntu-Title.ttf')
22
+ wrapper = doc.fonts.add(font_file)
23
+ max_gid = wrapper.wrapped_font[:maxp].num_glyphs
24
+
25
+ 255.times do |page|
26
+ break unless page * 256 < max_gid
27
+ canvas = doc.pages.add.canvas
28
+ canvas.font("Helvetica", size: 10)
29
+ canvas.text("Font: #{wrapper.wrapped_font.full_name}", at: [50, 825])
30
+
31
+ canvas.font(font_file, size: 15)
32
+ 16.times do |y|
33
+ canvas.move_text_cursor(offset: [50, 800 - y * 50], absolute: true)
34
+ canvas.show_glyphs((0..15).map do |i|
35
+ gid = page * 256 + y * 16 + i
36
+ glyph = wrapper.glyph(gid)
37
+ gid >= max_gid ? [] : [glyph, -(2000 - glyph.width)]
38
+ end.flatten!)
39
+ end
40
+ end
41
+
42
+ doc.write("truetype.pdf", optimize: true)
@@ -0,0 +1,55 @@
1
+ # # Show Character Bounding Boxes
2
+ #
3
+ # This examples shows how to process the contents of a page. It finds all
4
+ # characters on a page and surrounds them with their bounding box. Additionally,
5
+ # all consecutive text runs are also surrounded by a box.
6
+ #
7
+ # The code provides two ways of generating the boxes. The commented part of
8
+ # `ShowTextProcessor#show_text` uses a polyline since some characters may be
9
+ # transforemd (rotated or skewed). The un-commented part uses rectangles which
10
+ # is faster and correct for most but not all cases.
11
+ #
12
+ # Usage:
13
+ # : `ruby show_char_boxes.rb INPUT.PDF`
14
+ #
15
+
16
+ require 'hexapdf'
17
+
18
+ class ShowTextProcessor < HexaPDF::Content::Processor
19
+
20
+ def initialize(page)
21
+ super()
22
+ @canvas = page.canvas(type: :overlay)
23
+ end
24
+
25
+ def show_text(str)
26
+ boxes = decode_text_with_positioning(str)
27
+ return if boxes.string.empty?
28
+
29
+ @canvas.line_width = 1
30
+ @canvas.stroke_color(224, 0, 0)
31
+ # Polyline for transformed characters
32
+ #boxes.each {|box| @canvas.polyline(*box.points).close_subpath.stroke}
33
+ # Using rectangles is faster but not 100% correct
34
+ boxes.each do |box|
35
+ x, y = *box.lower_left
36
+ tx, ty = *box.upper_right
37
+ @canvas.rectangle(x, y, tx - x, ty - y).stroke
38
+ end
39
+
40
+ @canvas.line_width = 0.5
41
+ @canvas.stroke_color(0, 224, 0)
42
+ @canvas.polyline(*boxes.lower_left, *boxes.lower_right,
43
+ *boxes.upper_right, *boxes.upper_left).close_subpath.stroke
44
+ end
45
+ alias :show_text_with_positioning :show_text
46
+
47
+ end
48
+
49
+ doc = HexaPDF::Document.open(ARGV.shift)
50
+ doc.pages.each_with_index do |page, index|
51
+ puts "Processing page #{index + 1}"
52
+ processor = ShowTextProcessor.new(page)
53
+ page.process_contents(processor)
54
+ end
55
+ doc.write('show_char_boxes.pdf', optimize: true)
@@ -0,0 +1,47 @@
1
+ # # Text Layouter - Alignment
2
+ #
3
+ # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
4
+ # inside a rectangular area, with various horizontal and vertical alignment
5
+ # options.
6
+ #
7
+ # The text can be aligned horizontally by setting [HexaPDF::Layout::Style#align]
8
+ # and vertically by [HexaPDF::Layout::Style#valign]. In this example, a sample
9
+ # text is laid out in all possible combinations.
10
+ #
11
+ # Usage:
12
+ # : `ruby text_layouter_alignment.rb`
13
+ #
14
+
15
+ require 'hexapdf'
16
+
17
+ sample_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit,
18
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
19
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
20
+ aliquip ex ea commodo consequat. at".tr("\n", ' ')
21
+
22
+ doc = HexaPDF::Document.new
23
+ canvas = doc.pages.add.canvas
24
+ canvas.font("Times", size: 10, variant: :bold)
25
+
26
+ width = 100
27
+ height = 150
28
+ y_base = 800
29
+ tf = HexaPDF::Layout::TextFragment.create(sample_text,
30
+ font: doc.fonts.add("Times"))
31
+ tl = HexaPDF::Layout::TextLayouter.new
32
+
33
+ [:left, :center, :right, :justify].each_with_index do |align, x_index|
34
+ x = x_index * (width + 20) + 70
35
+ canvas.text(align.to_s, at: [x + 40, y_base + 15])
36
+
37
+ [:top, :center, :bottom].each_with_index do |valign, y_index|
38
+ y = y_base - (height + 30) * y_index
39
+ canvas.text(valign.to_s, at: [20, y - height / 2]) if x_index == 0
40
+
41
+ tl.style.align(align).valign(valign)
42
+ tl.fit([tf], width, height).draw(canvas, x, y)
43
+ canvas.stroke_color(128, 0, 0).rectangle(x, y, width, -height).stroke
44
+ end
45
+ end
46
+
47
+ doc.write("text_layouter_alignment.pdf", optimize: true)
@@ -0,0 +1,64 @@
1
+ # # Text Layouter - Inline Boxes
2
+ #
3
+ # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
4
+ # mixed with inline boxes.
5
+ #
6
+ # Inline boxes are used for showing graphics that follow the flow of the text.
7
+ # This means that their horizontal and their general vertical position is
8
+ # determined by the text layout functionality. However, inline boxes may be
9
+ # vertically aligned to various positions, like the baseline, the top/bottom of
10
+ # the text and the top/bottom of the line.
11
+ #
12
+ # This example shows some text containing emoticons that are replaced with their
13
+ # graphical representation, with normal smileys being aligned to the baseline
14
+ # and winking smileys to the top of the line.
15
+ #
16
+ # An inline box is a simple wrapper around a generic box that adheres to the
17
+ # necessary interface. Therefore they don't do any drawing operations themselves
18
+ # but delegate to their wrapped box. This means, for example, that inline boxes
19
+ # can use background colors or borders without doing anything special.
20
+ #
21
+ # Usage:
22
+ # : `ruby text_layouter_inline_boxes.rb`
23
+ #
24
+
25
+ require 'hexapdf'
26
+
27
+ include HexaPDF::Layout
28
+
29
+ sample_text = "Lorem ipsum :-) dolor sit amet, consectetur adipiscing
30
+ ;-) elit, sed do eiusmod tempor incididunt :-) ut labore et dolore magna
31
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
32
+ laboris nisi ut aliquip ex ea commodo consequat ;-). Duis aute irure
33
+ dolor in reprehenderit in voluptate velit esse cillum :-) dolore eu
34
+ fugiat nulla pariatur. ".tr("\n", ' ') * 4
35
+
36
+ doc = HexaPDF::Document.new
37
+ emoji_smile = doc.images.add(File.join(__dir__, "emoji-smile.png"))
38
+ emoji_wink = doc.images.add(File.join(__dir__, "emoji-wink.png"))
39
+ size = 10
40
+
41
+ items = sample_text.split(/(:-\)|;-\))/).map do |part|
42
+ case part
43
+ when ':-)'
44
+ InlineBox.create(width: size * 2, height: size * 2, content_box: true,
45
+ background_color: [162, 234, 247], padding: 2) do |canvas, box|
46
+ canvas.image(emoji_smile, at: [0, 0], width: box.content_width)
47
+ end
48
+ when ';-)'
49
+ InlineBox.create(width: size, height: size, content_box: true,
50
+ valign: :top, padding: 5, margin: [0, 10],
51
+ border: {width: [1, 2], color: [200, 40]}) do |canvas, box|
52
+ canvas.image(emoji_wink, at: [0, 0], width: box.content_width)
53
+ end
54
+ else
55
+ TextFragment.create(part, font: doc.fonts.add("Times"), font_size: 18)
56
+ end
57
+ end
58
+
59
+ layouter = TextLayouter.new
60
+ layouter.style.align = :justify
61
+ layouter.style.line_spacing(:proportional, 1.5)
62
+ layouter.fit(items, 500, 700).draw(doc.pages.add.canvas, 50, 800)
63
+
64
+ doc.write("text_layouter_inline_boxes.pdf")