prawn 2.0.2 → 2.3.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 (277) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -9
  6. data/Rakefile +20 -23
  7. data/lib/prawn.rb +37 -49
  8. data/lib/prawn/document.rb +181 -133
  9. data/lib/prawn/document/bounding_box.rb +41 -29
  10. data/lib/prawn/document/column_box.rb +7 -7
  11. data/lib/prawn/document/internals.rb +18 -8
  12. data/lib/prawn/document/span.rb +21 -16
  13. data/lib/prawn/encoding.rb +69 -68
  14. data/lib/prawn/errors.rb +12 -7
  15. data/lib/prawn/font.rb +115 -69
  16. data/lib/prawn/font_metric_cache.rb +14 -8
  17. data/lib/prawn/{font → fonts}/afm.rb +102 -68
  18. data/lib/prawn/{font → fonts}/dfont.rb +5 -11
  19. data/lib/prawn/fonts/otf.rb +11 -0
  20. data/lib/prawn/fonts/ttc.rb +36 -0
  21. data/lib/prawn/{font → fonts}/ttf.rb +87 -68
  22. data/lib/prawn/graphics.rb +120 -80
  23. data/lib/prawn/graphics/blend_mode.rb +65 -0
  24. data/lib/prawn/graphics/cap_style.rb +3 -3
  25. data/lib/prawn/graphics/color.rb +27 -25
  26. data/lib/prawn/graphics/dash.rb +23 -11
  27. data/lib/prawn/graphics/join_style.rb +9 -3
  28. data/lib/prawn/graphics/patterns.rb +197 -67
  29. data/lib/prawn/graphics/transformation.rb +17 -8
  30. data/lib/prawn/graphics/transparency.rb +17 -13
  31. data/lib/prawn/grid.rb +48 -47
  32. data/lib/prawn/image_handler.rb +5 -5
  33. data/lib/prawn/images.rb +39 -30
  34. data/lib/prawn/images/image.rb +2 -1
  35. data/lib/prawn/images/jpg.rb +28 -22
  36. data/lib/prawn/images/png.rb +107 -66
  37. data/lib/prawn/measurement_extensions.rb +10 -9
  38. data/lib/prawn/measurements.rb +19 -15
  39. data/lib/prawn/outline.rb +97 -77
  40. data/lib/prawn/repeater.rb +14 -10
  41. data/lib/prawn/security.rb +81 -61
  42. data/lib/prawn/security/arcfour.rb +2 -2
  43. data/lib/prawn/soft_mask.rb +26 -26
  44. data/lib/prawn/stamp.rb +20 -13
  45. data/lib/prawn/text.rb +68 -52
  46. data/lib/prawn/text/box.rb +11 -8
  47. data/lib/prawn/text/formatted.rb +5 -5
  48. data/lib/prawn/text/formatted/arranger.rb +78 -49
  49. data/lib/prawn/text/formatted/box.rb +134 -100
  50. data/lib/prawn/text/formatted/fragment.rb +11 -14
  51. data/lib/prawn/text/formatted/line_wrap.rb +121 -63
  52. data/lib/prawn/text/formatted/parser.rb +139 -117
  53. data/lib/prawn/text/formatted/wrap.rb +43 -31
  54. data/lib/prawn/transformation_stack.rb +44 -0
  55. data/lib/prawn/utilities.rb +7 -22
  56. data/lib/prawn/version.rb +2 -2
  57. data/lib/prawn/view.rb +17 -7
  58. data/manual/basic_concepts/adding_pages.rb +6 -7
  59. data/manual/basic_concepts/basic_concepts.rb +31 -22
  60. data/manual/basic_concepts/creation.rb +10 -11
  61. data/manual/basic_concepts/cursor.rb +4 -5
  62. data/manual/basic_concepts/measurement.rb +6 -7
  63. data/manual/basic_concepts/origin.rb +5 -6
  64. data/manual/basic_concepts/other_cursor_helpers.rb +11 -12
  65. data/manual/basic_concepts/view.rb +22 -16
  66. data/manual/bounding_box/bounding_box.rb +29 -24
  67. data/manual/bounding_box/bounds.rb +11 -12
  68. data/manual/bounding_box/canvas.rb +4 -5
  69. data/manual/bounding_box/creation.rb +6 -7
  70. data/manual/bounding_box/indentation.rb +14 -15
  71. data/manual/bounding_box/nesting.rb +24 -17
  72. data/manual/bounding_box/russian_boxes.rb +14 -13
  73. data/manual/bounding_box/stretchy.rb +12 -13
  74. data/manual/contents.rb +28 -22
  75. data/manual/cover.rb +33 -28
  76. data/manual/document_and_page_options/background.rb +11 -13
  77. data/manual/document_and_page_options/document_and_page_options.rb +25 -20
  78. data/manual/document_and_page_options/metadata.rb +18 -16
  79. data/manual/document_and_page_options/page_margins.rb +18 -20
  80. data/manual/document_and_page_options/page_size.rb +13 -12
  81. data/manual/document_and_page_options/print_scaling.rb +17 -15
  82. data/manual/example_helper.rb +5 -4
  83. data/manual/graphics/blend_mode.rb +52 -0
  84. data/manual/graphics/circle_and_ellipse.rb +4 -5
  85. data/manual/graphics/color.rb +7 -9
  86. data/manual/graphics/common_lines.rb +7 -8
  87. data/manual/graphics/fill_and_stroke.rb +4 -5
  88. data/manual/graphics/fill_rules.rb +9 -10
  89. data/manual/graphics/gradients.rb +27 -21
  90. data/manual/graphics/graphics.rb +48 -39
  91. data/manual/graphics/helper.rb +12 -9
  92. data/manual/graphics/line_width.rb +8 -7
  93. data/manual/graphics/lines_and_curves.rb +7 -8
  94. data/manual/graphics/polygon.rb +6 -8
  95. data/manual/graphics/rectangle.rb +4 -5
  96. data/manual/graphics/rotate.rb +6 -7
  97. data/manual/graphics/scale.rb +14 -15
  98. data/manual/graphics/soft_masks.rb +4 -5
  99. data/manual/graphics/stroke_cap.rb +6 -7
  100. data/manual/graphics/stroke_dash.rb +11 -12
  101. data/manual/graphics/stroke_join.rb +5 -6
  102. data/manual/graphics/translate.rb +9 -10
  103. data/manual/graphics/transparency.rb +7 -8
  104. data/manual/how_to_read_this_manual.rb +6 -6
  105. data/manual/images/absolute_position.rb +6 -7
  106. data/manual/images/fit.rb +7 -8
  107. data/manual/images/horizontal.rb +9 -10
  108. data/manual/images/images.rb +28 -24
  109. data/manual/images/plain_image.rb +5 -6
  110. data/manual/images/scale.rb +9 -10
  111. data/manual/images/vertical.rb +13 -14
  112. data/manual/images/width_and_height.rb +10 -11
  113. data/manual/layout/boxes.rb +5 -6
  114. data/manual/layout/content.rb +7 -8
  115. data/manual/layout/layout.rb +18 -16
  116. data/manual/layout/simple_grid.rb +6 -7
  117. data/manual/outline/add_subsection_to.rb +20 -21
  118. data/manual/outline/insert_section_after.rb +15 -16
  119. data/manual/outline/outline.rb +21 -17
  120. data/manual/outline/sections_and_pages.rb +17 -18
  121. data/manual/repeatable_content/alternate_page_numbering.rb +21 -17
  122. data/manual/repeatable_content/page_numbering.rb +17 -16
  123. data/manual/repeatable_content/repeatable_content.rb +25 -19
  124. data/manual/repeatable_content/repeater.rb +14 -15
  125. data/manual/repeatable_content/stamp.rb +14 -15
  126. data/manual/security/encryption.rb +9 -10
  127. data/manual/security/permissions.rb +19 -14
  128. data/manual/security/security.rb +19 -16
  129. data/manual/table.rb +3 -3
  130. data/manual/text/alignment.rb +16 -17
  131. data/manual/text/color.rb +12 -11
  132. data/manual/text/column_box.rb +9 -10
  133. data/manual/text/fallback_fonts.rb +25 -21
  134. data/manual/text/font.rb +11 -12
  135. data/manual/text/font_size.rb +13 -14
  136. data/manual/text/font_style.rb +7 -8
  137. data/manual/text/formatted_callbacks.rb +25 -21
  138. data/manual/text/formatted_text.rb +33 -25
  139. data/manual/text/free_flowing_text.rb +20 -21
  140. data/manual/text/inline.rb +18 -19
  141. data/manual/text/kerning_and_character_spacing.rb +14 -15
  142. data/manual/text/leading.rb +7 -8
  143. data/manual/text/line_wrapping.rb +37 -18
  144. data/manual/text/paragraph_indentation.rb +13 -14
  145. data/manual/text/positioned_text.rb +15 -16
  146. data/manual/text/registering_families.rb +20 -21
  147. data/manual/text/rendering_and_color.rb +9 -10
  148. data/manual/text/right_to_left_text.rb +26 -19
  149. data/manual/text/rotation.rb +28 -23
  150. data/manual/text/single_usage.rb +8 -9
  151. data/manual/text/text.rb +57 -52
  152. data/manual/text/text_box_excess.rb +20 -17
  153. data/manual/text/text_box_extensions.rb +18 -15
  154. data/manual/text/text_box_overflow.rb +18 -19
  155. data/manual/text/utf8.rb +11 -12
  156. data/manual/text/win_ansi_charset.rb +21 -19
  157. data/prawn.gemspec +45 -33
  158. data/spec/extensions/encoding_helpers.rb +3 -3
  159. data/spec/prawn/document/bounding_box_spec.rb +546 -0
  160. data/spec/prawn/document/column_box_spec.rb +75 -0
  161. data/spec/prawn/document/security_spec.rb +176 -0
  162. data/spec/prawn/document_annotations_spec.rb +76 -0
  163. data/spec/prawn/document_destinations_spec.rb +15 -0
  164. data/spec/prawn/document_grid_spec.rb +99 -0
  165. data/spec/prawn/document_reference_spec.rb +27 -0
  166. data/spec/prawn/document_span_spec.rb +36 -0
  167. data/spec/prawn/document_spec.rb +802 -0
  168. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  169. data/spec/prawn/font_spec.rb +542 -0
  170. data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
  171. data/spec/prawn/graphics/transparency_spec.rb +81 -0
  172. data/spec/prawn/graphics_spec.rb +837 -0
  173. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  174. data/spec/prawn/image_handler_spec.rb +53 -0
  175. data/spec/prawn/images/jpg_spec.rb +20 -0
  176. data/spec/prawn/images/png_spec.rb +283 -0
  177. data/spec/prawn/images_spec.rb +224 -0
  178. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  179. data/spec/prawn/outline_spec.rb +412 -0
  180. data/spec/prawn/repeater_spec.rb +165 -0
  181. data/spec/prawn/soft_mask_spec.rb +74 -0
  182. data/spec/prawn/stamp_spec.rb +172 -0
  183. data/spec/prawn/text/box_spec.rb +1112 -0
  184. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  185. data/spec/prawn/text/formatted/box_spec.rb +846 -0
  186. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  187. data/spec/prawn/text/formatted/line_wrap_spec.rb +494 -0
  188. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  189. data/spec/prawn/text_draw_text_spec.rb +149 -0
  190. data/spec/prawn/text_rendering_mode_spec.rb +48 -0
  191. data/spec/prawn/text_spacing_spec.rb +95 -0
  192. data/spec/prawn/text_spec.rb +603 -0
  193. data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
  194. data/spec/prawn/transformation_stack_spec.rb +66 -0
  195. data/spec/prawn/view_spec.rb +63 -0
  196. data/spec/prawn_manual_spec.rb +35 -0
  197. data/spec/spec_helper.rb +19 -23
  198. metadata +145 -185
  199. metadata.gz.sig +4 -0
  200. data/data/images/16bit.alpha +0 -0
  201. data/data/images/16bit.color +0 -0
  202. data/data/images/16bit.png +0 -0
  203. data/data/images/arrow.png +0 -0
  204. data/data/images/arrow2.png +0 -0
  205. data/data/images/dice.alpha +0 -0
  206. data/data/images/dice.color +0 -0
  207. data/data/images/dice.png +0 -0
  208. data/data/images/dice_interlaced.png +0 -0
  209. data/data/images/fractal.jpg +0 -0
  210. data/data/images/indexed_color.dat +0 -0
  211. data/data/images/indexed_color.png +0 -0
  212. data/data/images/letterhead.jpg +0 -0
  213. data/data/images/license.md +0 -8
  214. data/data/images/page_white_text.alpha +0 -0
  215. data/data/images/page_white_text.color +0 -0
  216. data/data/images/page_white_text.png +0 -0
  217. data/data/images/pal_bk.png +0 -0
  218. data/data/images/pigs.jpg +0 -0
  219. data/data/images/prawn.png +0 -0
  220. data/data/images/ruport.png +0 -0
  221. data/data/images/ruport_data.dat +0 -0
  222. data/data/images/ruport_transparent.png +0 -0
  223. data/data/images/ruport_type0.png +0 -0
  224. data/data/images/stef.jpg +0 -0
  225. data/data/images/tru256.bmp +0 -0
  226. data/data/images/web-links.dat +0 -1
  227. data/data/images/web-links.png +0 -0
  228. data/data/pdfs/complex_template.pdf +0 -0
  229. data/data/pdfs/contains_ttf_font.pdf +0 -0
  230. data/data/pdfs/encrypted.pdf +0 -0
  231. data/data/pdfs/form.pdf +1 -819
  232. data/data/pdfs/hexagon.pdf +0 -61
  233. data/data/pdfs/indirect_reference.pdf +0 -86
  234. data/data/pdfs/multipage_template.pdf +0 -127
  235. data/data/pdfs/nested_pages.pdf +0 -118
  236. data/data/pdfs/page_without_mediabox.pdf +0 -193
  237. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  238. data/data/pdfs/two_hexagons.pdf +0 -90
  239. data/data/pdfs/version_1_6.pdf +0 -61
  240. data/data/shift_jis_text.txt +0 -1
  241. data/spec/acceptance/png.rb +0 -24
  242. data/spec/annotations_spec.rb +0 -67
  243. data/spec/bounding_box_spec.rb +0 -501
  244. data/spec/column_box_spec.rb +0 -59
  245. data/spec/destinations_spec.rb +0 -13
  246. data/spec/document_spec.rb +0 -742
  247. data/spec/extensions/mocha.rb +0 -45
  248. data/spec/font_metric_cache_spec.rb +0 -52
  249. data/spec/font_spec.rb +0 -475
  250. data/spec/formatted_text_arranger_spec.rb +0 -423
  251. data/spec/formatted_text_box_spec.rb +0 -716
  252. data/spec/formatted_text_fragment_spec.rb +0 -299
  253. data/spec/graphics_spec.rb +0 -666
  254. data/spec/grid_spec.rb +0 -95
  255. data/spec/image_handler_spec.rb +0 -53
  256. data/spec/images_spec.rb +0 -167
  257. data/spec/inline_formatted_text_parser_spec.rb +0 -568
  258. data/spec/jpg_spec.rb +0 -23
  259. data/spec/line_wrap_spec.rb +0 -366
  260. data/spec/measurement_units_spec.rb +0 -22
  261. data/spec/outline_spec.rb +0 -409
  262. data/spec/png_spec.rb +0 -235
  263. data/spec/reference_spec.rb +0 -25
  264. data/spec/repeater_spec.rb +0 -154
  265. data/spec/security_spec.rb +0 -151
  266. data/spec/soft_mask_spec.rb +0 -78
  267. data/spec/span_spec.rb +0 -43
  268. data/spec/stamp_spec.rb +0 -179
  269. data/spec/stroke_styles_spec.rb +0 -208
  270. data/spec/text_at_spec.rb +0 -142
  271. data/spec/text_box_spec.rb +0 -1038
  272. data/spec/text_rendering_mode_spec.rb +0 -45
  273. data/spec/text_spacing_spec.rb +0 -93
  274. data/spec/text_spec.rb +0 -549
  275. data/spec/text_with_inline_formatting_spec.rb +0 -35
  276. data/spec/transparency_spec.rb +0 -91
  277. data/spec/view_spec.rb +0 -42
