prawn 1.1.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 (309) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +1 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -9
  6. data/Rakefile +9 -40
  7. data/lib/prawn/document/bounding_box.rb +54 -40
  8. data/lib/prawn/document/column_box.rb +8 -10
  9. data/lib/prawn/document/internals.rb +39 -146
  10. data/lib/prawn/document/span.rb +23 -17
  11. data/lib/prawn/document.rb +217 -182
  12. data/lib/prawn/encoding.rb +69 -101
  13. data/lib/prawn/errors.rb +47 -43
  14. data/lib/prawn/font.rb +124 -104
  15. data/lib/prawn/font_metric_cache.rb +23 -21
  16. data/lib/prawn/fonts/afm.rb +292 -0
  17. data/lib/prawn/{font → fonts}/dfont.rb +5 -12
  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 +140 -88
  21. data/lib/prawn/graphics/blend_mode.rb +65 -0
  22. data/lib/prawn/graphics/cap_style.rb +5 -5
  23. data/lib/prawn/graphics/color.rb +46 -44
  24. data/lib/prawn/graphics/dash.rb +27 -11
  25. data/lib/prawn/graphics/join_style.rb +11 -6
  26. data/lib/prawn/graphics/patterns.rb +220 -78
  27. data/lib/prawn/graphics/transformation.rb +20 -12
  28. data/lib/prawn/graphics/transparency.rb +20 -18
  29. data/lib/prawn/graphics.rb +153 -115
  30. data/lib/prawn/grid.rb +93 -50
  31. data/lib/prawn/image_handler.rb +4 -4
  32. data/lib/prawn/images/image.rb +3 -2
  33. data/lib/prawn/images/jpg.rb +31 -24
  34. data/lib/prawn/images/png.rb +101 -68
  35. data/lib/prawn/images.rb +64 -56
  36. data/lib/prawn/measurement_extensions.rb +10 -9
  37. data/lib/prawn/measurements.rb +20 -23
  38. data/lib/prawn/outline.rb +96 -75
  39. data/lib/prawn/repeater.rb +16 -16
  40. data/lib/prawn/security/arcfour.rb +2 -2
  41. data/lib/prawn/security.rb +100 -85
  42. data/lib/prawn/soft_mask.rb +37 -38
  43. data/lib/prawn/stamp.rb +28 -13
  44. data/lib/prawn/text/box.rb +24 -29
  45. data/lib/prawn/text/formatted/arranger.rb +108 -63
  46. data/lib/prawn/text/formatted/box.rb +187 -124
  47. data/lib/prawn/text/formatted/fragment.rb +24 -20
  48. data/lib/prawn/text/formatted/line_wrap.rb +133 -73
  49. data/lib/prawn/text/formatted/parser.rb +147 -127
  50. data/lib/prawn/text/formatted/wrap.rb +48 -35
  51. data/lib/prawn/text/formatted.rb +5 -5
  52. data/lib/prawn/text.rb +103 -68
  53. data/lib/prawn/transformation_stack.rb +44 -0
  54. data/lib/prawn/utilities.rb +10 -22
  55. data/lib/prawn/version.rb +5 -0
  56. data/lib/prawn/view.rb +101 -0
  57. data/lib/prawn.rb +39 -54
  58. data/manual/basic_concepts/adding_pages.rb +6 -7
  59. data/manual/basic_concepts/basic_concepts.rb +34 -25
  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 +8 -9
  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 +48 -0
  66. data/manual/bounding_box/bounding_box.rb +30 -28
  67. data/manual/bounding_box/bounds.rb +12 -13
  68. data/manual/bounding_box/canvas.rb +7 -8
  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 +25 -18
  72. data/manual/bounding_box/russian_boxes.rb +18 -18
  73. data/manual/bounding_box/stretchy.rb +12 -14
  74. data/manual/contents.rb +28 -22
  75. data/manual/cover.rb +33 -32
  76. data/manual/document_and_page_options/background.rb +15 -13
  77. data/manual/document_and_page_options/document_and_page_options.rb +24 -22
  78. data/manual/document_and_page_options/metadata.rb +20 -18
  79. data/manual/document_and_page_options/page_margins.rb +18 -20
  80. data/manual/document_and_page_options/page_size.rb +13 -13
  81. data/manual/document_and_page_options/print_scaling.rb +18 -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 +5 -6
  88. data/manual/graphics/fill_rules.rb +12 -11
  89. data/manual/graphics/gradients.rb +27 -21
  90. data/manual/graphics/graphics.rb +46 -40
  91. data/manual/graphics/helper.rb +19 -9
  92. data/manual/graphics/line_width.rb +8 -7
  93. data/manual/graphics/lines_and_curves.rb +8 -9
  94. data/manual/graphics/polygon.rb +6 -8
  95. data/manual/graphics/rectangle.rb +4 -5
  96. data/manual/graphics/rotate.rb +6 -9
  97. data/manual/graphics/scale.rb +14 -13
  98. data/manual/graphics/soft_masks.rb +4 -6
  99. data/manual/graphics/stroke_cap.rb +7 -8
  100. data/manual/graphics/stroke_dash.rb +15 -16
  101. data/manual/graphics/stroke_join.rb +6 -7
  102. data/manual/graphics/translate.rb +10 -10
  103. data/manual/graphics/transparency.rb +7 -9
  104. data/manual/how_to_read_this_manual.rb +8 -9
  105. data/manual/images/absolute_position.rb +6 -7
  106. data/manual/images/fit.rb +7 -8
  107. data/manual/images/horizontal.rb +10 -11
  108. data/manual/images/images.rb +28 -27
  109. data/manual/images/plain_image.rb +5 -6
  110. data/manual/images/scale.rb +9 -10
  111. data/manual/images/vertical.rb +16 -14
  112. data/manual/images/width_and_height.rb +10 -11
  113. data/manual/layout/boxes.rb +10 -11
  114. data/manual/layout/content.rb +9 -10
  115. data/manual/layout/layout.rb +17 -18
  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 +22 -21
  120. data/manual/outline/sections_and_pages.rb +17 -18
  121. data/manual/repeatable_content/alternate_page_numbering.rb +36 -0
  122. data/manual/repeatable_content/page_numbering.rb +17 -16
  123. data/manual/repeatable_content/repeatable_content.rb +27 -23
  124. data/manual/repeatable_content/repeater.rb +15 -16
  125. data/manual/repeatable_content/stamp.rb +14 -15
  126. data/manual/security/encryption.rb +8 -11
  127. data/manual/security/permissions.rb +20 -15
  128. data/manual/security/security.rb +18 -18
  129. data/manual/table.rb +16 -0
  130. data/manual/text/alignment.rb +16 -17
  131. data/manual/text/color.rb +12 -12
  132. data/manual/text/column_box.rb +9 -11
  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 +10 -8
  137. data/manual/text/formatted_callbacks.rb +33 -23
  138. data/manual/text/formatted_text.rb +36 -25
  139. data/manual/text/free_flowing_text.rb +22 -23
  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 +15 -10
  145. data/manual/text/positioned_text.rb +16 -17
  146. data/manual/text/registering_families.rb +27 -24
  147. data/manual/text/rendering_and_color.rb +9 -10
  148. data/manual/text/right_to_left_text.rb +30 -19
  149. data/manual/text/rotation.rb +33 -24
  150. data/manual/text/single_usage.rb +8 -9
  151. data/manual/text/text.rb +56 -54
  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 +24 -17
  155. data/manual/text/utf8.rb +12 -13
  156. data/manual/text/win_ansi_charset.rb +28 -25
  157. data/prawn.gemspec +45 -50
  158. data/spec/extensions/encoding_helpers.rb +3 -3
  159. data/spec/prawn/document/bounding_box_spec.rb +550 -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 +44 -0
  167. data/spec/prawn/document_spec.rb +805 -0
  168. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  169. data/spec/prawn/font_spec.rb +544 -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 +872 -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 +229 -0
  178. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  179. data/spec/prawn/outline_spec.rb +512 -0
  180. data/spec/prawn/repeater_spec.rb +166 -0
  181. data/spec/prawn/soft_mask_spec.rb +74 -0
  182. data/spec/prawn/stamp_spec.rb +173 -0
  183. data/spec/prawn/text/box_spec.rb +1110 -0
  184. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  185. data/spec/prawn/text/formatted/box_spec.rb +849 -0
  186. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  187. data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
  188. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  189. data/spec/prawn/text_draw_text_spec.rb +150 -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 -25
  198. data.tar.gz.sig +0 -0
  199. metadata +113 -276
  200. metadata.gz.sig +0 -0
  201. data/data/encodings/win_ansi.txt +0 -29
  202. data/data/images/16bit.alpha +0 -0
  203. data/data/images/16bit.color +0 -0
  204. data/data/images/16bit.png +0 -0
  205. data/data/images/arrow.png +0 -0
  206. data/data/images/arrow2.png +0 -0
  207. data/data/images/dice.alpha +0 -0
  208. data/data/images/dice.color +0 -0
  209. data/data/images/dice.png +0 -0
  210. data/data/images/dice_interlaced.png +0 -0
  211. data/data/images/fractal.jpg +0 -0
  212. data/data/images/indexed_color.dat +0 -0
  213. data/data/images/indexed_color.png +0 -0
  214. data/data/images/letterhead.jpg +0 -0
  215. data/data/images/license.md +0 -8
  216. data/data/images/page_white_text.alpha +0 -0
  217. data/data/images/page_white_text.color +0 -0
  218. data/data/images/page_white_text.png +0 -0
  219. data/data/images/pigs.jpg +0 -0
  220. data/data/images/prawn.png +0 -0
  221. data/data/images/ruport.png +0 -0
  222. data/data/images/ruport_data.dat +0 -0
  223. data/data/images/ruport_transparent.png +0 -0
  224. data/data/images/ruport_type0.png +0 -0
  225. data/data/images/stef.jpg +0 -0
  226. data/data/images/tru256.bmp +0 -0
  227. data/data/images/web-links.dat +0 -1
  228. data/data/images/web-links.png +0 -0
  229. data/data/pdfs/complex_template.pdf +0 -0
  230. data/data/pdfs/contains_ttf_font.pdf +0 -0
  231. data/data/pdfs/encrypted.pdf +0 -0
  232. data/data/pdfs/form.pdf +1 -819
  233. data/data/pdfs/hexagon.pdf +0 -61
  234. data/data/pdfs/indirect_reference.pdf +0 -86
  235. data/data/pdfs/multipage_template.pdf +0 -127
  236. data/data/pdfs/nested_pages.pdf +0 -118
  237. data/data/pdfs/page_without_mediabox.pdf +0 -193
  238. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  239. data/data/pdfs/two_hexagons.pdf +0 -90
  240. data/data/pdfs/version_1_6.pdf +0 -61
  241. data/data/shift_jis_text.txt +0 -1
  242. data/lib/prawn/document/graphics_state.rb +0 -73
  243. data/lib/prawn/font/afm.rb +0 -247
  244. data/lib/prawn/table/cell/image.rb +0 -69
  245. data/lib/prawn/table/cell/in_table.rb +0 -33
  246. data/lib/prawn/table/cell/span_dummy.rb +0 -93
  247. data/lib/prawn/table/cell/subtable.rb +0 -66
  248. data/lib/prawn/table/cell/text.rb +0 -154
  249. data/lib/prawn/table/cell.rb +0 -772
  250. data/lib/prawn/table/cells.rb +0 -255
  251. data/lib/prawn/table/column_width_calculator.rb +0 -182
  252. data/lib/prawn/table.rb +0 -644
  253. data/manual/table/basic_block.rb +0 -53
  254. data/manual/table/before_rendering_page.rb +0 -26
  255. data/manual/table/cell_border_lines.rb +0 -24
  256. data/manual/table/cell_borders_and_bg.rb +0 -31
  257. data/manual/table/cell_dimensions.rb +0 -30
  258. data/manual/table/cell_text.rb +0 -38
  259. data/manual/table/column_widths.rb +0 -30
  260. data/manual/table/content_and_subtables.rb +0 -39
  261. data/manual/table/creation.rb +0 -27
  262. data/manual/table/filtering.rb +0 -36
  263. data/manual/table/flow_and_header.rb +0 -17
  264. data/manual/table/image_cells.rb +0 -33
  265. data/manual/table/position.rb +0 -29
  266. data/manual/table/row_colors.rb +0 -20
  267. data/manual/table/span.rb +0 -30
  268. data/manual/table/style.rb +0 -22
  269. data/manual/table/table.rb +0 -52
  270. data/manual/table/width.rb +0 -27
  271. data/spec/acceptance/png.rb +0 -25
  272. data/spec/annotations_spec.rb +0 -74
  273. data/spec/bounding_box_spec.rb +0 -510
  274. data/spec/cell_spec.rb +0 -629
  275. data/spec/column_box_spec.rb +0 -65
  276. data/spec/destinations_spec.rb +0 -15
  277. data/spec/document_spec.rb +0 -730
  278. data/spec/extensions/mocha.rb +0 -46
  279. data/spec/font_metric_cache_spec.rb +0 -52
  280. data/spec/font_spec.rb +0 -449
  281. data/spec/formatted_text_arranger_spec.rb +0 -421
  282. data/spec/formatted_text_box_spec.rb +0 -639
  283. data/spec/formatted_text_fragment_spec.rb +0 -298
  284. data/spec/graphics_spec.rb +0 -669
  285. data/spec/grid_spec.rb +0 -96
  286. data/spec/image_handler_spec.rb +0 -54
  287. data/spec/images_spec.rb +0 -153
  288. data/spec/inline_formatted_text_parser_spec.rb +0 -564
  289. data/spec/jpg_spec.rb +0 -25
  290. data/spec/line_wrap_spec.rb +0 -344
  291. data/spec/measurement_units_spec.rb +0 -25
  292. data/spec/outline_spec.rb +0 -430
  293. data/spec/png_spec.rb +0 -237
  294. data/spec/reference_spec.rb +0 -25
  295. data/spec/repeater_spec.rb +0 -160
  296. data/spec/security_spec.rb +0 -158
  297. data/spec/soft_mask_spec.rb +0 -79
  298. data/spec/span_spec.rb +0 -44
  299. data/spec/stamp_spec.rb +0 -160
  300. data/spec/stroke_styles_spec.rb +0 -211
  301. data/spec/table/span_dummy_spec.rb +0 -17
  302. data/spec/table_spec.rb +0 -1527
  303. data/spec/text_at_spec.rb +0 -115
  304. data/spec/text_box_spec.rb +0 -1034
  305. data/spec/text_rendering_mode_spec.rb +0 -45
  306. data/spec/text_spacing_spec.rb +0 -93
  307. data/spec/text_spec.rb +0 -437
  308. data/spec/text_with_inline_formatting_spec.rb +0 -35
  309. data/spec/transparency_spec.rb +0 -91
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # core/text/formatted/line_wrap.rb : Implements individual line wrapping of
4
4
  # formatted text
