hexapdf 0.17.1 → 0.17.2

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 (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")