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