@@ -11,10 +11,8 @@
11
11
  module Prawn
12
12
  module Text
13
13
  module Formatted #:nodoc:
14
-
15
14
  # @private
16
15
  class LineWrap #:nodoc:
17
-
18
16
  # The width of the last wrapped line
19
17
  #
20
18
  def width
@@ -26,7 +24,11 @@ module Prawn
26
24
 
27
25
  # Whether this line is the last line in the paragraph
28
26
  def paragraph_finished?
29
- @newline_encountered || is_next_string_newline? || @arranger.finished?
27
+ @newline_encountered || next_string_newline? || @arranger.finished?
28
+ end
29
+
30
+ def tokenize(fragment)
31
+ fragment.scan(scan_pattern(fragment.encoding))
30
32
  end
31
33
 
32
34
  # Work in conjunction with the PDF::Formatted::Arranger
@@ -36,8 +38,10 @@ module Prawn
36
38
  def wrap_line(options)
37
39
  initialize_line(options)
38
40
 
41
+ # rubocop: disable Lint/AssignmentInCondition
39
42
  while fragment = @arranger.next_string
40
- @fragment_output = ""
43
+ # rubocop: enable Lint/AssignmentInCondition
44
+ @fragment_output = +''
41
45
 
42
46
  fragment.lstrip! if first_fragment_on_this_line?(fragment)
