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,341 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prawn::Text::Formatted::Fragment do
4
+ let(:pdf) { create_pdf }
5
+
6
+ describe 'Text::Formatted::Fragment' do
7
+ let(:fragment) do
8
+ format_state = {
9
+ styles: [:bold, :italic],
10
+ color: nil,
11
+ link: nil,
12
+ anchor: nil,
13
+ font: nil,
14
+ size: nil
15
+ }
16
+ described_class.new(
17
+ 'hello world',
18
+ format_state,
19
+ pdf
20
+ ).tap do |fragment|
21
+ fragment.width = 100
22
+ fragment.left = 50
23
+ fragment.baseline = 200
24
+ fragment.line_height = 27
25
+ fragment.descender = 7
26
+ fragment.ascender = 17
27
+ end
28
+ end
29
+
30
+ describe '#width' do
31
+ it 'returns the width' do
32
+ expect(fragment.width).to eq(100)
33
+ end
34
+ end
35
+
36
+ describe '#styles' do
37
+ it 'returns the styles array' do
38
+ expect(fragment.styles).to eq([:bold, :italic])
39
+ end
40
+
41
+ it 'nevers return nil' do
42
+ format_state = {
43
+ styles: nil,
44
+ color: nil,
45
+ link: nil,
46
+ anchor: nil,
47
+ font: nil,
48
+ size: nil
49
+ }
50
+ fragment = described_class.new(
51
+ 'hello world',
52
+ format_state,
53
+ pdf
54
+ )
55
+ expect(fragment.styles).to eq([])
56
+ end
57
+ end
58
+
59
+ describe '#line_height' do
60
+ it 'returns the line_height' do
61
+ expect(fragment.line_height).to eq(27)
62
+ end
63
+ end
64
+
65
+ describe '#ascender' do
66
+ it 'returns the ascender' do
67
+ expect(fragment.ascender).to eq(17)
68
+ end
69
+ end
70
+
71
+ describe '#descender' do
72
+ it 'returns the descender' do
73
+ expect(fragment.descender).to eq(7)
74
+ end
75
+ end
76
+
77
+ describe '#y_offset' do
78
+ it 'is zero' do
79
+ expect(fragment.y_offset).to eq(0)
80
+ end
81
+ end
82
+
83
+ describe '#bounding_box' do
84
+ it 'returns the bounding box surrounding the fragment' do
85
+ target_box = [50, 193, 150, 217]
86
+ expect(fragment.bounding_box).to eq(target_box)
87
+ end
88
+ end
89
+
90
+ describe '#absolute_bounding_box' do
91
+ it 'should return the bounding box surrounding the fragment' \
92
+ ' in absolute coordinates' do
93
+ target_box = [50, 193, 150, 217]
94
+ target_box[0] += pdf.bounds.absolute_left
95
+ target_box[1] += pdf.bounds.absolute_bottom
96
+ target_box[2] += pdf.bounds.absolute_left
97
+ target_box[3] += pdf.bounds.absolute_bottom
98
+
99
+ expect(fragment.absolute_bounding_box).to eq(target_box)
100
+ end
101
+ end
102
+
103
+ describe '#underline_points' do
104
+ it 'defines a line under the fragment' do
105
+ y = 198.75
106
+ target_points = [[50, y], [150, y]]
107
+ expect(fragment.underline_points).to eq(target_points)
108
+ end
109
+ end
110
+
111
+ describe '#strikethrough_points' do
112
+ it 'defines a line through the fragment' do
113
+ y = 200 + fragment.ascender * 0.3
114
+ target_points = [[50, y], [150, y]]
115
+ expect(fragment.strikethrough_points).to eq(target_points)
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '#space_count' do
121
+ it 'returns the number of spaces in the fragment' do
122
+ format_state = {}
123
+ fragment = described_class.new(
124
+ 'hello world ',
125
+ format_state,
126
+ pdf
127
+ )
128
+ expect(fragment.space_count).to eq(2)
129
+ end
130
+
131
+ it 'should exclude trailing spaces from the count when ' \
132
+ ':exclude_trailing_white_space => true' do
133
+ format_state = { exclude_trailing_white_space: true }
134
+ fragment = described_class.new(
135
+ 'hello world ',
136
+ format_state,
137
+ pdf
138
+ )
139
+ expect(fragment.space_count).to eq(1)
140
+ end
141
+ end
142
+
143
+ describe '#include_trailing_white_space!' do
144
+ it 'makes the fragment include trailing white space' do
145
+ format_state = { exclude_trailing_white_space: true }
146
+ fragment = described_class.new(
147
+ 'hello world ',
148
+ format_state,
149
+ pdf
150
+ )
151
+ expect(fragment.space_count).to eq(1)
152
+ fragment.include_trailing_white_space!
153
+ expect(fragment.space_count).to eq(2)
154
+ end
155
+ end
156
+
157
+ describe '#text' do
158
+ it 'returns the fragment text' do
159
+ format_state = {}
160
+ fragment = described_class.new(
161
+ 'hello world ',
162
+ format_state,
163
+ pdf
164
+ )
165
+ expect(fragment.text).to eq('hello world ')
166
+ end
167
+
168
+ it 'should return the fragment text without trailing spaces when ' \
169
+ ':exclude_trailing_white_space => true' do
170
+ format_state = { exclude_trailing_white_space: true }
171
+ fragment = described_class.new(
172
+ 'hello world ',
173
+ format_state,
174
+ pdf
175
+ )
176
+ expect(fragment.text).to eq('hello world')
177
+ end
178
+ end
179
+
180
+ describe '#word_spacing=' do
181
+ let(:fragment) do
182
+ format_state = {
183
+ styles: [:bold, :italic],
184
+ color: nil,
185
+ link: nil,
186
+ anchor: nil,
187
+ font: nil,
188
+ size: nil
189
+ }
190
+ described_class.new(
191
+ 'hello world',
192
+ format_state,
193
+ pdf
194
+ ).tap do |fragment|
195
+ fragment.width = 100
196
+ fragment.left = 50
197
+ fragment.baseline = 200
198
+ fragment.line_height = 27
199
+ fragment.descender = 7
200
+ fragment.ascender = 17
201
+ fragment.word_spacing = 10
202
+ end
203
+ end
204
+
205
+ it 'accounts for word_spacing in #width' do
206
+ expect(fragment.width).to eq(110)
207
+ end
208
+
209
+ it 'accounts for word_spacing in #bounding_box' do
210
+ target_box = [50, 193, 160, 217]
211
+ expect(fragment.bounding_box).to eq(target_box)
212
+ end
213
+
214
+ it 'accounts for word_spacing in #absolute_bounding_box' do
215
+ target_box = [50, 193, 160, 217]
216
+ target_box[0] += pdf.bounds.absolute_left
217
+ target_box[1] += pdf.bounds.absolute_bottom
218
+ target_box[2] += pdf.bounds.absolute_left
219
+ target_box[3] += pdf.bounds.absolute_bottom
220
+ expect(fragment.absolute_bounding_box).to eq(target_box)
221
+ end
222
+
223
+ it 'accounts for word_spacing in #underline_points' do
224
+ y = 198.75
225
+ target_points = [[50, y], [160, y]]
226
+ expect(fragment.underline_points).to eq(target_points)
227
+ end
228
+
229
+ it 'accounts for word_spacing in #strikethrough_points' do
230
+ y = 200 + fragment.ascender * 0.3
231
+ target_points = [[50, y], [160, y]]
232
+ expect(fragment.strikethrough_points).to eq(target_points)
233
+ end
234
+ end
235
+
236
+ describe 'subscript' do
237
+ let(:fragment) do
238
+ format_state = {
239
+ styles: [:subscript],
240
+ color: nil,
241
+ link: nil,
242
+ anchor: nil,
243
+ font: nil,
244
+ size: nil
245
+ }
246
+ described_class.new(
247
+ 'hello world',
248
+ format_state,
249
+ pdf
250
+ ).tap do |fragment|
251
+ fragment.line_height = 27
252
+ fragment.descender = 7
253
+ fragment.ascender = 17
254
+ end
255
+ end
256
+
257
+ describe '#subscript?' do
258
+ it 'be_trues' do
259
+ expect(fragment).to be_subscript
260
+ end
261
+ end
262
+
263
+ describe '#y_offset' do
264
+ it 'returns a negative value' do
265
+ expect(fragment.y_offset).to be < 0
266
+ end
267
+ end
268
+ end
269
+
270
+ describe 'superscript' do
271
+ let(:fragment) do
272
+ format_state = {
273
+ styles: [:superscript],
274
+ color: nil,
275
+ link: nil,
276
+ anchor: nil,
277
+ font: nil,
278
+ size: nil
279
+ }
280
+ described_class.new(
281
+ 'hello world',
282
+ format_state,
283
+ pdf
284
+ ).tap do |fragment|
285
+ fragment.line_height = 27
286
+ fragment.descender = 7
287
+ fragment.ascender = 17
288
+ end
289
+ end
290
+
291
+ describe '#superscript?' do
292
+ it 'be_trues' do
293
+ expect(fragment).to be_superscript
294
+ end
295
+ end
296
+
297
+ describe '#y_offset' do
298
+ it 'returns a positive value' do
299
+ expect(fragment.y_offset).to be > 0
300
+ end
301
+ end
302
+ end
303
+
304
+ context 'with :direction => :rtl' do
305
+ it '#text should be reversed' do
306
+ format_state = { direction: :rtl }
307
+ fragment = described_class.new(
308
+ 'hello world',
309
+ format_state,
310
+ pdf
311
+ )
312
+ expect(fragment.text).to eq('dlrow olleh')
313
+ end
314
+ end
315
+
316
+ describe '#default_direction=' do
317
+ it 'should set the direction if there is no fragment level direction ' \
318
+ 'specification' do
319
+ format_state = {}
320
+ fragment = described_class.new(
321
+ 'hello world',
322
+ format_state,
323
+ pdf
324
+ )
325
+ fragment.default_direction = :rtl
326
+ expect(fragment.direction).to eq(:rtl)
327
+ end
328
+
329
+ it 'should not set the direction if there is a fragment level direction ' \
330
+ 'specification' do
331
+ format_state = { direction: :rtl }
332
+ fragment = described_class.new(
333
+ 'hello world',
334
+ format_state,
335
+ pdf
336
+ )
337
+ fragment.default_direction = :ltr
338
+ expect(fragment.direction).to eq(:rtl)
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,491 @@
1
+ require 'spec_helper'
2
+
3
+ describe Prawn::Text::Formatted::LineWrap do
4
+ let(:pdf) { create_pdf }
5
+ let(:arranger) do
6
+ Prawn::Text::Formatted::Arranger.new(pdf).tap do |a|
7
+ a.format_array = [
8
+ { text: "hello\nworld\n\n\nhow are you?" },
9
+ { text: "\n" },
10
+ { text: "\n" },
11
+ { text: '' },
12
+ { text: 'fine, thanks. ' * 4 },
13
+ { text: '' },
14
+ { text: "\n" },
15
+ { text: '' }
16
+ ]
17
+ end
18
+ end
19
+ let(:line_wrap) { described_class.new }
20
+
21
+ it 'should only return an empty string if nothing fit or there' \
22
+ 'was nothing to wrap' do
23
+ 8.times do
24
+ line = line_wrap.wrap_line(
25
+ arranger: arranger,
26
+ width: 200,
27
+ document: pdf
28
+ )
29
+ expect(line).to_not be_empty
30
+ end
31
+ line = line_wrap.wrap_line(
32
+ arranger: arranger,
33
+ width: 200,
34
+ document: pdf
35
+ )
36
+ expect(line).to be_empty
37
+ end
38
+
39
+ it 'tokenizes a string using the scan_pattern' do
40
+ tokens = line_wrap.tokenize('one two three')
41
+ expect(tokens.length).to eq(5)
42
+ end
43
+
44
+ describe 'Core::Text::Formatted::LineWrap#wrap_line' do
45
+ let(:arranger) { Prawn::Text::Formatted::Arranger.new(pdf) }
46
+ let(:one_word_width) { 50 }
47
+
48
+ it 'strips leading and trailing spaces' do
49
+ array = [
50
+ { text: ' hello world, ' },
51
+ { text: 'goodbye ', style: [:bold] }
52
+ ]
53
+ arranger.format_array = array
54
+ string = line_wrap.wrap_line(
55
+ arranger: arranger,
56
+ width: 300,
57
+ document: pdf
58
+ )
59
+ expect(string).to eq('hello world, goodbye')
60
+ end
61
+
62
+ it 'should strip trailing spaces when a white-space-only fragment was' \
63
+ ' successfully pushed onto the end of a line but no other non-white' \
64
+ ' space fragment fits after it' do
65
+ array = [
66
+ { text: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ' },
67
+ { text: ' ', style: [:bold] },
68
+ { text: ' bbbbbbbbbbbbbbbbbbbbbbbbbbbb' }
69
+ ]
70
+ arranger.format_array = array
71
+ string = line_wrap.wrap_line(
72
+ arranger: arranger,
73
+ width: 300,
74
+ document: pdf
75
+ )
76
+ expect(string).to eq('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
77
+ end
78
+ it 'raise_errors CannotFit if a too-small width is given' do
79
+ array = [
80
+ { text: ' hello world, ' },
81
+ { text: 'goodbye ', style: [:bold] }
82
+ ]
83
+ arranger.format_array = array
84
+ expect do
85
+ line_wrap.wrap_line(
86
+ arranger: arranger,
87
+ width: 1,
88
+ document: pdf
89
+ )
90
+ end.to raise_error(Prawn::Errors::CannotFit)
91
+ end
92
+
93
+ it 'breaks on space' do
94
+ array = [{ text: 'hello world' }]
95
+ arranger.format_array = array
96
+ string = line_wrap.wrap_line(
97
+ arranger: arranger,
98
+ width: one_word_width,
99
+ document: pdf
100
+ )
101
+ expect(string).to eq('hello')
102
+ end
103
+
104
+ it 'breaks on zero-width space' do
105
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
106
+ array = [{ text: "hello#{Prawn::Text::ZWSP}world" }]
107
+ arranger.format_array = array
108
+ string = line_wrap.wrap_line(
109
+ arranger: arranger,
110
+ width: one_word_width,
111
+ document: pdf
112
+ )
113
+ expect(string).to eq('hello')
114
+ end
115
+
116
+ it 'does not display zero-width space' do
117
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
118
+ array = [{ text: "hello#{Prawn::Text::ZWSP}world" }]
119
+ arranger.format_array = array
120
+ string = line_wrap.wrap_line(
121
+ arranger: arranger,
122
+ width: 300,
123
+ document: pdf
124
+ )
125
+ expect(string).to eq('helloworld')
126
+ end
127
+
128
+ it 'does not raise CannotFit if first fragment is a zero-width space' do
129
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
130
+ array = [{ text: Prawn::Text::ZWSP }, { text: 'stringofchars' }]
131
+ arranger.format_array = array
132
+ string = line_wrap.wrap_line(
133
+ arranger: arranger,
134
+ width: 50,
135
+ document: pdf
136
+ )
137
+ expect(string).to eq('stringof')
138
+ end
139
+
140
+ it 'breaks on tab' do
141
+ array = [{ text: "hello\tworld" }]
142
+ arranger.format_array = array
143
+ string = line_wrap.wrap_line(
144
+ arranger: arranger,
145
+ width: one_word_width,
146
+ document: pdf
147
+ )
148
+ expect(string).to eq('hello')
149
+ end
150
+
151
+ it 'does not break on NBSP' do
152
+ array = [{ text: "hello#{Prawn::Text::NBSP}world" }]
153
+ arranger.format_array = array
154
+ string = line_wrap.wrap_line(
155
+ arranger: arranger,
156
+ width: one_word_width,
157
+ document: pdf
158
+ )
159
+ expect(string).to eq("hello#{Prawn::Text::NBSP}wor")
160
+ end
161
+
162
+ it 'does not break on NBSP in a Win-1252 encoded string' do
163
+ array = [{
164
+ text: "hello#{Prawn::Text::NBSP}world".encode(Encoding::Windows_1252)
165
+ }]
166
+ arranger.format_array = array
167
+ string = line_wrap.wrap_line(
168
+ arranger: arranger,
169
+ width: one_word_width,
170
+ document: pdf
171
+ )
172
+ expect(string).to eq("hello#{Prawn::Text::NBSP}wor")
173
+ end
174
+
175
+ it 'breaks on hyphens' do
176
+ array = [{ text: 'hello-world' }]
177
+ arranger.format_array = array
178
+ string = line_wrap.wrap_line(
179
+ arranger: arranger,
180
+ width: one_word_width,
181
+ document: pdf
182
+ )
183
+ expect(string).to eq('hello-')
184
+ end
185
+
186
+ it 'should not break after a hyphen that follows white space and' \
187
+ 'precedes a word' do
188
+ array = [{ text: 'hello -' }]
189
+ arranger.format_array = array
190
+ string = line_wrap.wrap_line(
191
+ arranger: arranger,
192
+ width: one_word_width,
193
+ document: pdf
194
+ )
195
+ expect(string).to eq('hello -')
196
+
197
+ array = [{ text: 'hello -world' }]
198
+ arranger.format_array = array
199
+ string = line_wrap.wrap_line(
200
+ arranger: arranger,
201
+ width: one_word_width,
202
+ document: pdf
203
+ )
204
+ expect(string).to eq('hello')
205
+ end
206
+
207
+ it 'breaks on a soft hyphen' do
208
+ string = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}world")
209
+ array = [{ text: string }]
210
+ arranger.format_array = array
211
+ string = line_wrap.wrap_line(
212
+ arranger: arranger,
213
+ width: one_word_width,
214
+ document: pdf
215
+ )
216
+ expect(string).to eq("hello#{Prawn::Text::SHY}")
217
+
218
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
219
+ line_wrap = described_class.new
220
+
221
+ string = "hello#{Prawn::Text::SHY}world"
222
+ array = [{ text: string }]
223
+ arranger.format_array = array
224
+ string = line_wrap.wrap_line(
225
+ arranger: arranger,
226
+ width: one_word_width,
227
+ document: pdf
228
+ )
229
+ expect(string).to eq("hello#{Prawn::Text::SHY}")
230
+ end
231
+
232
+ it 'ignores width of a soft-hyphen during adding fragments to line',
233
+ issue: 775 do
234
+ hyphen_string = "Hy#{Prawn::Text::SHY}phe#{Prawn::Text::SHY}nat"\
235
+ "#{Prawn::Text::SHY}ions "
236
+ string1 = pdf.font.normalize_encoding(hyphen_string * 5)
237
+ string2 = pdf.font.normalize_encoding('Hyphenations ' * 3 + hyphen_string)
238
+
239
+ array1 = [{ text: string1 }]
240
+ array2 = [{ text: string2 }]
241
+
242
+ arranger.format_array = array1
243
+
244
+ res1 = line_wrap.wrap_line(
245
+ arranger: arranger,
246
+ width: 300,
247
+ document: pdf
248
+ )
249
+
250
+ line_wrap = described_class.new
251
+
252
+ arranger.format_array = array2
253
+
254
+ res2 = line_wrap.wrap_line(
255
+ arranger: arranger,
256
+ width: 300,
257
+ document: pdf
258
+ )
259
+ expect(res1).to eq(res2)
260
+ end
261
+
262
+ it 'should not display soft hyphens except at the end of a line ' \
263
+ 'for more than one element in format_array', issue: 347 do
264
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
265
+ line_wrap = described_class.new
266
+
267
+ string1 = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}world ")
268
+ string2 = pdf.font.normalize_encoding("hi#{Prawn::Text::SHY}earth")
269
+ array = [{ text: string1 }, { text: string2 }]
270
+ arranger.format_array = array
271
+ string = line_wrap.wrap_line(
272
+ arranger: arranger,
273
+ width: 300,
274
+ document: pdf
275
+ )
276
+ expect(string).to eq('helloworld hiearth')
277
+ end
278
+
279
+ it 'does not break before a hard hyphen that follows a word' do
280
+ enough_width_for_hello_world = 60
281
+
282
+ array = [{ text: 'hello world' }]
283
+ arranger.format_array = array
284
+ string = line_wrap.wrap_line(
285
+ arranger: arranger,
286
+ width: enough_width_for_hello_world,
287
+ document: pdf
288
+ )
289
+ expect(string).to eq('hello world')
290
+
291
+ array = [{ text: 'hello world-' }]
292
+ arranger.format_array = array
293
+ string = line_wrap.wrap_line(
294
+ arranger: arranger,
295
+ width: enough_width_for_hello_world,
296
+ document: pdf
297
+ )
298
+ expect(string).to eq('hello')
299
+
300
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
301
+ line_wrap = described_class.new
302
+ enough_width_for_hello_world = 68
303
+
304
+ array = [{ text: 'hello world' }]
305
+ arranger.format_array = array
306
+ string = line_wrap.wrap_line(
307
+ arranger: arranger,
308
+ width: enough_width_for_hello_world,
309
+ document: pdf
310
+ )
311
+ expect(string).to eq('hello world')
312
+
313
+ array = [{ text: 'hello world-' }]
314
+ arranger.format_array = array
315
+ string = line_wrap.wrap_line(
316
+ arranger: arranger,
317
+ width: enough_width_for_hello_world,
318
+ document: pdf
319
+ )
320
+ expect(string).to eq('hello')
321
+ end
322
+
323
+ it 'should not break after a hard hyphen that follows a soft hyphen and' \
324
+ 'precedes a word' do
325
+ string = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}-")
326
+ array = [{ text: string }]
327
+ arranger.format_array = array
328
+ string = line_wrap.wrap_line(
329
+ arranger: arranger,
330
+ width: one_word_width,
331
+ document: pdf
332
+ )
333
+ expect(string).to eq('hello-')
334
+
335
+ string = pdf.font.normalize_encoding("hello#{Prawn::Text::SHY}-world")
336
+ array = [{ text: string }]
337
+ arranger.format_array = array
338
+ string = line_wrap.wrap_line(
339
+ arranger: arranger,
340
+ width: one_word_width,
341
+ document: pdf
342
+ )
343
+ expect(string).to eq("hello#{Prawn::Text::SHY}")
344
+
345
+ pdf.font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
346
+ line_wrap = described_class.new
347
+
348
+ string = "hello#{Prawn::Text::SHY}-"
349
+ array = [{ text: string }]
350
+ arranger.format_array = array
351
+ string = line_wrap.wrap_line(
352
+ arranger: arranger,
353
+ width: one_word_width,
354
+ document: pdf
355
+ )
356
+ expect(string).to eq('hello-')
357
+
358
+ string = "hello#{Prawn::Text::SHY}-world"
359
+ array = [{ text: string }]
360
+ arranger.format_array = array
361
+ string = line_wrap.wrap_line(
362
+ arranger: arranger,
363
+ width: one_word_width,
364
+ document: pdf
365
+ )
366
+ expect(string).to eq("hello#{Prawn::Text::SHY}")
367
+ end
368
+
369
+ it 'does not process UTF-8 chars with default font', issue: 693 do
370
+ array = [{ text: 'Test' }]
371
+ arranger.format_array = array
372
+
373
+ expect do
374
+ line_wrap.wrap_line(
375
+ arranger: arranger,
376
+ width: 300,
377
+ document: pdf
378
+ )
379
+ end.to raise_exception(Prawn::Errors::IncompatibleStringEncoding)
380
+ end
381
+
382
+ it 'processes UTF-8 chars with UTF-8 font', issue: 693 do
383
+ array = [{ text: 'Test' }]
384
+ arranger.format_array = array
385
+
386
+ pdf.font Pathname.new("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
387
+ string = line_wrap.wrap_line(
388
+ arranger: arranger,
389
+ width: 300,
390
+ document: pdf
391
+ )
392
+
393
+ expect(string).to eq('Test')
394
+ end
395
+ end
396
+
397
+ describe '#space_count' do
398
+ let(:arranger) { Prawn::Text::Formatted::Arranger.new(pdf) }
399
+
400
+ it 'returns the number of spaces in the last wrapped line' do
401
+ array = [
402
+ { text: 'hello world, ' },
403
+ { text: 'goodbye', style: [:bold] }
404
+ ]
405
+ arranger.format_array = array
406
+ line_wrap.wrap_line(
407
+ arranger: arranger,
408
+ width: 300,
409
+ document: pdf
410
+ )
411
+ expect(line_wrap.space_count).to eq(2)
412
+ end
413
+
414
+ it 'excludes preceding and trailing spaces from the count' do
415
+ array = [
416
+ { text: ' hello world, ' },
417
+ { text: 'goodbye ', style: [:bold] }
418
+ ]
419
+ arranger.format_array = array
420
+ line_wrap.wrap_line(
421
+ arranger: arranger,
422
+ width: 300,
423
+ document: pdf
424
+ )
425
+ expect(line_wrap.space_count).to eq(2)
426
+ end
427
+ end
428
+
429
+ describe '#paragraph_finished?' do
430
+ let(:arranger) { Prawn::Text::Formatted::Arranger.new(pdf) }
431
+ let(:line_wrap) { described_class.new }
432
+ let(:one_word_width) { 50 }
433
+
434
+ it 'is false when the last printed line is not the end of the paragraph' do
435
+ array = [{ text: 'hello world' }]
436
+ arranger.format_array = array
437
+ line_wrap.wrap_line(
438
+ arranger: arranger,
439
+ width: one_word_width,
440
+ document: pdf
441
+ )
442
+
443
+ expect(line_wrap.paragraph_finished?).to eq(false)
444
+ end
445
+
446
+ it 'is true when the last printed line is the last fragment to print' do
447
+ array = [{ text: 'hello world' }]
448
+ arranger.format_array = array
449
+ line_wrap.wrap_line(
450
+ arranger: arranger,
451
+ width: one_word_width,
452
+ document: pdf
453
+ )
454
+ line_wrap.wrap_line(
455
+ arranger: arranger,
456
+ width: one_word_width,
457
+ document: pdf
458
+ )
459
+
460
+ expect(line_wrap.paragraph_finished?).to eq(true)
461
+ end
462
+
463
+ it 'be_trues when a newline exists on the current line' do
464
+ array = [{ text: "hello\n world" }]
465
+ arranger.format_array = array
466
+ line_wrap.wrap_line(
467
+ arranger: arranger,
468
+ width: one_word_width,
469
+ document: pdf
470
+ )
471
+
472
+ expect(line_wrap.paragraph_finished?).to eq(true)
473
+ end
474
+
475
+ it 'be_trues when a newline exists in the next fragment' do
476
+ array = [
477
+ { text: 'hello ' },
478
+ { text: " \n" },
479
+ { text: 'world' }
480
+ ]
481
+ arranger.format_array = array
482
+ line_wrap.wrap_line(
483
+ arranger: arranger,
484
+ width: one_word_width,
485
+ document: pdf
486
+ )
487
+
488
+ expect(line_wrap.paragraph_finished?).to eq(true)
489
+ end
490
+ end
491
+ end