prawn 0.13.0 → 2.4.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 (348) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +10 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -16
  6. data/Rakefile +17 -39
  7. data/lib/prawn/document/bounding_box.rb +85 -42
  8. data/lib/prawn/document/column_box.rb +21 -11
  9. data/lib/prawn/document/internals.rb +40 -147
  10. data/lib/prawn/document/span.rb +25 -17
  11. data/lib/prawn/document.rb +286 -245
  12. data/lib/prawn/encoding.rb +68 -101
  13. data/lib/prawn/errors.rb +47 -43
  14. data/lib/prawn/font.rb +204 -155
  15. data/lib/prawn/font_metric_cache.rb +25 -21
  16. data/lib/prawn/fonts/afm.rb +292 -0
  17. data/lib/prawn/{font → fonts}/dfont.rb +7 -13
  18. data/lib/prawn/fonts/otf.rb +11 -0
  19. data/lib/prawn/fonts/ttc.rb +36 -0
  20. data/lib/prawn/{font → fonts}/ttf.rb +142 -80
  21. data/lib/prawn/graphics/blend_mode.rb +65 -0
  22. data/lib/prawn/graphics/cap_style.rb +6 -5
  23. data/lib/prawn/graphics/color.rb +47 -44
  24. data/lib/prawn/graphics/dash.rb +30 -13
  25. data/lib/prawn/graphics/join_style.rb +13 -6
  26. data/lib/prawn/graphics/patterns.rb +221 -90
  27. data/lib/prawn/graphics/transformation.rb +21 -12
  28. data/lib/prawn/graphics/transparency.rb +21 -17
  29. data/lib/prawn/graphics.rb +155 -128
  30. data/lib/prawn/{layout/grid.rb → grid.rb} +110 -47
  31. data/lib/prawn/image_handler.rb +16 -2
  32. data/lib/prawn/images/image.rb +4 -2
  33. data/lib/prawn/images/jpg.rb +39 -30
  34. data/lib/prawn/images/png.rb +132 -169
  35. data/lib/prawn/images.rb +70 -62
  36. data/lib/prawn/measurement_extensions.rb +15 -10
  37. data/lib/prawn/measurements.rb +22 -23
  38. data/lib/prawn/outline.rb +301 -13
  39. data/lib/prawn/repeater.rb +19 -17
  40. data/lib/prawn/security/arcfour.rb +54 -0
  41. data/lib/prawn/security.rb +108 -86
  42. data/lib/prawn/soft_mask.rb +40 -41
  43. data/lib/prawn/stamp.rb +29 -12
  44. data/lib/prawn/text/box.rb +27 -29
  45. data/lib/prawn/text/formatted/arranger.rb +110 -67
  46. data/lib/prawn/text/formatted/box.rb +233 -165
  47. data/lib/prawn/text/formatted/fragment.rb +27 -27
  48. data/lib/prawn/text/formatted/line_wrap.rb +137 -97
  49. data/lib/prawn/text/formatted/parser.rb +149 -127
  50. data/lib/prawn/text/formatted/wrap.rb +57 -37
  51. data/lib/prawn/text/formatted.rb +6 -4
  52. data/lib/prawn/text.rb +105 -73
  53. data/lib/prawn/transformation_stack.rb +44 -0
  54. data/lib/prawn/utilities.rb +11 -21
  55. data/lib/prawn/version.rb +5 -0
  56. data/lib/prawn/view.rb +101 -0
  57. data/lib/prawn.rb +42 -68
  58. data/{data/images/fractal.jpg → manual/absolute_position.pdf} +0 -0
  59. data/manual/basic_concepts/adding_pages.rb +9 -10
  60. data/manual/basic_concepts/basic_concepts.rb +33 -24
  61. data/manual/basic_concepts/creation.rb +10 -11
  62. data/manual/basic_concepts/cursor.rb +9 -10
  63. data/manual/basic_concepts/measurement.rb +10 -11
  64. data/manual/basic_concepts/origin.rb +8 -9
  65. data/manual/basic_concepts/other_cursor_helpers.rb +17 -18
  66. data/manual/basic_concepts/view.rb +48 -0
  67. data/manual/bounding_box/bounding_box.rb +31 -29
  68. data/manual/bounding_box/bounds.rb +17 -18
  69. data/manual/bounding_box/canvas.rb +8 -9
  70. data/manual/bounding_box/creation.rb +8 -9
  71. data/manual/bounding_box/indentation.rb +22 -23
  72. data/manual/bounding_box/nesting.rb +32 -25
  73. data/manual/bounding_box/russian_boxes.rb +19 -19
  74. data/manual/bounding_box/stretchy.rb +18 -20
  75. data/manual/contents.rb +35 -0
  76. data/manual/cover.rb +43 -0
  77. data/manual/document_and_page_options/background.rb +16 -14
  78. data/manual/document_and_page_options/document_and_page_options.rb +26 -23
  79. data/manual/document_and_page_options/metadata.rb +21 -19
  80. data/manual/document_and_page_options/page_margins.rb +20 -22
  81. data/manual/document_and_page_options/page_size.rb +15 -15
  82. data/manual/document_and_page_options/print_scaling.rb +23 -0
  83. data/manual/example_helper.rb +5 -408
  84. data/manual/graphics/blend_mode.rb +52 -0
  85. data/manual/graphics/circle_and_ellipse.rb +8 -9
  86. data/manual/graphics/color.rb +11 -13
  87. data/manual/graphics/common_lines.rb +13 -12
  88. data/manual/graphics/fill_and_stroke.rb +10 -11
  89. data/manual/graphics/fill_rules.rb +13 -12
  90. data/manual/graphics/gradients.rb +28 -22
  91. data/manual/graphics/graphics.rb +52 -46
  92. data/manual/graphics/helper.rb +20 -10
  93. data/manual/graphics/line_width.rb +13 -12
  94. data/manual/graphics/lines_and_curves.rb +13 -14
  95. data/manual/graphics/polygon.rb +10 -12
  96. data/manual/graphics/rectangle.rb +7 -8
  97. data/manual/graphics/rotate.rb +9 -12
  98. data/manual/graphics/scale.rb +19 -18
  99. data/manual/graphics/soft_masks.rb +5 -7
  100. data/manual/graphics/stroke_cap.rb +10 -11
  101. data/manual/graphics/stroke_dash.rb +16 -17
  102. data/manual/graphics/stroke_join.rb +10 -11
  103. data/manual/graphics/translate.rb +13 -13
  104. data/manual/graphics/transparency.rb +11 -13
  105. data/manual/{manual/how_to_read_this_manual.rb → how_to_read_this_manual.rb} +23 -25
  106. data/manual/images/absolute_position.rb +9 -10
  107. data/manual/images/fit.rb +9 -10
  108. data/manual/images/horizontal.rb +13 -14
  109. data/manual/images/images.rb +31 -30
  110. data/manual/images/plain_image.rb +6 -7
  111. data/manual/images/scale.rb +12 -13
  112. data/manual/images/vertical.rb +19 -17
  113. data/manual/images/width_and_height.rb +13 -14
  114. data/manual/layout/boxes.rb +14 -15
  115. data/manual/layout/content.rb +12 -13
  116. data/manual/layout/layout.rb +19 -20
  117. data/manual/layout/simple_grid.rb +8 -9
  118. data/manual/outline/add_subsection_to.rb +26 -27
  119. data/manual/outline/insert_section_after.rb +19 -20
  120. data/manual/outline/outline.rb +23 -22
  121. data/manual/outline/sections_and_pages.rb +24 -25
  122. data/manual/repeatable_content/alternate_page_numbering.rb +36 -0
  123. data/manual/repeatable_content/page_numbering.rb +20 -19
  124. data/manual/repeatable_content/repeatable_content.rb +26 -22
  125. data/manual/repeatable_content/repeater.rb +18 -19
  126. data/manual/repeatable_content/stamp.rb +18 -19
  127. data/manual/security/encryption.rb +8 -11
  128. data/manual/security/permissions.rb +20 -15
  129. data/manual/security/security.rb +20 -20
  130. data/manual/table.rb +16 -0
  131. data/manual/text/alignment.rb +17 -18
  132. data/manual/text/color.rb +13 -13
  133. data/manual/text/column_box.rb +10 -12
  134. data/manual/text/fallback_fonts.rb +29 -25
  135. data/manual/text/font.rb +17 -18
  136. data/manual/text/font_size.rb +21 -22
  137. data/manual/text/font_style.rb +12 -10
  138. data/manual/text/formatted_callbacks.rb +36 -26
  139. data/manual/text/formatted_text.rb +41 -34
  140. data/manual/text/free_flowing_text.rb +28 -29
  141. data/manual/text/inline.rb +23 -26
  142. data/manual/text/kerning_and_character_spacing.rb +20 -21
  143. data/manual/text/leading.rb +10 -11
  144. data/manual/text/line_wrapping.rb +40 -21
  145. data/manual/text/paragraph_indentation.rb +17 -12
  146. data/manual/text/positioned_text.rb +19 -20
  147. data/manual/text/registering_families.rb +33 -30
  148. data/manual/text/rendering_and_color.rb +11 -12
  149. data/manual/text/right_to_left_text.rb +31 -20
  150. data/manual/text/rotation.rb +36 -27
  151. data/manual/text/single_usage.rb +13 -14
  152. data/manual/text/text.rb +62 -62
  153. data/manual/text/text_box_excess.rb +22 -19
  154. data/manual/text/text_box_extensions.rb +21 -18
  155. data/manual/text/text_box_overflow.rb +28 -21
  156. data/manual/text/utf8.rb +16 -17
  157. data/manual/text/win_ansi_charset.rb +29 -26
  158. data/prawn.gemspec +45 -43
  159. data/spec/extensions/encoding_helpers.rb +4 -3
  160. data/spec/prawn/document/bounding_box_spec.rb +550 -0
  161. data/spec/prawn/document/column_box_spec.rb +75 -0
  162. data/spec/prawn/document/security_spec.rb +176 -0
  163. data/spec/prawn/document_annotations_spec.rb +76 -0
  164. data/spec/prawn/document_destinations_spec.rb +15 -0
  165. data/spec/prawn/document_grid_spec.rb +99 -0
  166. data/spec/prawn/document_reference_spec.rb +27 -0
  167. data/spec/prawn/document_span_spec.rb +44 -0
  168. data/spec/prawn/document_spec.rb +805 -0
  169. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  170. data/spec/prawn/font_spec.rb +544 -0
  171. data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
  172. data/spec/prawn/graphics/transparency_spec.rb +81 -0
  173. data/spec/prawn/graphics_spec.rb +872 -0
  174. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  175. data/spec/prawn/image_handler_spec.rb +53 -0
  176. data/spec/prawn/images/jpg_spec.rb +20 -0
  177. data/spec/prawn/images/png_spec.rb +283 -0
  178. data/spec/prawn/images_spec.rb +229 -0
  179. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  180. data/spec/prawn/outline_spec.rb +512 -0
  181. data/spec/prawn/repeater_spec.rb +166 -0
  182. data/spec/prawn/soft_mask_spec.rb +74 -0
  183. data/spec/prawn/stamp_spec.rb +173 -0
  184. data/spec/prawn/text/box_spec.rb +1110 -0
  185. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  186. data/spec/prawn/text/formatted/box_spec.rb +849 -0
  187. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  188. data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
  189. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  190. data/spec/prawn/text_draw_text_spec.rb +150 -0
  191. data/spec/prawn/text_rendering_mode_spec.rb +48 -0
  192. data/spec/prawn/text_spacing_spec.rb +95 -0
  193. data/spec/prawn/text_spec.rb +603 -0
  194. data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
  195. data/spec/prawn/transformation_stack_spec.rb +66 -0
  196. data/spec/prawn/view_spec.rb +63 -0
  197. data/spec/prawn_manual_spec.rb +35 -0
  198. data/spec/spec_helper.rb +22 -21
  199. data.tar.gz.sig +0 -0
  200. metadata +168 -307
  201. metadata.gz.sig +0 -0
  202. data/README.md +0 -109
  203. data/data/encodings/win_ansi.txt +0 -29
  204. data/data/images/16bit.alpha +0 -0
  205. data/data/images/16bit.dat +0 -0
  206. data/data/images/16bit.png +0 -0
  207. data/data/images/arrow.png +0 -0
  208. data/data/images/arrow2.png +0 -0
  209. data/data/images/barcode_issue.png +0 -0
  210. data/data/images/dice.alpha +0 -0
  211. data/data/images/dice.dat +0 -0
  212. data/data/images/dice.png +0 -0
  213. data/data/images/dice_interlaced.png +0 -0
  214. data/data/images/indexed_color.dat +0 -0
  215. data/data/images/indexed_color.png +0 -0
  216. data/data/images/letterhead.jpg +0 -0
  217. data/data/images/page_white_text.alpha +0 -0
  218. data/data/images/page_white_text.dat +0 -0
  219. data/data/images/page_white_text.png +0 -0
  220. data/data/images/pigs.jpg +0 -0
  221. data/data/images/prawn.png +0 -0
  222. data/data/images/ruport.png +0 -0
  223. data/data/images/ruport_data.dat +0 -0
  224. data/data/images/ruport_transparent.png +0 -0
  225. data/data/images/ruport_type0.png +0 -0
  226. data/data/images/stef.jpg +0 -0
  227. data/data/images/tru256.bmp +0 -0
  228. data/data/images/web-links.dat +0 -1
  229. data/data/images/web-links.png +0 -0
  230. data/data/pdfs/complex_template.pdf +0 -0
  231. data/data/pdfs/contains_ttf_font.pdf +0 -0
  232. data/data/pdfs/encrypted.pdf +0 -0
  233. data/data/pdfs/form.pdf +1 -819
  234. data/data/pdfs/hexagon.pdf +0 -61
  235. data/data/pdfs/indirect_reference.pdf +0 -86
  236. data/data/pdfs/multipage_template.pdf +0 -127
  237. data/data/pdfs/nested_pages.pdf +0 -118
  238. data/data/pdfs/page_without_mediabox.pdf +0 -193
  239. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  240. data/data/pdfs/two_hexagons.pdf +0 -90
  241. data/data/pdfs/version_1_6.pdf +0 -61
  242. data/data/shift_jis_text.txt +0 -1
  243. data/lib/pdf/core/annotations.rb +0 -60
  244. data/lib/pdf/core/byte_string.rb +0 -9
  245. data/lib/pdf/core/destinations.rb +0 -90
  246. data/lib/pdf/core/document_state.rb +0 -78
  247. data/lib/pdf/core/filter_list.rb +0 -51
  248. data/lib/pdf/core/filters.rb +0 -36
  249. data/lib/pdf/core/graphics_state.rb +0 -68
  250. data/lib/pdf/core/literal_string.rb +0 -16
  251. data/lib/pdf/core/name_tree.rb +0 -177
  252. data/lib/pdf/core/object_store.rb +0 -320
  253. data/lib/pdf/core/outline.rb +0 -315
  254. data/lib/pdf/core/page.rb +0 -212
  255. data/lib/pdf/core/page_geometry.rb +0 -126
  256. data/lib/pdf/core/pdf_object.rb +0 -124
  257. data/lib/pdf/core/reference.rb +0 -103
  258. data/lib/pdf/core/stream.rb +0 -98
  259. data/lib/pdf/core/text.rb +0 -275
  260. data/lib/pdf/core.rb +0 -35
  261. data/lib/prawn/compatibility.rb +0 -91
  262. data/lib/prawn/document/graphics_state.rb +0 -73
  263. data/lib/prawn/document/snapshot.rb +0 -89
  264. data/lib/prawn/font/afm.rb +0 -203
  265. data/lib/prawn/layout.rb +0 -20
  266. data/lib/prawn/table/cell/image.rb +0 -70
  267. data/lib/prawn/table/cell/in_table.rb +0 -27
  268. data/lib/prawn/table/cell/span_dummy.rb +0 -92
  269. data/lib/prawn/table/cell/subtable.rb +0 -65
  270. data/lib/prawn/table/cell/text.rb +0 -153
  271. data/lib/prawn/table/cell.rb +0 -770
  272. data/lib/prawn/table/cells.rb +0 -295
  273. data/lib/prawn/table.rb +0 -643
  274. data/manual/example_file.rb +0 -116
  275. data/manual/example_package.rb +0 -53
  276. data/manual/example_section.rb +0 -46
  277. data/manual/manual/cover.rb +0 -35
  278. data/manual/manual/foreword.rb +0 -85
  279. data/manual/manual/manual.rb +0 -35
  280. data/manual/syntax_highlight.rb +0 -52
  281. data/manual/table/basic_block.rb +0 -53
  282. data/manual/table/before_rendering_page.rb +0 -26
  283. data/manual/table/cell_border_lines.rb +0 -24
  284. data/manual/table/cell_borders_and_bg.rb +0 -31
  285. data/manual/table/cell_dimensions.rb +0 -30
  286. data/manual/table/cell_text.rb +0 -38
  287. data/manual/table/column_widths.rb +0 -30
  288. data/manual/table/content_and_subtables.rb +0 -39
  289. data/manual/table/creation.rb +0 -27
  290. data/manual/table/filtering.rb +0 -36
  291. data/manual/table/flow_and_header.rb +0 -17
  292. data/manual/table/image_cells.rb +0 -33
  293. data/manual/table/position.rb +0 -29
  294. data/manual/table/row_colors.rb +0 -20
  295. data/manual/table/span.rb +0 -30
  296. data/manual/table/style.rb +0 -22
  297. data/manual/table/table.rb +0 -52
  298. data/manual/table/width.rb +0 -27
  299. data/manual/templates/full_template.rb +0 -25
  300. data/manual/templates/page_template.rb +0 -48
  301. data/manual/templates/templates.rb +0 -27
  302. data/manual/text/group.rb +0 -29
  303. data/spec/acceptance/png.rb +0 -23
  304. data/spec/annotations_spec.rb +0 -74
  305. data/spec/bounding_box_spec.rb +0 -493
  306. data/spec/cell_spec.rb +0 -628
  307. data/spec/column_box_spec.rb +0 -33
  308. data/spec/destinations_spec.rb +0 -15
  309. data/spec/document_spec.rb +0 -761
  310. data/spec/extensions/mocha.rb +0 -44
  311. data/spec/filters_spec.rb +0 -34
  312. data/spec/font_metric_cache_spec.rb +0 -52
  313. data/spec/font_spec.rb +0 -464
  314. data/spec/formatted_text_arranger_spec.rb +0 -421
  315. data/spec/formatted_text_box_spec.rb +0 -650
  316. data/spec/formatted_text_fragment_spec.rb +0 -298
  317. data/spec/graphics_spec.rb +0 -651
  318. data/spec/grid_spec.rb +0 -85
  319. data/spec/image_handler_spec.rb +0 -42
  320. data/spec/images_spec.rb +0 -157
  321. data/spec/inline_formatted_text_parser_spec.rb +0 -564
  322. data/spec/jpg_spec.rb +0 -25
  323. data/spec/line_wrap_spec.rb +0 -333
  324. data/spec/measurement_units_spec.rb +0 -23
  325. data/spec/name_tree_spec.rb +0 -112
  326. data/spec/object_store_spec.rb +0 -170
  327. data/spec/outline_spec.rb +0 -448
  328. data/spec/pdf_object_spec.rb +0 -172
  329. data/spec/png_spec.rb +0 -240
  330. data/spec/reference_spec.rb +0 -82
  331. data/spec/repeater_spec.rb +0 -158
  332. data/spec/security_spec.rb +0 -158
  333. data/spec/snapshot_spec.rb +0 -186
  334. data/spec/soft_mask_spec.rb +0 -117
  335. data/spec/span_spec.rb +0 -44
  336. data/spec/stamp_spec.rb +0 -158
  337. data/spec/stream_spec.rb +0 -58
  338. data/spec/stroke_styles_spec.rb +0 -211
  339. data/spec/table/span_dummy_spec.rb +0 -17
  340. data/spec/table_spec.rb +0 -1355
  341. data/spec/template_spec.rb +0 -351
  342. data/spec/text_at_spec.rb +0 -130
  343. data/spec/text_box_spec.rb +0 -1030
  344. data/spec/text_rendering_mode_spec.rb +0 -45
  345. data/spec/text_spacing_spec.rb +0 -93
  346. data/spec/text_spec.rb +0 -425
  347. data/spec/text_with_inline_formatting_spec.rb +0 -35
  348. data/spec/transparency_spec.rb +0 -89