43
47
  next if empty_line?(fragment)
@@ -59,21 +63,20 @@ module Prawn
59
63
  end
60
64
 
61
65
  def empty_line?(fragment)
62
- empty = line_empty? && fragment.empty? && is_next_string_newline?
63
- @arranger.update_last_string("", "", soft_hyphen) if empty
66
+ empty = line_empty? && fragment.empty? && next_string_newline?
67
+ if empty
68
+ @arranger.update_last_string('', '', soft_hyphen(fragment.encoding))
69
+ end
64
70
  empty
65
71
  end
66
72
 
67
- def is_next_string_newline?
73
+ def next_string_newline?
68
74
  @arranger.preview_next_string == "\n"
69
75
  end
70
76
 
71
77
  def apply_font_settings_and_add_fragment_to_line(fragment)
72
78
  result = nil
73
79
  @arranger.apply_font_settings do
74
- # if font has changed from Unicode to non-Unicode, or vice versa, the characters used for soft hyphens
75
- # and zero-width spaces will be different
76
- set_soft_hyphen_and_zero_width_space
77
80
  result = add_fragment_to_line(fragment)
78
81
  end
79
82
  result
@@ -83,23 +86,33 @@ module Prawn
83
86
  # the line
84
87
  #
85
88
  def add_fragment_to_line(fragment)