@@ -0,0 +1,466 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Prawn::Text::Formatted::Arranger do
6
+ let(:pdf) { create_pdf }
7
+ let(:arranger) { described_class.new pdf }
8
+
9
+ describe '#format_array' do
10
+ it 'populates the unconsumed array' do
11
+ array = [
12
+ { text: 'hello ' },
13
+ { text: 'world how ', styles: [:bold] },
14
+ { text: 'are', styles: %i[bold italic] },
15
+ { text: ' you?' }
16
+ ]
17
+
18
+ arranger.format_array = array
19
+
20
+ expect(arranger.unconsumed[0]).to eq(text: 'hello ')
21
+ expect(arranger.unconsumed[1]).to eq(text: 'world how ', styles: [:bold])
22
+ expect(arranger.unconsumed[2])
23
+ .to eq(text: 'are', styles: %i[bold italic])
24
+ expect(arranger.unconsumed[3]).to eq(text: ' you?')
25
+ end
26
+
27
+ it 'splits newlins into their own elements' do
28
+ array = [
29
+ { text: "\nhello\nworld" }
30
+ ]
31
+
32
+ arranger.format_array = array
33
+
34
+ expect(arranger.unconsumed[0]).to eq(text: "\n")
35
+ expect(arranger.unconsumed[1]).to eq(text: 'hello')
36
+ expect(arranger.unconsumed[2]).to eq(text: "\n")
37
+ expect(arranger.unconsumed[3]).to eq(text: 'world')
38
+ end
39
+ end
40
+
41
+ describe '#preview_next_string' do
42
+ context 'with a formatted array' do
43
+ let(:array) { [{ text: 'hello' }] }
44
+
45
+ before do
46
+ arranger.format_array = array
47
+ end
48
+
49
+ it 'does not populate the consumed array' do
50
+ arranger.preview_next_string
51
+ expect(arranger.consumed).to eq([])
52
+ end
53
+
54
+ it 'returns the text of the next unconsumed hash' do
55
+ expect(arranger.preview_next_string).to eq('hello')
56
+ end
57
+
58
+ it 'returns nil if there is no more unconsumed text' do
59
+ arranger.next_string
60
+ expect(arranger.preview_next_string).to be_nil
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#next_string' do
66
+ let(:array) do
67
+ [
68
+ { text: 'hello ' },
69
+ { text: 'world how ', styles: [:bold] },
70
+ { text: 'are', styles: %i[bold italic] },
71
+ { text: ' you?' }
72
+ ]
73
+ end
74
+
75
+ before do
76
+ arranger.format_array = array
77
+ end
78
+
79
+ it 'raises RuntimeError if called after a line was finalized' do
80
+ arranger.finalize_line
81
+ expect { arranger.next_string }.to raise_error(RuntimeError)
82
+ end
83
+
84
+ it 'populates the conumed array' do
85
+ while arranger.next_string
86
+ end
87
+
88
+ expect(arranger.consumed[0]).to eq(text: 'hello ')
89
+ expect(arranger.consumed[1]).to eq(text: 'world how ', styles: [:bold])
90
+ expect(arranger.consumed[2]).to eq(text: 'are', styles: %i[bold italic])
91
+ expect(arranger.consumed[3]).to eq(text: ' you?')
92
+ end
93
+
94
+ it 'populates the current_format_state array' do
95
+ arranger.next_string
96
+ expect(arranger.current_format_state).to eq({})
97
+
98
+ arranger.next_string
99
+ expect(arranger.current_format_state).to eq(styles: [:bold])
100
+
101
+ arranger.next_string
102
+ expect(arranger.current_format_state).to eq(styles: %i[bold italic])
103
+
104
+ arranger.next_string
105
+ expect(arranger.current_format_state).to eq({})
106
+ end
107
+
108
+ it 'returns the text of the newly consumed hash' do
109
+ expect(arranger.next_string).to eq('hello ')
110
+ end
111
+
112
+ it 'returns nil when there are no more unconsumed hashes' do
113
+ 4.times do
114
+ arranger.next_string
115
+ end
116
+
117
+ expect(arranger.next_string).to be_nil
118
+ end
119
+ end
120
+
121
+ describe '#retrieve_fragment' do
122
+ context 'with a formatted array whos text is an empty string' do
123
+ let(:array) do
124
+ [
125
+ { text: "hello\nworld\n\n\nhow are you?" },
126
+ { text: "\n" },
127
+ { text: "\n" },
128
+ { text: "\n" },
129
+ { text: '' },
130
+ { text: 'fine, thanks.' },
131
+ { text: '' },
132
+ { text: "\n" },
133
+ { text: '' }
134
+ ]
135
+ end
136
+
137
+ before do
138
+ arranger.format_array = array
139
+
140
+ while arranger.next_string
141
+ end
142
+
143
+ arranger.finalize_line
144
+ end
145
+
146
+ # rubocop: disable Lint/AssignmentInCondition
147
+ it 'never returns a fragment whose text is an empty string' do
148
+ while fragment = arranger.retrieve_fragment
149
+ expect(fragment.text).to_not be_empty
150
+ end
151
+ end
152
+ # rubocop: enable Lint/AssignmentInCondition
153
+ end
154
+
155
+ context 'with formatted array' do
156
+ let(:array) do
157
+ [
158
+ { text: 'hello ' },
159
+ { text: 'world how ', styles: [:bold] },
160
+ { text: 'are', styles: %i[bold italic] },
161
+ { text: ' you?' }
162
+ ]
163
+ end
164
+
165
+ before do
166
+ arranger.format_array = array
167
+ end
168
+
169
+ describe 'after all strings have been consumed' do
170
+ before do
171
+ while arranger.next_string
172
+ end
173
+ end
174
+
175
+ it 'raises RuntimeError an error if not finalized' do
176
+ expect { arranger.retrieve_fragment }.to raise_error(RuntimeError)
177
+ end
178
+
179
+ describe 'and finalized' do
180
+ before do
181
+ arranger.finalize_line
182
+ end
183
+
184
+ it 'returns the consumed fragments in order of consumption' do
185
+ expect(arranger.retrieve_fragment.text).to eq('hello ')
186
+ expect(arranger.retrieve_fragment.text).to eq('world how ')
187
+ expect(arranger.retrieve_fragment.text).to eq('are')
188
+ expect(arranger.retrieve_fragment.text).to eq(' you?')
189
+ end
190
+
191
+ it 'does not alter the current font style' do
192
+ arranger.retrieve_fragment
193
+ expect(arranger.current_format_state[:styles]).to be_nil
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ describe '#update_last_string' do
201
+ it 'updates the last retrieved string with what actually fit on the line '\
202
+ 'and the list of unconsumed with what did not' do
203
+ array = [
204
+ { text: 'hello ' },
205
+ { text: 'world how ', styles: [:bold] },
206
+ { text: 'are', styles: %i[bold italic] },
207
+ { text: ' you now?', styles: %i[bold italic] }
208
+ ]
209
+ arranger.format_array = array
210
+ while arranger.next_string
211
+ end
212
+ arranger.update_last_string(' you', ' now?', nil)
213
+ expect(arranger.consumed[3]).to eq(
214
+ text: ' you',
215
+ styles: %i[bold italic]
216
+ )
217
+ expect(arranger.unconsumed).to eq([
218
+ { text: ' now?', styles: %i[bold italic] }
219
+ ])
220
+ end
221
+
222
+ it 'sets the format state to the previously processed fragment' do
223
+ array = [
224
+ { text: 'hello ' },
225
+ { text: 'world how ', styles: [:bold] },
226
+ { text: 'are', styles: %i[bold italic] },
227
+ { text: ' you now?' }
228
+ ]
229
+ arranger.format_array = array
230
+ 3.times { arranger.next_string }
231
+ expect(arranger.current_format_state).to eq(styles: %i[bold italic])
232
+ arranger.update_last_string('', 'are', '-')
233
+ expect(arranger.current_format_state).to eq(styles: [:bold])
234
+ end
235
+
236
+ context 'when the entire string was used' do
237
+ it 'does not push empty string onto unconsumed' do
238
+ array = [
239
+ { text: 'hello ' },
240
+ { text: 'world how ', styles: [:bold] },
241
+ { text: 'are', styles: %i[bold italic] },
242
+ { text: ' you now?' }
243
+ ]
244
+ arranger.format_array = array
245
+ while arranger.next_string
246
+ end
247
+ arranger.update_last_string(' you now?', '', nil)
248
+ expect(arranger.unconsumed).to eq([])
249
+ end
250
+ end
251
+ end
252
+
253
+ describe '#space_count' do
254
+ before do
255
+ array = [
256
+ { text: 'hello ' },
257
+ { text: 'world how ', styles: [:bold] },
258
+ { text: 'are', styles: %i[bold italic] },
259
+ { text: ' you?' }
260
+ ]
261
+ arranger.format_array = array
262
+ while arranger.next_string
263
+ end
264
+ end
265
+
266
+ it 'raise_errors an error if called before finalize_line was called' do
267
+ expect do
268
+ arranger.space_count
269
+ end.to raise_error(RuntimeError)
270
+ end
271
+
272
+ it 'returns the total number of spaces in all fragments' do
273
+ arranger.finalize_line
274
+ expect(arranger.space_count).to eq(4)
275
+ end
276
+ end
277
+
278
+ describe '#finalize_line' do
279
+ it 'makes it so that all trailing white space fragments exclude '\
280
+ 'trailing white space' do
281
+ array = [
282
+ { text: 'hello ' },
283
+ { text: 'world how ', styles: [:bold] },
284
+ { text: ' ', styles: %i[bold italic] }
285
+ ]
286
+ arranger.format_array = array
287
+ while arranger.next_string
288
+ end
289
+ arranger.finalize_line
290
+ expect(arranger.fragments.length).to eq(3)
291
+
292
+ fragment = arranger.retrieve_fragment
293
+ expect(fragment.text).to eq('hello ')
294
+
295
+ fragment = arranger.retrieve_fragment
296
+ expect(fragment.text).to eq('world how')
297
+
298
+ fragment = arranger.retrieve_fragment
299
+ expect(fragment.text).to eq('')
300
+ end
301
+ end
302
+
303
+ describe '#line_width' do
304
+ before do
305
+ array = [
306
+ { text: 'hello ' },
307
+ { text: 'world', styles: [:bold] }
308
+ ]
309
+ arranger.format_array = array
310
+ while arranger.next_string
311
+ end
312
+ end
313
+
314
+ it 'raise_errors an error if called before finalize_line was called' do
315
+ expect do
316
+ arranger.line_width
317
+ end.to raise_error(RuntimeError)
318
+ end
319
+
320
+ it 'returns the width of the complete line' do
321
+ arranger.finalize_line
322
+ expect(arranger.line_width).to be > 0
323
+ end
324
+ end
325
+
326
+ describe '#line_width with character_spacing > 0' do
327
+ it 'returns a width greater than a line without a character_spacing' do
328
+ array = [
329
+ { text: 'hello ' },
330
+ { text: 'world', styles: [:bold] }
331
+ ]
332
+ arranger.format_array = array
333
+ while arranger.next_string
334
+ end
335
+ arranger.finalize_line
336
+
337
+ base_line_width = arranger.line_width
338
+
339
+ array = [
340
+ { text: 'hello ' },
341
+ { text: 'world', styles: [:bold], character_spacing: 7 }
342
+ ]
343
+ arranger.format_array = array
344
+ while arranger.next_string
345
+ end
346
+ arranger.finalize_line
347
+ expect(arranger.line_width).to be > base_line_width
348
+ end
349
+ end
350
+
351
+ describe '#line' do
352
+ before do
353
+ array = [
354
+ { text: 'hello ' },
355
+ { text: 'world', styles: [:bold] }
356
+ ]
357
+ arranger.format_array = array
358
+ while arranger.next_string
359
+ end
360
+ end
361
+
362
+ it 'raise_errors an error if called before finalize_line was called' do
363
+ expect do
364
+ arranger.line
365
+ end.to raise_error(RuntimeError)
366
+ end
367
+
368
+ it 'returns the complete line' do
369
+ arranger.finalize_line
370
+ expect(arranger.line).to eq('hello world')
371
+ end
372
+ end
373
+
374
+ describe '#unconsumed' do
375
+ it 'returns the original array if nothing was consumed' do
376
+ array = [
377
+ { text: 'hello ' },
378
+ { text: 'world how ', styles: [:bold] },
379
+ { text: 'are', styles: %i[bold italic] },
380
+ { text: ' you now?' }
381
+ ]
382
+ arranger.format_array = array
383
+ expect(arranger.unconsumed).to eq(array)
384
+ end
385
+
386
+ it 'returns an empty array if everything was consumed' do
387
+ array = [
388
+ { text: 'hello ' },
389
+ { text: 'world how ', styles: [:bold] },
390
+ { text: 'are', styles: %i[bold italic] },
391
+ { text: ' you now?' }
392
+ ]
393
+ arranger.format_array = array
394
+ while arranger.next_string
395
+ end
396
+ expect(arranger.unconsumed).to eq([])
397
+ end
398
+ end
399
+
400
+ describe '#finished' do
401
+ it 'be_falses if anything was not printed' do
402
+ array = [
403
+ { text: 'hello ' },
404
+ { text: 'world how ', styles: [:bold] },
405
+ { text: 'are', styles: %i[bold italic] },
406
+ { text: ' you now?' }
407
+ ]
408
+ arranger.format_array = array
409
+ while arranger.next_string
410
+ end
411
+ arranger.update_last_string(' you', 'now?', nil)
412
+ expect(arranger).to_not be_finished
413
+ end
414
+
415
+ it 'be_falses if everything was printed' do
416
+ array = [
417
+ { text: 'hello ' },
418
+ { text: 'world how ', styles: [:bold] },
419
+ { text: 'are', styles: %i[bold italic] },
420
+ { text: ' you now?' }
421
+ ]
422
+ arranger.format_array = array
423
+ while arranger.next_string
424
+ end
425
+ expect(arranger).to be_finished
426
+ end
427
+ end
428
+
429
+ describe '#max_line_height' do
430
+ it 'is the height of the maximum consumed fragment' do
431
+ array = [
432
+ { text: 'hello ' },
433
+ { text: 'world how ', styles: [:bold] },
434
+ { text: 'are', styles: %i[bold italic], size: 28 },
435
+ { text: ' you now?' }
436
+ ]
437
+ arranger.format_array = array
438
+ while arranger.next_string
439
+ end
440
+ arranger.finalize_line
441
+ expect(arranger.max_line_height).to be_within(0.0001).of(33.32)
442
+ end
443
+ end
444
+
445
+ describe '#repack_unretrieved' do
446
+ it 'restores part of the original string' do
447
+ array = [
448
+ { text: 'hello ' },
449
+ { text: 'world how ', styles: [:bold] },
450
+ { text: 'are', styles: %i[bold italic] },
451
+ { text: ' you now?' }
452
+ ]
453
+ arranger.format_array = array
454
+ while arranger.next_string
455
+ end
456
+ arranger.finalize_line
457
+ arranger.retrieve_fragment
458
+ arranger.retrieve_fragment
459
+ arranger.repack_unretrieved
460
+ expect(arranger.unconsumed).to eq([
461
+ { text: 'are', styles: %i[bold italic] },
462
+ { text: ' you now?' }
463
+ ])
464
+ end
465
+ end
466
+ end