@@ -0,0 +1,849 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Prawn::Text::Formatted::Box do
6
+ let(:pdf) { create_pdf }
7
+
8
+ describe 'wrapping' do
9
+ it 'does not wrap between two fragments' do
10
+ texts = [
11
+ { text: 'Hello ' },
12
+ { text: 'World' },
13
+ { text: '2', styles: [:superscript] }
14
+ ]
15
+ text_box = described_class.new(
16
+ texts,
17
+ document: pdf,
18
+ width: pdf.width_of('Hello World')
19
+ )
20
+ text_box.render
21
+ expect(text_box.text).to eq("Hello\nWorld2")
22
+ end
23
+
24
+ it 'does not raise an Encoding::CompatibilityError when keeping a TTF and '\
25
+ 'an AFM font together' do
26
+ file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
27
+
28
+ pdf.font_families['Kai'] = {
29
+ normal: { file: file, font: 'Kai' }
30
+ }
31
+
32
+ texts = [
33
+ { text: 'Hello ' },
34
+ { text: '再见', font: 'Kai' },
35
+ { text: 'World' }
36
+ ]
37
+ text_box = described_class.new(
38
+ texts,
39
+ document: pdf,
40
+ width: pdf.width_of('Hello World')
41
+ )
42
+
43
+ text_box.render
44
+ end
45
+
46
+ it 'wraps between two fragments when the preceding fragment ends with '\
47
+ 'a white space' do
48
+ texts = [
49
+ { text: 'Hello ' },
50
+ { text: 'World ' },
51
+ { text: '2', styles: [:superscript] }
52
+ ]
53
+ text_box = described_class.new(
54
+ texts,
55
+ document: pdf,
56
+ width: pdf.width_of('Hello World')
57
+ )
58
+ text_box.render
59
+ expect(text_box.text).to eq("Hello World\n2")
60
+
61
+ texts = [
62
+ { text: 'Hello ' },
63
+ { text: "World\n" },
64
+ { text: '2', styles: [:superscript] }
65
+ ]
66
+ text_box = described_class.new(
67
+ texts,
68
+ document: pdf,
69
+ width: pdf.width_of('Hello World')
70
+ )
71
+ text_box.render
72
+ expect(text_box.text).to eq("Hello World\n2")
73
+ end
74
+
75
+ it 'wraps between two fragments when the final fragment begins with '\
76
+ 'a white space' do
77
+ texts = [
78
+ { text: 'Hello ' },
79
+ { text: 'World' },
80
+ { text: ' 2', styles: [:superscript] }
81
+ ]
82
+ text_box = described_class.new(
83
+ texts,
84
+ document: pdf,
85
+ width: pdf.width_of('Hello World')
86
+ )
87
+ text_box.render
88
+ expect(text_box.text).to eq("Hello World\n2")
89
+
90
+ texts = [
91
+ { text: 'Hello ' },
92
+ { text: 'World' },
93
+ { text: "\n2", styles: [:superscript] }
94
+ ]
95
+ text_box = described_class.new(
96
+ texts,
97
+ document: pdf,
98
+ width: pdf.width_of('Hello World')
99
+ )
100
+ text_box.render
101
+ expect(text_box.text).to eq("Hello World\n2")
102
+ end
103
+
104
+ it 'properlies handle empty slices using default encoding' do
105
+ texts = [{
106
+ text: 'Noua Delineatio Geographica generalis | Apostolicarum ' \
107
+ 'peregrinationum | S FRANCISCI XAUERII | Indiarum & Iaponiæ Apostoli',
108
+ font: 'Courier',
109
+ size: 10
110
+ }]
111
+ text_box = described_class.new(
112
+ texts,
113
+ document: pdf,
114
+ width: pdf.width_of('Noua Delineatio Geographica gen')
115
+ )
116
+ expect do
117
+ text_box.render
118
+ end.to_not raise_error
119
+ expect(text_box.text).to eq(
120
+ "Noua Delineatio Geographica\ngeneralis | Apostolicarum\n" \
121
+ "peregrinationum | S FRANCISCI\nXAUERII | Indiarum & Iaponi\346\n" \
122
+ 'Apostoli'
123
+ )
124
+ end
125
+ end
126
+
127
+ describe 'Text::Formatted::Box with :fallback_fonts option that includes' \
128
+ 'a Chinese font and set of Chinese glyphs not in the current font' do
129
+ it 'changes the font to the Chinese font for the Chinese glyphs' do
130
+ file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
131
+ pdf.font_families['Kai'] = {
132
+ normal: { file: file, font: 'Kai' }
133
+ }
134
+ formatted_text = [
135
+ { text: 'hello你好' },
136
+ { text: '再见goodbye' }
137
+ ]
138
+ pdf.formatted_text_box(formatted_text, fallback_fonts: ['Kai'])
139
+
140
+ text = PDF::Inspector::Text.analyze(pdf.render)
141
+
142
+ fonts_used = text.font_settings.map { |e| e[:name] }
143
+ expect(fonts_used.length).to eq(4)
144
+ expect(fonts_used[0]).to eq(:Helvetica)
145
+ expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
146
+ expect(fonts_used[2].to_s).to match(/GBZenKai-Medium/)
147
+ expect(fonts_used[3]).to eq(:Helvetica)
148
+
149
+ expect(text.strings[0]).to eq('hello')
150
+ expect(text.strings[1]).to eq('你好')
151
+ expect(text.strings[2]).to eq('再见')
152
+ expect(text.strings[3]).to eq('goodbye')
153
+ end
154
+ end
155
+
156
+ describe 'Text::Formatted::Box with :fallback_fonts option that includes' \
157
+ 'an AFM font and Win-Ansi glyph not in the current Chinese font' do
158
+ it 'changes the font to the AFM font for the Win-Ansi glyph' do
159
+ file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
160
+ pdf.font_families['Kai'] = {
161
+ normal: { file: file, font: 'Kai' }
162
+ }
163
+ pdf.font('Kai')
164
+ formatted_text = [
165
+ { text: 'hello你好' },
166
+ { text: '再见€' }
167
+ ]
168
+ pdf.formatted_text_box(formatted_text, fallback_fonts: ['Helvetica'])
169
+
170
+ text = PDF::Inspector::Text.analyze(pdf.render)
171
+
172
+ fonts_used = text.font_settings.map { |e| e[:name] }
173
+ expect(fonts_used.length).to eq(4)
174
+ expect(fonts_used[0].to_s).to match(/GBZenKai-Medium/)
175
+ expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
176
+ expect(fonts_used[2].to_s).to match(/GBZenKai-Medium/)
177
+ expect(fonts_used[3]).to eq(:Helvetica)
178
+
179
+ expect(text.strings[0]).to eq('hello')
180
+ expect(text.strings[1]).to eq('你好')
181
+ expect(text.strings[2]).to eq('再见')
182
+ expect(text.strings[3]).to eq('€')
183
+ end
184
+ end
185
+
186
+ describe 'Text::Formatted::Box with :fallback_fonts option and fragment ' \
187
+ 'level font' do
188
+ it 'uses the fragment level font except for glyphs not in that font' do
189
+ file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
190
+ pdf.font_families['Kai'] = {
191
+ normal: { file: file, font: 'Kai' }
192
+ }
193
+
194
+ file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
195
+ pdf.font_families['DejaVu Sans'] = {
196
+ normal: { file: file }
197
+ }
198
+
199
+ formatted_text = [
200
+ { text: 'hello你好' },
201
+ { text: '再见goodbye', font: 'Times-Roman' }
202
+ ]
203
+ pdf.formatted_text_box(formatted_text, fallback_fonts: ['Kai'])
204
+
205
+ text = PDF::Inspector::Text.analyze(pdf.render)
206
+
207
+ fonts_used = text.font_settings.map { |e| e[:name] }
208
+ expect(fonts_used.length).to eq(4)
209
+ expect(fonts_used[0]).to eq(:Helvetica)
210
+ expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
211
+ expect(fonts_used[2].to_s).to match(/GBZenKai-Medium/)
212
+ expect(fonts_used[3]).to eq(:"Times-Roman")
213
+
214
+ expect(text.strings[0]).to eq('hello')
215
+ expect(text.strings[1]).to eq('你好')
216
+ expect(text.strings[2]).to eq('再见')
217
+ expect(text.strings[3]).to eq('goodbye')
218
+ end
219
+ end
220
+
221
+ describe 'Text::Formatted::Box' do
222
+ let(:formatted_text) { [{ text: 'hello你好' }] }
223
+
224
+ before do
225
+ file = "#{Prawn::DATADIR}/fonts/gkai00mp.ttf"
226
+ pdf.font_families['Kai'] = {
227
+ normal: { file: file, font: 'Kai' }
228
+ }
229
+
230
+ file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf"
231
+ pdf.font_families['DejaVu Sans'] = {
232
+ normal: { file: file }
233
+ }
234
+
235
+ pdf.fallback_fonts(['Kai'])
236
+ pdf.fallback_fonts = ['Kai']
237
+ end
238
+
239
+ it '#fallback_fonts should return the document-wide fallback fonts' do
240
+ expect(pdf.fallback_fonts).to eq(['Kai'])
241
+ end
242
+
243
+ it 'is able to set text fallback_fonts document-wide' do
244
+ pdf.formatted_text_box(formatted_text)
245
+
246
+ text = PDF::Inspector::Text.analyze(pdf.render)
247
+
248
+ fonts_used = text.font_settings.map { |e| e[:name] }
249
+ expect(fonts_used.length).to eq(2)
250
+ expect(fonts_used[0]).to eq(:Helvetica)
251
+ expect(fonts_used[1].to_s).to match(/GBZenKai-Medium/)
252
+ end
253
+
254
+ it 'is able to override document-wide fallback_fonts' do
255
+ pdf.fallback_fonts = ['DejaVu Sans']
256
+ pdf.formatted_text_box(formatted_text, fallback_fonts: ['Kai'])
257
+
258
+ text = PDF::Inspector::Text.analyze(pdf.render)
259
+
260
+ fonts_used = text.font_settings.map { |e| e[:name] }
261
+ expect(fonts_used.length).to eq(2)
262
+ expect(fonts_used[0]).to eq(:Helvetica)
263
+ expect(fonts_used[1]).to match(/Kai/)
264
+ end
265
+
266
+ it 'omits the fallback fonts overhead when passing an empty array ' \
267
+ 'as the :fallback_fonts' do
268
+ pdf.font('Kai')
269
+
270
+ box = described_class.new(
271
+ formatted_text,
272
+ document: pdf,
273
+ fallback_fonts: []
274
+ )
275
+
276
+ allow(box).to receive(:process_fallback_fonts)
277
+ box.render
278
+ expect(box).to_not have_received(:process_fallback_fonts)
279
+ end
280
+
281
+ it 'is able to clear document-wide fallback_fonts' do
282
+ pdf.fallback_fonts([])
283
+ box = described_class.new(formatted_text, document: pdf)
284
+
285
+ pdf.font('Kai')
286
+
287
+ allow(box).to receive(:process_fallback_fonts)
288
+ box.render
289
+ expect(box).to_not have_received(:process_fallback_fonts)
290
+ end
291
+ end
292
+
293
+ describe 'Text::Formatted::Box with :fallback_fonts option ' \
294
+ 'with glyphs not in the primary or the fallback fonts' do
295
+ it 'raises an exception' do
296
+ formatted_text = [{ text: 'hello world. 世界你好。' }]
297
+
298
+ expect do
299
+ pdf.formatted_text_box(formatted_text, fallback_fonts: ['Courier'])
300
+ end.to raise_error(Prawn::Errors::IncompatibleStringEncoding)
301
+ end
302
+ end
303
+
304
+ describe 'Text::Formatted::Box#extensions' do
305
+ let(:formatted_wrap_override) do
306
+ Module.new do
307
+ # rubocop: disable RSpec/InstanceVariable
308
+ def wrap(_array)
309
+ initialize_wrap([{ text: 'all your base are belong to us' }])
310
+ @line_wrap.wrap_line(
311
+ document: @document,
312
+ kerning: @kerning,
313
+ width: 10_000,
314
+ arranger: @arranger
315
+ )
316
+ fragment = @arranger.retrieve_fragment
317
+ format_and_draw_fragment(fragment, 0, @line_wrap.width, 0)
318
+
319
+ []
320
+ end
321
+ # rubocop: enable RSpec/InstanceVariable
322
+ end
323
+ end
324
+
325
+ it 'is able to override default line wrapping' do
326
+ described_class.extensions << formatted_wrap_override
327
+ pdf.formatted_text_box([{ text: 'hello world' }], {})
328
+ described_class.extensions.delete(formatted_wrap_override)
329
+ text = PDF::Inspector::Text.analyze(pdf.render)
330
+ expect(text.strings[0]).to eq('all your base are belong to us')
331
+ end
332
+
333
+ it 'overrides Text::Formatted::Box line wrapping does not affect ' \
334
+ 'Text::Box wrapping' do
335
+ described_class.extensions << formatted_wrap_override
336
+ pdf.text_box('hello world', {})
337
+ described_class.extensions.delete(formatted_wrap_override)
338
+ text = PDF::Inspector::Text.analyze(pdf.render)
339
+ expect(text.strings[0]).to eq('hello world')
340
+ end
341
+
342
+ it "overring Text::Box line wrapping doesn't override Text::Box wrapping" do
343
+ Prawn::Text::Box.extensions << formatted_wrap_override
344
+ pdf.text_box('hello world', {})
345
+ Prawn::Text::Box.extensions.delete(formatted_wrap_override)
346
+ text = PDF::Inspector::Text.analyze(pdf.render)
347
+ expect(text.strings[0]).to eq('all your base are belong to us')
348
+ end
349
+ end
350
+
351
+ describe 'Text::Formatted::Box#render' do
352
+ let(:fragment_callback_class) do
353
+ Class.new do
354
+ def initialize(_string, _number, _options); end
355
+
356
+ def render_behind(fragment); end
357
+
358
+ def render_in_front(fragment); end
359
+ end
360
+ end
361
+
362
+ it 'handles newlines' do
363
+ array = [{ text: "hello\nworld" }]
364
+ options = { document: pdf }
365
+ text_box = described_class.new(array, options)
366
+ text_box.render
367
+ expect(text_box.text).to eq("hello\nworld")
368
+ end
369
+
370
+ it 'omits spaces from the beginning of the line' do
371
+ array = [{ text: " hello\n world" }]
372
+ options = { document: pdf }
373
+ text_box = described_class.new(array, options)
374
+ text_box.render
375
+ expect(text_box.text).to eq("hello\nworld")
376
+ end
377
+
378
+ it 'is okay printing a line of whitespace' do
379
+ array = [{ text: "hello\n \nworld" }]
380
+ options = { document: pdf }
381
+ text_box = described_class.new(array, options)
382
+ text_box.render
383
+ expect(text_box.text).to eq("hello\n\nworld")
384
+
385
+ array = [
386
+ { text: "hello#{' ' * 500}" },
387
+ { text: ' ' * 500 },
388
+ { text: "#{' ' * 500}\n" },
389
+ { text: 'world' }
390
+ ]
391
+ options = { document: pdf }
392
+ text_box = described_class.new(array, options)
393
+ text_box.render
394
+ expect(text_box.text).to eq("hello\n\nworld")
395
+ end
396
+
397
+ it 'enables fragment level direction setting' do
398
+ number_of_hellos = 18
399
+ array = [
400
+ { text: 'hello ' * number_of_hellos },
401
+ { text: 'world', direction: :ltr },
402
+ { text: ', how are you?' }
403
+ ]
404
+ options = { document: pdf, direction: :rtl }
405
+ text_box = described_class.new(array, options)
406
+ text_box.render
407
+ text = PDF::Inspector::Text.analyze(pdf.render)
408
+ expect(text.strings[0]).to eq('era woh ,')
409
+ expect(text.strings[1]).to eq('world')
410
+ expect(text.strings[2]).to eq(' olleh' * number_of_hellos)
411
+ expect(text.strings[3]).to eq('?uoy')
412
+ end
413
+
414
+ it 'is able to perform fragment callbacks' do
415
+ callback_object =
416
+ fragment_callback_class.new('something', 7, document: pdf)
417
+ allow(callback_object).to receive(:render_behind)
418
+ allow(callback_object).to receive(:render_in_front)
419
+ array = [
420
+ { text: 'hello world ' },
421
+ { text: 'callback now', callback: callback_object }
422
+ ]
423
+ text_box = described_class.new(array, document: pdf)
424
+ text_box.render
425
+
426
+ expect(callback_object).to have_received(:render_behind).with(
427
+ kind_of(Prawn::Text::Formatted::Fragment)
428
+ )
429
+ expect(callback_object).to have_received(:render_in_front).with(
430
+ kind_of(Prawn::Text::Formatted::Fragment)
431
+ )
432
+ end
433
+
434
+ it 'is able to perform fragment callbacks on multiple objects' do
435
+ callback_object =
436
+ fragment_callback_class.new('something', 7, document: pdf)
437
+ allow(callback_object).to receive(:render_behind)
438
+ allow(callback_object).to receive(:render_in_front)
439
+
440
+ callback_object2 = fragment_callback_class.new(
441
+ 'something else', 14, document: pdf
442
+ )
443
+ allow(callback_object2).to receive(:render_behind)
444
+ allow(callback_object2).to receive(:render_in_front)
445
+
446
+ array = [
447
+ { text: 'hello world ' },
448
+ { text: 'callback now', callback: [callback_object, callback_object2] }
449
+ ]
450
+ text_box = described_class.new(array, document: pdf)
451
+ text_box.render
452
+
453
+ expect(callback_object).to have_received(:render_behind).with(
454
+ kind_of(Prawn::Text::Formatted::Fragment)
455
+ )
456
+ expect(callback_object).to have_received(:render_in_front).with(
457
+ kind_of(Prawn::Text::Formatted::Fragment)
458
+ )
459
+ expect(callback_object2).to have_received(:render_behind).with(
460
+ kind_of(Prawn::Text::Formatted::Fragment)
461
+ )
462
+ expect(callback_object2).to have_received(:render_in_front).with(
463
+ kind_of(Prawn::Text::Formatted::Fragment)
464
+ )
465
+ end
466
+
467
+ it 'fragment callbacks is able to define only the callback they need' do
468
+ behind = (
469
+ Class.new do
470
+ def initialize(_string, _number, _options); end
471
+
472
+ def render_behind(fragment); end
473
+ end
474
+ ).new(
475
+ 'something',
476
+ 7,
477
+ document: pdf
478
+ )
479
+ in_front = (
480
+ Class.new do
481
+ def initialize(_string, _number, _options); end
482
+
483
+ def render_in_front(fragment); end
484
+ end
485
+ ).new(
486
+ 'something',
487
+ 7,
488
+ document: pdf
489
+ )
490
+ array = [
491
+ { text: 'hello world ' },
492
+ { text: 'callback now', callback: [behind, in_front] }
493
+ ]
494
+ text_box = described_class.new(array, document: pdf)
495
+
496
+ text_box.render # trigger callbacks
497
+ end
498
+
499
+ it 'is able to set the font' do
500
+ array = [
501
+ { text: 'this contains ' },
502
+ {
503
+ text: 'Times-Bold',
504
+ styles: [:bold],
505
+ font: 'Times-Roman'
506
+ },
507
+ { text: ' text' }
508
+ ]
509
+ text_box = described_class.new(array, document: pdf)
510
+ text_box.render
511
+ contents = PDF::Inspector::Text.analyze(pdf.render)
512
+ fonts = contents.font_settings.map { |e| e[:name] }
513
+ expect(fonts).to eq(%i[Helvetica Times-Bold Helvetica])
514
+ expect(contents.strings[0]).to eq('this contains ')
515
+ expect(contents.strings[1]).to eq('Times-Bold')
516
+ expect(contents.strings[2]).to eq(' text')
517
+ end
518
+
519
+ it 'is able to set bold' do
520
+ array = [
521
+ { text: 'this contains ' },
522
+ { text: 'bold', styles: [:bold] },
523
+ { text: ' text' }
524
+ ]
525
+ text_box = described_class.new(array, document: pdf)
526
+ text_box.render
527
+ contents = PDF::Inspector::Text.analyze(pdf.render)
528
+ fonts = contents.font_settings.map { |e| e[:name] }
529
+ expect(fonts).to eq(%i[Helvetica Helvetica-Bold Helvetica])
530
+ expect(contents.strings[0]).to eq('this contains ')
531
+ expect(contents.strings[1]).to eq('bold')
532
+ expect(contents.strings[2]).to eq(' text')
533
+ end
534
+
535
+ it 'is able to set italics' do
536
+ array = [
537
+ { text: 'this contains ' },
538
+ { text: 'italic', styles: [:italic] },
539
+ { text: ' text' }
540
+ ]
541
+ text_box = described_class.new(array, document: pdf)
542
+ text_box.render
543
+ contents = PDF::Inspector::Text.analyze(pdf.render)
544
+ fonts = contents.font_settings.map { |e| e[:name] }
545
+ expect(fonts).to eq(%i[Helvetica Helvetica-Oblique Helvetica])
546
+ end
547
+
548
+ it 'is able to set subscript' do
549
+ array = [
550
+ { text: 'this contains ' },
551
+ { text: 'subscript', size: 18, styles: [:subscript] },
552
+ { text: ' text' }
553
+ ]
554
+ text_box = described_class.new(array, document: pdf)
555
+ text_box.render
556
+ contents = PDF::Inspector::Text.analyze(pdf.render)
557
+ expect(contents.font_settings[0][:size]).to eq(12)
558
+ expect(contents.font_settings[1][:size])
559
+ .to be_within(0.0001).of(18 * 0.583)
560
+ end
561
+
562
+ it 'is able to set superscript' do
563
+ array = [
564
+ { text: 'this contains ' },
565
+ { text: 'superscript', size: 18, styles: [:superscript] },
566
+ { text: ' text' }
567
+ ]
568
+ text_box = described_class.new(array, document: pdf)
569
+ text_box.render
570
+ contents = PDF::Inspector::Text.analyze(pdf.render)
571
+ expect(contents.font_settings[0][:size]).to eq(12)
572
+ expect(contents.font_settings[1][:size])
573
+ .to be_within(0.0001).of(18 * 0.583)
574
+ end
575
+
576
+ it 'is able to set compound bold and italic text' do
577
+ array = [
578
+ { text: 'this contains ' },
579
+ { text: 'bold italic', styles: %i[bold italic] },
580
+ { text: ' text' }
581
+ ]
582
+ text_box = described_class.new(array, document: pdf)
583
+ text_box.render
584
+ contents = PDF::Inspector::Text.analyze(pdf.render)
585
+ fonts = contents.font_settings.map { |e| e[:name] }
586
+ expect(fonts).to eq(%i[Helvetica Helvetica-BoldOblique Helvetica])
587
+ end
588
+
589
+ it 'is able to underline' do
590
+ array = [
591
+ { text: 'this contains ' },
592
+ { text: 'underlined', styles: [:underline] },
593
+ { text: ' text' }
594
+ ]
595
+ text_box = described_class.new(array, document: pdf)
596
+ text_box.render
597
+ line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
598
+ expect(line_drawing.points.length).to eq(2)
599
+ end
600
+
601
+ it 'is able to strikethrough' do
602
+ array = [
603
+ { text: 'this contains ' },
604
+ { text: 'struckthrough', styles: [:strikethrough] },
605
+ { text: ' text' }
606
+ ]
607
+ text_box = described_class.new(array, document: pdf)
608
+ text_box.render
609
+ line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
610
+ expect(line_drawing.points.length).to eq(2)
611
+ end
612
+
613
+ it 'is able to add URL links' do
614
+ allow(pdf).to receive(:link_annotation)
615
+ array = [
616
+ { text: 'click ' },
617
+ { text: 'here', link: 'http://example.com' },
618
+ { text: ' to visit' }
619
+ ]
620
+ text_box = described_class.new(array, document: pdf)
621
+ text_box.render
622
+
623
+ expect(pdf).to have_received(:link_annotation).with(
624
+ kind_of(Array),
625
+ Border: [0, 0, 0],
626
+ A: {
627
+ Type: :Action,
628
+ S: :URI,
629
+ URI: 'http://example.com'
630
+ }
631
+ )
632
+ end
633
+
634
+ it 'is able to add destination links' do
635
+ allow(pdf).to receive(:link_annotation)
636
+ array = [
637
+ { text: 'Go to the ' },
638
+ { text: 'Table of Contents', anchor: 'ToC' }
639
+ ]
640
+ text_box = described_class.new(array, document: pdf)
641
+ text_box.render
642
+
643
+ expect(pdf).to have_received(:link_annotation).with(
644
+ kind_of(Array),
645
+ Border: [0, 0, 0],
646
+ Dest: 'ToC'
647
+ )
648
+ end
649
+
650
+ it 'is able to add local actions' do
651
+ allow(pdf).to receive(:link_annotation)
652
+ array = [
653
+ { text: 'click ' },
654
+ { text: 'here', local: '../example.pdf' },
655
+ { text: ' to open a local file' }
656
+ ]
657
+ text_box = described_class.new(array, document: pdf)
658
+ text_box.render
659
+
660
+ expect(pdf).to have_received(:link_annotation).with(
661
+ kind_of(Array),
662
+ Border: [0, 0, 0],
663
+ A: {
664
+ Type: :Action,
665
+ S: :Launch,
666
+ F: '../example.pdf',
667
+ NewWindow: true
668
+ }
669
+ )
670
+ end
671
+
672
+ it 'is able to set font size' do
673
+ array = [
674
+ { text: 'this contains ' },
675
+ { text: 'sized', size: 24 },
676
+ { text: ' text' }
677
+ ]
678
+ text_box = described_class.new(array, document: pdf)
679
+ text_box.render
680
+ contents = PDF::Inspector::Text.analyze(pdf.render)
681
+ expect(contents.font_settings[0][:size]).to eq(12)
682
+ expect(contents.font_settings[1][:size]).to eq(24)
683
+ end
684
+
685
+ it 'sets the baseline based on the tallest fragment on a given line' do
686
+ array = [
687
+ { text: 'this contains ' },
688
+ { text: 'sized', size: 24 },
689
+ { text: ' text' }
690
+ ]
691
+ text_box = described_class.new(array, document: pdf)
692
+ text_box.render
693
+ pdf.font_size(24) do
694
+ expect(text_box.height).to be_within(0.001)
695
+ .of(pdf.font.ascender + pdf.font.descender)
696
+ end
697
+ end
698
+
699
+ it 'is able to set color via an rgb hex string' do
700
+ array = [{
701
+ text: 'rgb',
702
+ color: 'ff0000'
703
+ }]
704
+ text_box = described_class.new(array, document: pdf)
705
+ text_box.render
706
+ colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
707
+ expect(colors.fill_color_count).to eq(2)
708
+ expect(colors.stroke_color_count).to eq(2)
709
+ end
710
+
711
+ it 'is able to set color using a cmyk array' do
712
+ array = [{
713
+ text: 'cmyk',
714
+ color: [100, 0, 0, 0]
715
+ }]
716
+ text_box = described_class.new(array, document: pdf)
717
+ text_box.render
718
+ colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
719
+ expect(colors.fill_color_count).to eq(2)
720
+ expect(colors.stroke_color_count).to eq(2)
721
+ end
722
+ end
723
+
724
+ describe 'Text::Formatted::Box#render(:dry_run => true)' do
725
+ it 'does not change the graphics state of the document' do
726
+ state_before = PDF::Inspector::Graphics::Color.analyze(pdf.render)
727
+ fill_color_count = state_before.fill_color_count
728
+ stroke_color_count = state_before.stroke_color_count
729
+ stroke_color_space_count = state_before.stroke_color_space_count
730
+
731
+ array = [{
732
+ text: 'Foo',
733
+ color: [0, 0, 0, 100]
734
+ }]
735
+ options = { document: pdf }
736
+ text_box = described_class.new(array, options)
737
+ text_box.render(dry_run: true)
738
+
739
+ state_after = PDF::Inspector::Graphics::Color.analyze(pdf.render)
740
+ expect(state_after.fill_color_count).to eq(fill_color_count)
741
+ expect(state_after.stroke_color_count).to eq(stroke_color_count)
742
+ expect(state_after.stroke_color_space_count)
743
+ .to eq(stroke_color_space_count)
744
+ end
745
+ end
746
+
747
+ describe 'Text::Formatted::Box#render with fragment level '\
748
+ ':character_spacing option' do
749
+ it 'draws the character spacing to the document' do
750
+ array = [{
751
+ text: 'hello world',
752
+ character_spacing: 7
753
+ }]
754
+ options = { document: pdf }
755
+ text_box = described_class.new(array, options)
756
+ text_box.render
757
+ contents = PDF::Inspector::Text.analyze(pdf.render)
758
+ expect(contents.character_spacing[0]).to eq(7)
759
+ end
760
+
761
+ it 'lays out text properly' do
762
+ array = [{
763
+ text: 'hello world',
764
+ font: 'Courier',
765
+ character_spacing: 10
766
+ }]
767
+ options = {
768
+ document: pdf,
769
+ width: 100,
770
+ overflow: :expand
771
+ }
772
+ text_box = described_class.new(array, options)
773
+ text_box.render
774
+ expect(text_box.text).to eq("hello\nworld")
775
+ end
776
+ end
777
+
778
+ describe 'Text::Formatted::Box#render with :align => :justify' do
779
+ it 'does not justify the last line of a paragraph' do
780
+ array = [
781
+ { text: 'hello world ' },
782
+ { text: "\n" },
783
+ { text: 'goodbye' }
784
+ ]
785
+ options = { document: pdf, align: :justify }
786
+ text_box = described_class.new(array, options)
787
+ text_box.render
788
+ contents = PDF::Inspector::Text.analyze(pdf.render)
789
+ expect(contents.word_spacing).to be_empty
790
+ end
791
+
792
+ it 'raise an exception when align value is not a symbol' do
793
+ array = [
794
+ { text: 'hello world ' },
795
+ { text: "\n" },
796
+ { text: 'goodbye' }
797
+ ]
798
+ options = { document: pdf, align: 'justify' }
799
+ text_box = described_class.new(array, options)
800
+ expect { text_box.render }.to raise_error(
801
+ ArgumentError,
802
+ 'align must be one of :left, :right, :center or :justify symbols'
803
+ )
804
+ end
805
+ end
806
+
807
+ describe 'Text::Formatted::Box#render with :valign => :center' do
808
+ it 'has a bottom gap equal to baseline and bottom of box' do
809
+ box_height = 100
810
+ y = 450
811
+ array = [{ text: 'Vertical Align' }]
812
+ options = {
813
+ document: pdf,
814
+ valign: :center,
815
+ at: [0, y],
816
+ width: 100,
817
+ height: box_height,
818
+ size: 16
819
+ }
820
+ text_box = described_class.new(array, options)
821
+ text_box.render
822
+ line_padding = (box_height - text_box.height + text_box.descender) * 0.5
823
+ baseline = y - line_padding
824
+
825
+ expect(text_box.at[1]).to be_within(0.01).of(baseline)
826
+ end
827
+ end
828
+
829
+ describe 'Text::Formatted::Box#render with :valign => :bottom' do
830
+ it 'does not render a gap between the text and bottom of box' do
831
+ box_height = 100
832
+ y = 450
833
+ array = [{ text: 'Vertical Align' }]
834
+ options = {
835
+ document: pdf,
836
+ valign: :bottom,
837
+ at: [0, y],
838
+ width: 100,
839
+ height: box_height,
840
+ size: 16
841
+ }
842
+ text_box = described_class.new(array, options)
843
+ text_box.render
844
+ top_padding = y - (box_height - text_box.height)
845
+
846
+ expect(text_box.at[1]).to be_within(0.01).of(top_padding)
847
+ end
848
+ end
849
+ end