86
- if fragment == ""
89
+ case fragment
90
+ when ''
87
91
  true
88
- elsif fragment == "\n"
92
+ when "\n"
89
93
  @newline_encountered = true
90
94
  false
91
95
  else
92
- fragment.scan(scan_pattern).each do |segment|
93
- if segment == zero_width_space
94
- segment_width = 0
95
- else
96
- segment_width = @document.width_of(segment, :kerning => @kerning)
97
- end
96
+ tokenize(fragment).each do |segment|
97
+ segment_width =
98
+ if segment == zero_width_space(segment.encoding)
99
+ 0
100
+ else
101
+ @document.width_of(segment, kerning: @kerning)
102
+ end
98
103
 
99
104
  if @accumulated_width + segment_width <= @width
100
105
  @accumulated_width += segment_width
106
+ shy = soft_hyphen(segment.encoding)
107
+ if segment[-1] == shy
108
+ sh_width = @document.width_of(shy, kerning: @kerning)
109
+ @accumulated_width -= sh_width
110
+ end
101
111
  @fragment_output += segment
102
112
  else
113
+ if @accumulated_width.zero? && @line_contains_more_than_one_word
114
+ @line_contains_more_than_one_word = false
115
+ end
103
116
  end_of_the_line_reached(segment)
104
117
  fragment_finished(fragment)
105
118
  return false
@@ -113,13 +126,25 @@ module Prawn
113
126
 
114
127
  # The pattern used to determine chunks of text to place on a given line
115
128
  #
116
- def scan_pattern
117
- pattern = "[^#{break_chars}]+#{soft_hyphen}|" +
118
- "[^#{break_chars}]+#{hyphen}+|" +
119
- "[^#{break_chars}]+|" +
120
- "[#{whitespace}]+|" +
121
- "#{hyphen}+[^#{break_chars}]*|" +
122
- "#{soft_hyphen}"
129
+ def scan_pattern(encoding = ::Encoding::UTF_8)
130
+ ebc = break_chars(encoding)
131
+ eshy = soft_hyphen(encoding)
132
+ ehy = hyphen(encoding)
133
+ ews = whitespace(encoding)
134
+
135
+ patterns = [
136
+ "[^#{ebc}]+#{eshy}",
137
+ "[^#{ebc}]+#{ehy}+",
138
+ "[^#{ebc}]+",
139
+ "[#{ews}]+",
140
+ "#{ehy}+[^#{ebc}]*",
141
+ eshy.to_s
142
+ ]
143
+
144
+ pattern = patterns
145
+ .map { |p| p.encode(encoding) }
146
+ .join('|')
147
+
123
148
  Regexp.new(pattern)
124
149
  end
125
150
 
@@ -127,32 +152,57 @@ module Prawn
127
152
  # current line, which in turn determines whether character level
128
153
  # word breaking is needed
129
154
  #
130
- def word_division_scan_pattern
131
- Regexp.new("\\s|[#{zero_width_space}#{soft_hyphen}#{hyphen}]")
155
+ def word_division_scan_pattern(encoding = ::Encoding::UTF_8)
156
+ common_whitespaces =
157
+ ["\t", "\n", "\v", "\r", ' '].map do |c|
158
+ c.encode(encoding)
159
+ end
160
+
161
+ Regexp.union(
162
+ common_whitespaces +
163
+ [
164
+ zero_width_space(encoding),
165
+ soft_hyphen(encoding),
166
+ hyphen(encoding)
167
+ ].compact
168
+ )
132
169
  end
133
170
 
134
- def break_chars
135
- "#{whitespace}#{soft_hyphen}#{hyphen}"
171
+ def soft_hyphen(encoding = ::Encoding::UTF_8)
172
+ Prawn::Text::SHY.encode(encoding)
173
+ rescue ::Encoding::InvalidByteSequenceError,
174
+ ::Encoding::UndefinedConversionError
175
+ nil
136
176
  end
137
177
 
138
- def whitespace
139
- " \\t#{zero_width_space}"
178
+ def break_chars(encoding = ::Encoding::UTF_8)
179
+ [
180
+ whitespace(encoding),
181
+ soft_hyphen(encoding),
182
+ hyphen(encoding)
183
+ ].join('')
140
184
  end
141
185
 
142
- def hyphen
143
- "-"
186
+ def zero_width_space(encoding = ::Encoding::UTF_8)
187
+ Prawn::Text::ZWSP.encode(encoding)
188
+ rescue ::Encoding::InvalidByteSequenceError,
189
+ ::Encoding::UndefinedConversionError
190
+ nil
144
191
  end
145
192
 
146
- def soft_hyphen
147
- @soft_hyphen
193
+ def whitespace(encoding = ::Encoding::UTF_8)
194
+ "\s\t#{zero_width_space(encoding)}".encode(encoding)
148
195
  end
149
196
 
150
- def zero_width_space
151
- @zero_width_space
197
+ def hyphen(_encoding = ::Encoding::UTF_8)
198
+ '-'
199
+ rescue ::Encoding::InvalidByteSequenceError,
200
+ ::Encoding::UndefinedConversionError
201
+ nil
152
202
  end
153
203
 
154
204
  def line_empty?
155
- @line_empty && @accumulated_width == 0
205
+ @line_empty && @accumulated_width.zero?
156
206
  end
157
207
 
158
208
  def initialize_line(options)
@@ -160,6 +210,8 @@ module Prawn
160
210
  @kerning = options[:kerning]
161
211
  @width = options[:width]
162
212
 
213
+ @disable_wrap_by_char = options[:disable_wrap_by_char]
214
+
163
215
  @accumulated_width = 0
164
216
  @line_empty = true
165
217
  @line_contains_more_than_one_word = false
@@ -171,52 +223,57 @@ module Prawn
171
223
  @line_full = false
172
224
  end
173
225
 
174
- def set_soft_hyphen_and_zero_width_space
175
- # this is done once per fragment, after the font settings for the fragment are applied --
176
- # it could actually be skipped if the font hasn't changed
177
- font = @document.font
178
- @soft_hyphen = font.normalize_encoding(Prawn::Text::SHY)
179
- @zero_width_space = font.unicode? ? Prawn::Text::ZWSP : ""
180
- end
181
-
182
226
  def fragment_finished(fragment)
183
227
  if fragment == "\n"
184
228
  @newline_encountered = true
185
229
  @line_empty = false
186
230
  else
187
- update_output_based_on_last_fragment(fragment, soft_hyphen)
231
+ update_output_based_on_last_fragment(
232
+ fragment,
233
+ soft_hyphen(fragment.encoding)
234
+ )
188
235
  update_line_status_based_on_last_output
189
- determine_whether_to_pull_preceding_fragment_to_join_this_one(fragment)
236
+ pull_preceding_fragment_to_join_this_one?(fragment)
190
237
  end
191
238
  remember_this_fragment_for_backward_looking_ops
192
239
  end
193
240
 
194
- def update_output_based_on_last_fragment(fragment, normalized_soft_hyphen=nil)
195
- remaining_text = fragment.slice(@fragment_output.length..fragment.length)
196
- raise Prawn::Errors::CannotFit if line_finished? && line_empty? &&
197
- @fragment_output.empty? && !fragment.strip.empty?
198
- @arranger.update_last_string(@fragment_output, remaining_text, normalized_soft_hyphen)
241
+ def update_output_based_on_last_fragment(
242
+ fragment, normalized_soft_hyphen = nil
243
+ )
244
+ remaining_text =
245
+ fragment.slice(@fragment_output.length..fragment.length)
246
+ if line_finished? && line_empty? && @fragment_output.empty? &&
247
+ !fragment.strip.empty?
248
+ raise Prawn::Errors::CannotFit
249
+ end
250
+
251
+ @arranger.update_last_string(
252
+ @fragment_output,
253
+ remaining_text,
254
+ normalized_soft_hyphen
255
+ )
199
256
  end
200
257
 
201
- def determine_whether_to_pull_preceding_fragment_to_join_this_one(current_fragment)
202
- if @fragment_output.empty? &&
203
- !current_fragment.empty? &&
204
- @line_contains_more_than_one_word
205
- unless previous_fragment_ended_with_breakable? ||
206
- fragment_begins_with_breakable?(current_fragment)
207
- @fragment_output = @previous_fragment_output_without_last_word
208
- update_output_based_on_last_fragment(@previous_fragment)
209
- end
258
+ def pull_preceding_fragment_to_join_this_one?(current_fragment)
259
+ if @fragment_output.empty? && !current_fragment.empty? &&
260
+ @line_contains_more_than_one_word &&
261
+ !(previous_fragment_ended_with_breakable? ||
262
+ fragment_begins_with_breakable?(current_fragment))
263
+ @fragment_output = @previous_fragment_output_without_last_word
264
+ update_output_based_on_last_fragment(@previous_fragment)
210
265
  end
211
266
  end
212
267
 
213
268
  def remember_this_fragment_for_backward_looking_ops
214
269
  @previous_fragment = @fragment_output.dup
215
270
  pf = @previous_fragment
216
- @previous_fragment_ended_with_breakable = pf =~ /[#{break_chars}]$/
217
- last_word = pf.slice(/[^#{break_chars}]*$/)
271
+ @previous_fragment_ended_with_breakable =
272
+ pf =~ /[#{break_chars(pf.encoding)}]$/
273
+ last_word = pf.slice(/[^#{break_chars(pf.encoding)}]*$/)
218
274
  last_word_length = last_word.nil? ? 0 : last_word.length
219
- @previous_fragment_output_without_last_word = pf.slice(0, pf.length - last_word_length)
275
+ @previous_fragment_output_without_last_word =
276
+ pf.slice(0, pf.length - last_word_length)
220
277
  end
221
278
 
222
279
  def previous_fragment_ended_with_breakable?
@@ -224,7 +281,7 @@ module Prawn
224
281
  end
225
282
 
226
283
  def fragment_begins_with_breakable?(fragment)
227
- fragment =~ /^[#{break_chars}]/
284
+ fragment =~ /^[#{break_chars(fragment.encoding)}]/
228
285
  end
229
286
 
230
287
  def line_finished?
@@ -232,25 +289,28 @@ module Prawn
232
289
  end
233
290
 
234
291
  def update_line_status_based_on_last_output
235
- @line_contains_more_than_one_word = true if @fragment_output =~ word_division_scan_pattern
292
+ if @fragment_output&.match?(word_division_scan_pattern(@fragment_output.encoding))
293
+ @line_contains_more_than_one_word = true
294
+ end
236
295
  end
237
296
 
238
297
  def end_of_the_line_reached(segment)
239
298
  update_line_status_based_on_last_output
240
- wrap_by_char(segment) unless @line_contains_more_than_one_word
299
+ unless @disable_wrap_by_char || @line_contains_more_than_one_word
300
+ wrap_by_char(segment)
301
+ end
241
302
  @line_full = true
242
303
  end
243
304
 
244
305
  def wrap_by_char(segment)
245
- font = @document.font
246
306
  segment.each_char do |char|
247
- break unless append_char(char,font)
307
+ break unless append_char(char)
248
308
  end
249
309
  end
250
310
 
251
- def append_char(char,font)
311
+ def append_char(char)
252
312
  # kerning doesn't make sense in the context of a single character
253
- char_width = font.compute_width_of(char)
313
+ char_width = @document.width_of(char)
254
314
 
255
315
  if @accumulated_width + char_width <= @width
256
316
  @accumulated_width += char_width