alphasights-prawn 0.10.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 (244) hide show
  1. data/COPYING +340 -0
  2. data/HACKING +50 -0
  3. data/LICENSE +56 -0
  4. data/README +141 -0
  5. data/Rakefile +52 -0
  6. data/data/encodings/win_ansi.txt +29 -0
  7. data/data/fonts/Action Man.dfont +0 -0
  8. data/data/fonts/Activa.ttf +0 -0
  9. data/data/fonts/Chalkboard.ttf +0 -0
  10. data/data/fonts/Courier-Bold.afm +342 -0
  11. data/data/fonts/Courier-BoldOblique.afm +342 -0
  12. data/data/fonts/Courier-Oblique.afm +342 -0
  13. data/data/fonts/Courier.afm +342 -0
  14. data/data/fonts/DejaVuSans.ttf +0 -0
  15. data/data/fonts/Dustismo_Roman.ttf +0 -0
  16. data/data/fonts/Helvetica-Bold.afm +2827 -0
  17. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  18. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  19. data/data/fonts/Helvetica.afm +3051 -0
  20. data/data/fonts/MustRead.html +19 -0
  21. data/data/fonts/Symbol.afm +213 -0
  22. data/data/fonts/Times-Bold.afm +2588 -0
  23. data/data/fonts/Times-BoldItalic.afm +2384 -0
  24. data/data/fonts/Times-Italic.afm +2667 -0
  25. data/data/fonts/Times-Roman.afm +2419 -0
  26. data/data/fonts/ZapfDingbats.afm +225 -0
  27. data/data/fonts/comicsans.ttf +0 -0
  28. data/data/fonts/gkai00mp.ttf +0 -0
  29. data/data/images/16bit.alpha +0 -0
  30. data/data/images/16bit.dat +0 -0
  31. data/data/images/16bit.png +0 -0
  32. data/data/images/arrow.png +0 -0
  33. data/data/images/arrow2.png +0 -0
  34. data/data/images/barcode_issue.png +0 -0
  35. data/data/images/dice.alpha +0 -0
  36. data/data/images/dice.dat +0 -0
  37. data/data/images/dice.png +0 -0
  38. data/data/images/dice_interlaced.png +0 -0
  39. data/data/images/fractal.jpg +0 -0
  40. data/data/images/letterhead.jpg +0 -0
  41. data/data/images/page_white_text.alpha +0 -0
  42. data/data/images/page_white_text.dat +0 -0
  43. data/data/images/page_white_text.png +0 -0
  44. data/data/images/pigs.jpg +0 -0
  45. data/data/images/rails.dat +0 -0
  46. data/data/images/rails.png +0 -0
  47. data/data/images/ruport.png +0 -0
  48. data/data/images/ruport_data.dat +0 -0
  49. data/data/images/ruport_transparent.png +0 -0
  50. data/data/images/ruport_type0.png +0 -0
  51. data/data/images/stef.jpg +0 -0
  52. data/data/images/tru256.bmp +0 -0
  53. data/data/images/web-links.dat +1 -0
  54. data/data/images/web-links.png +0 -0
  55. data/data/pdfs/complex_template.pdf +0 -0
  56. data/data/pdfs/contains_ttf_font.pdf +0 -0
  57. data/data/pdfs/encrypted.pdf +0 -0
  58. data/data/pdfs/hexagon.pdf +61 -0
  59. data/data/pdfs/indirect_reference.pdf +86 -0
  60. data/data/pdfs/nested_pages.pdf +118 -0
  61. data/data/pdfs/resources_as_indirect_object.pdf +83 -0
  62. data/data/pdfs/two_hexagons.pdf +90 -0
  63. data/data/pdfs/version_1_6.pdf +61 -0
  64. data/data/shift_jis_text.txt +1 -0
  65. data/examples/bounding_box/bounding_boxes.rb +43 -0
  66. data/examples/bounding_box/indentation.rb +34 -0
  67. data/examples/bounding_box/russian_boxes.rb +36 -0
  68. data/examples/bounding_box/stretched_nesting.rb +67 -0
  69. data/examples/builder/simple.rb +28 -0
  70. data/examples/example_helper.rb +4 -0
  71. data/examples/general/background.rb +23 -0
  72. data/examples/general/canvas.rb +15 -0
  73. data/examples/general/context_sensitive_headers.rb +37 -0
  74. data/examples/general/float.rb +11 -0
  75. data/examples/general/margin.rb +36 -0
  76. data/examples/general/measurement_units.rb +51 -0
  77. data/examples/general/metadata-info.rb +16 -0
  78. data/examples/general/multi_page_layout.rb +18 -0
  79. data/examples/general/outlines.rb +50 -0
  80. data/examples/general/page_geometry.rb +31 -0
  81. data/examples/general/page_numbering.rb +15 -0
  82. data/examples/general/repeaters.rb +47 -0
  83. data/examples/general/stamp.rb +41 -0
  84. data/examples/general/templates.rb +13 -0
  85. data/examples/graphics/basic_images.rb +23 -0
  86. data/examples/graphics/chunkable.rb +38 -0
  87. data/examples/graphics/cmyk.rb +12 -0
  88. data/examples/graphics/curves.rb +11 -0
  89. data/examples/graphics/hexagon.rb +13 -0
  90. data/examples/graphics/image_fit.rb +15 -0
  91. data/examples/graphics/image_flow.rb +37 -0
  92. data/examples/graphics/image_position.rb +17 -0
  93. data/examples/graphics/line.rb +32 -0
  94. data/examples/graphics/png_types.rb +22 -0
  95. data/examples/graphics/polygons.rb +16 -0
  96. data/examples/graphics/remote_images.rb +12 -0
  97. data/examples/graphics/rounded_polygons.rb +19 -0
  98. data/examples/graphics/rounded_rectangle.rb +20 -0
  99. data/examples/graphics/ruport_style_helpers.rb +19 -0
  100. data/examples/graphics/stroke_bounds.rb +20 -0
  101. data/examples/graphics/stroke_cap_and_join.rb +45 -0
  102. data/examples/graphics/stroke_dash.rb +42 -0
  103. data/examples/graphics/transformations.rb +52 -0
  104. data/examples/graphics/transparency.rb +26 -0
  105. data/examples/m17n/chinese_text_wrapping.rb +17 -0
  106. data/examples/m17n/euro.rb +15 -0
  107. data/examples/m17n/sjis.rb +28 -0
  108. data/examples/m17n/utf8.rb +13 -0
  109. data/examples/m17n/win_ansi_charset.rb +54 -0
  110. data/examples/security/hello_foo.rb +8 -0
  111. data/examples/table/bill.rb +53 -0
  112. data/examples/table/cell.rb +12 -0
  113. data/examples/table/checkerboard.rb +22 -0
  114. data/examples/table/header.rb +14 -0
  115. data/examples/table/inline_format_table.rb +12 -0
  116. data/examples/table/multi_page_table.rb +9 -0
  117. data/examples/table/simple_table.rb +24 -0
  118. data/examples/table/subtable.rb +12 -0
  119. data/examples/table/widths.rb +20 -0
  120. data/examples/text/alignment.rb +18 -0
  121. data/examples/text/character_spacing.rb +12 -0
  122. data/examples/text/dfont.rb +48 -0
  123. data/examples/text/family_based_styling.rb +24 -0
  124. data/examples/text/font_calculations.rb +91 -0
  125. data/examples/text/font_size.rb +33 -0
  126. data/examples/text/hyphenation.rb +45 -0
  127. data/examples/text/indent_paragraphs.rb +22 -0
  128. data/examples/text/inline_format.rb +103 -0
  129. data/examples/text/kerning.rb +30 -0
  130. data/examples/text/rotated.rb +98 -0
  131. data/examples/text/shaped_text_box.rb +31 -0
  132. data/examples/text/simple_text.rb +17 -0
  133. data/examples/text/simple_text_ttf.rb +17 -0
  134. data/examples/text/text_box.rb +88 -0
  135. data/examples/text/text_box_returning_excess.rb +51 -0
  136. data/examples/text/text_flow.rb +67 -0
  137. data/lib/prawn.rb +27 -0
  138. data/lib/prawn/canvas.rb +119 -0
  139. data/lib/prawn/chunkable.rb +37 -0
  140. data/lib/prawn/compatibility.rb +51 -0
  141. data/lib/prawn/core.rb +85 -0
  142. data/lib/prawn/core/annotations.rb +61 -0
  143. data/lib/prawn/core/byte_string.rb +9 -0
  144. data/lib/prawn/core/chunk.rb +36 -0
  145. data/lib/prawn/core/destinations.rb +90 -0
  146. data/lib/prawn/core/document_state.rb +78 -0
  147. data/lib/prawn/core/literal_string.rb +16 -0
  148. data/lib/prawn/core/name_tree.rb +165 -0
  149. data/lib/prawn/core/object_store.rb +236 -0
  150. data/lib/prawn/core/page.rb +179 -0
  151. data/lib/prawn/core/pdf_object.rb +108 -0
  152. data/lib/prawn/core/reference.rb +112 -0
  153. data/lib/prawn/core/text.rb +140 -0
  154. data/lib/prawn/core/text/formatted/arranger.rb +266 -0
  155. data/lib/prawn/core/text/formatted/line_wrap.rb +127 -0
  156. data/lib/prawn/core/text/formatted/wrap.rb +112 -0
  157. data/lib/prawn/core/text/line_wrap.rb +209 -0
  158. data/lib/prawn/core/text/wrap.rb +80 -0
  159. data/lib/prawn/document.rb +573 -0
  160. data/lib/prawn/document/bounding_box.rb +425 -0
  161. data/lib/prawn/document/graphics_state.rb +48 -0
  162. data/lib/prawn/document/internals.rb +170 -0
  163. data/lib/prawn/document/page_geometry.rb +136 -0
  164. data/lib/prawn/document/snapshot.rb +87 -0
  165. data/lib/prawn/document_builder.rb +51 -0
  166. data/lib/prawn/document_builder/command.rb +38 -0
  167. data/lib/prawn/document_builder/constructs.rb +2 -0
  168. data/lib/prawn/document_builder/constructs/flowing_text_construct.rb +18 -0
  169. data/lib/prawn/document_builder/constructs/path_construct.rb +9 -0
  170. data/lib/prawn/document_builder/layout.rb +25 -0
  171. data/lib/prawn/document_builder/modifications.rb +2 -0
  172. data/lib/prawn/document_builder/modifications/layout_modification.rb +9 -0
  173. data/lib/prawn/document_builder/modifications/path_modification.rb +9 -0
  174. data/lib/prawn/encoding.rb +121 -0
  175. data/lib/prawn/errors.rb +94 -0
  176. data/lib/prawn/font.rb +341 -0
  177. data/lib/prawn/font/afm.rb +225 -0
  178. data/lib/prawn/font/dfont.rb +42 -0
  179. data/lib/prawn/font/ttf.rb +350 -0
  180. data/lib/prawn/graphics.rb +325 -0
  181. data/lib/prawn/graphics/cap_style.rb +38 -0
  182. data/lib/prawn/graphics/color.rb +205 -0
  183. data/lib/prawn/graphics/dash.rb +71 -0
  184. data/lib/prawn/graphics/join_style.rb +38 -0
  185. data/lib/prawn/graphics/transformation.rb +156 -0
  186. data/lib/prawn/graphics/transparency.rb +99 -0
  187. data/lib/prawn/images.rb +348 -0
  188. data/lib/prawn/images/jpg.rb +46 -0
  189. data/lib/prawn/images/png.rb +226 -0
  190. data/lib/prawn/measurement_extensions.rb +46 -0
  191. data/lib/prawn/measurements.rb +71 -0
  192. data/lib/prawn/outline.rb +278 -0
  193. data/lib/prawn/repeater.rb +129 -0
  194. data/lib/prawn/security.rb +262 -0
  195. data/lib/prawn/security/arcfour.rb +51 -0
  196. data/lib/prawn/stamp.rb +126 -0
  197. data/lib/prawn/table.rb +421 -0
  198. data/lib/prawn/table/accessors.rb +180 -0
  199. data/lib/prawn/table/cell.rb +350 -0
  200. data/lib/prawn/table/cell/in_table.rb +27 -0
  201. data/lib/prawn/table/cell/subtable.rb +65 -0
  202. data/lib/prawn/table/cell/text.rb +125 -0
  203. data/lib/prawn/text.rb +449 -0
  204. data/lib/prawn/text/box.rb +392 -0
  205. data/lib/prawn/text/formatted.rb +4 -0
  206. data/lib/prawn/text/formatted/box.rb +228 -0
  207. data/lib/prawn/text/formatted/fragment.rb +181 -0
  208. data/lib/prawn/text/formatted/parser.rb +213 -0
  209. data/spec/annotations_spec.rb +90 -0
  210. data/spec/bounding_box_spec.rb +190 -0
  211. data/spec/cell_spec.rb +348 -0
  212. data/spec/destinations_spec.rb +15 -0
  213. data/spec/document_spec.rb +473 -0
  214. data/spec/font_spec.rb +324 -0
  215. data/spec/formatted_text_arranger_spec.rb +426 -0
  216. data/spec/formatted_text_box_spec.rb +756 -0
  217. data/spec/formatted_text_fragment_spec.rb +211 -0
  218. data/spec/graphics_spec.rb +446 -0
  219. data/spec/images_spec.rb +96 -0
  220. data/spec/inline_formatted_text_parser_spec.rb +502 -0
  221. data/spec/jpg_spec.rb +25 -0
  222. data/spec/line_wrap_spec.rb +341 -0
  223. data/spec/measurement_units_spec.rb +23 -0
  224. data/spec/name_tree_spec.rb +112 -0
  225. data/spec/object_store_spec.rb +160 -0
  226. data/spec/outline_spec.rb +269 -0
  227. data/spec/pdf_object_spec.rb +170 -0
  228. data/spec/png_spec.rb +237 -0
  229. data/spec/reference_spec.rb +82 -0
  230. data/spec/repeater_spec.rb +96 -0
  231. data/spec/security_spec.rb +120 -0
  232. data/spec/snapshot_spec.rb +138 -0
  233. data/spec/spec_helper.rb +26 -0
  234. data/spec/stamp_spec.rb +108 -0
  235. data/spec/stroke_styles_spec.rb +163 -0
  236. data/spec/table_spec.rb +598 -0
  237. data/spec/template_spec.rb +158 -0
  238. data/spec/text_at_spec.rb +119 -0
  239. data/spec/text_box_spec.rb +742 -0
  240. data/spec/text_spacing_spec.rb +75 -0
  241. data/spec/text_spec.rb +333 -0
  242. data/spec/text_with_inline_formatting_spec.rb +193 -0
  243. data/spec/transparency_spec.rb +89 -0
  244. metadata +331 -0
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+
3
+ # core/text/formatted/line_wrap.rb : Implements individual line wrapping of
4
+ # formatted text
5
+ #
6
+ # Copyright February 2010, Daniel Nelson. All Rights Reserved.
7
+ #
8
+ # This is free software. Please see the LICENSE and COPYING files for details.
9
+ #
10
+
11
+ module Prawn
12
+ module Core
13
+ module Text
14
+ module Formatted #:nodoc:
15
+
16
+ class LineWrap < Prawn::Core::Text::LineWrap #:nodoc:
17
+
18
+ # Work in conjunction with the Prawn::Core::Formatted::Arranger
19
+ # defined in the :arranger option to determine what formatted text
20
+ # will fit within the width defined by the :width option
21
+ #
22
+ def wrap_line(options)
23
+ initialize_line(options)
24
+
25
+ while fragment = @arranger.next_string
26
+ @output = ""
27
+ preview = @arranger.preview_next_string
28
+
29
+ fragment.lstrip! if @line_output.empty? && fragment != "\n"
30
+ if @line_output.empty? && fragment.empty? && preview == "\n"
31
+ # this line was just whitespace followed by a newline, which is
32
+ # equivalent to just a newline
33
+ @arranger.update_last_string("", "")
34
+ next
35
+ end
36
+
37
+ if !add_fragment_to_line(fragment)
38
+ fragment_finished(fragment, true)
39
+ break
40
+ end
41
+
42
+ fragment_finished(fragment, preview == "\n" || preview.nil?)
43
+ end
44
+
45
+ @arranger.finalize_line
46
+ @accumulated_width = @arranger.line_width
47
+ @space_count = @arranger.space_count
48
+ @arranger.line
49
+ end
50
+
51
+ private
52
+
53
+ def initialize_line(options)
54
+ @document = options[:document]
55
+ @kerning = options[:kerning]
56
+ @width = options[:width]
57
+
58
+ @scan_pattern = scan_pattern
59
+ @word_division_scan_pattern = word_division_scan_pattern
60
+
61
+ @accumulated_width = 0
62
+ @line_output = ""
63
+
64
+ @arranger = options[:arranger]
65
+ @arranger.initialize_line
66
+ end
67
+
68
+ def fragment_finished(fragment, finished_line)
69
+ if fragment == "\n"
70
+ @line_output = "\n" if @line_output.empty?
71
+ else
72
+ update_output_based_on_last_fragment(fragment, finished_line)
73
+ @line_output += @output
74
+ end
75
+ end
76
+
77
+ def update_output_based_on_last_fragment(fragment, finished_line)
78
+ remaining_text = fragment.slice(@output.length..fragment.length)
79
+ @output.rstrip! if finished_line
80
+ raise Errors::CannotFit if finished_line && @line_output.empty? &&
81
+ @output.empty? && !fragment.strip.empty?
82
+ @arranger.update_last_string(@output, remaining_text)
83
+ end
84
+
85
+ # returns true iff all text was printed without running into the end of
86
+ # the line
87
+ #
88
+ def add_fragment_to_line(fragment)
89
+ return true if fragment == ""
90
+ return false if fragment == "\n"
91
+ previous_segment = nil
92
+ fragment.scan(@scan_pattern).each do |segment|
93
+ @arranger.apply_font_settings do
94
+ segment_width = @document.width_of(segment, :kerning => @kerning)
95
+
96
+ if @accumulated_width + segment_width <= @width
97
+ @accumulated_width += segment_width
98
+ @output += segment
99
+ else
100
+ end_of_the_line(segment)
101
+ return false
102
+ end
103
+ end
104
+ previous_segment = segment
105
+ end
106
+ true
107
+ end
108
+
109
+ # If there is more than one word on the line, then clean up the last
110
+ # word on the line; otherwise, wrap by character
111
+ #
112
+ def end_of_the_line(segment)
113
+ if (@line_output + @output) =~ @word_division_scan_pattern
114
+ if segment =~ new_regexp("^#{hyphen}") &&
115
+ @output !~ new_regexp("[#{break_chars}]$")
116
+ remove_last_output_word
117
+ end
118
+ else
119
+ wrap_by_char(segment)
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,112 @@
1
+ require "prawn/core/text/formatted/line_wrap"
2
+ require "prawn/core/text/formatted/arranger"
3
+
4
+ module Prawn
5
+ module Core
6
+ module Text
7
+ module Formatted #:nodoc:
8
+ module Wrap
9
+
10
+ def initialize(array, options)
11
+ super(array, options)
12
+ @line_wrap = Prawn::Core::Text::Formatted::LineWrap.new
13
+ @arranger = Prawn::Core::Text::Formatted::Arranger.new(@document)
14
+ end
15
+
16
+
17
+ # See the developer documentation for Prawn::Core::Text#wrap
18
+ #
19
+ # Formatted#wrap should set some of the variables slightly differently
20
+ # than Text#wrap;
21
+ # <tt>@line_height</tt>::
22
+ # the height of the tallest fragment in the last printed line
23
+ # <tt>@descender</tt>::
24
+ # the descender height of the tallest fragment in the last
25
+ # printed line
26
+ # <tt>@ascender</tt>::
27
+ # the ascender heigth of the tallest fragment in the last
28
+ # printed line
29
+ #
30
+ # Returns any formatted text that was not printed
31
+ #
32
+ def wrap(array) #:nodoc:
33
+ initialize_wrap(array)
34
+
35
+ move_baseline = true
36
+ while @arranger.unfinished?
37
+ printed_fragments = []
38
+
39
+ line_to_print = @line_wrap.wrap_line(:document => @document,
40
+ :kerning => @kerning,
41
+ :width => available_width,
42
+ :arranger => @arranger)
43
+
44
+ move_baseline = false
45
+ break unless enough_height_for_this_line?
46
+ move_baseline_down
47
+
48
+ accumulated_width = 0
49
+ word_spacing = word_spacing_for_this_line
50
+ while fragment = @arranger.retrieve_fragment
51
+ fragment.word_spacing = word_spacing
52
+ if fragment.text == "\n"
53
+ printed_fragments << "\n" if @printed_lines.last == ""
54
+ break
55
+ end
56
+ printed_fragments << fragment.text
57
+ format_and_draw_fragment(fragment, accumulated_width,
58
+ @line_wrap.width, word_spacing)
59
+ accumulated_width += fragment.width
60
+ end
61
+ @printed_lines << printed_fragments.join("")
62
+ break if @single_line
63
+ move_baseline = true unless @arranger.finished?
64
+ end
65
+ move_baseline_down if move_baseline
66
+ @text = @printed_lines.join("\n")
67
+
68
+ @arranger.unconsumed
69
+ end
70
+
71
+ private
72
+
73
+ def enough_height_for_this_line?
74
+ @line_height = @arranger.max_line_height
75
+ @descender = @arranger.max_descender
76
+ @ascender = @arranger.max_ascender
77
+ required_height = @baseline_y == 0 ? @line_height : @line_height + @descender
78
+ if @baseline_y.abs + required_height > @height
79
+ # no room for the full height of this line
80
+ @arranger.repack_unretrieved
81
+ false
82
+ else
83
+ true
84
+ end
85
+ end
86
+
87
+ def initialize_wrap(array)
88
+ @text = nil
89
+ @arranger.format_array = array
90
+
91
+ # these values will depend on the maximum value within a given line
92
+ @line_height = 0
93
+ @descender = 0
94
+ @ascender = 0
95
+ @baseline_y = 0
96
+
97
+ @printed_lines = []
98
+ end
99
+
100
+ def format_and_draw_fragment(fragment, accumulated_width,
101
+ line_width, word_spacing)
102
+ @arranger.apply_color_and_font_settings(fragment) do
103
+ draw_fragment(fragment, accumulated_width,
104
+ line_width, word_spacing)
105
+ end
106
+ end
107
+
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,209 @@
1
+ # encoding: utf-8
2
+
3
+ # core/text/line_wrap.rb : Implements individual line wrapping
4
+ #
5
+ # Copyright January 2010, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ module Core
12
+ module Text
13
+
14
+ class LineWrap #:nodoc:
15
+
16
+ # The width of the last wrapped line
17
+ #
18
+ def width
19
+ @accumulated_width || 0
20
+ end
21
+
22
+ # The number of spaces in the last wrapped line
23
+ #
24
+ def space_count
25
+ @space_count
26
+ end
27
+
28
+ # The number of characters consumed from the last line passed into
29
+ # wrap_line. This may differ from the number of characters in the
30
+ # returned line because trailing white spaces are removed
31
+ #
32
+ def consumed_char_count
33
+ @consumed_char_count
34
+ end
35
+
36
+ # The pattern used to determine chunks of text to place on a given line
37
+ #
38
+ def scan_pattern
39
+ pattern = "[^#{break_chars}]+#{soft_hyphen}|" +
40
+ "[^#{break_chars}]+#{hyphen}+|" +
41
+ "[^#{break_chars}]+|" +
42
+ "[#{whitespace}]+|" +
43
+ "#{hyphen}+[^#{break_chars}]*|" +
44
+ "#{soft_hyphen}"
45
+ new_regexp(pattern)
46
+ end
47
+
48
+ # The pattern used to determine whether any word breaks exist on a
49
+ # current line, which in turn determines whether character level
50
+ # word breaking is needed
51
+ #
52
+ def word_division_scan_pattern
53
+ new_regexp("\\s|[#{hyphen}#{soft_hyphen}]")
54
+ end
55
+
56
+ # Take a single line and deterimine what part of it fits within the
57
+ # width defined by the :width option
58
+ #
59
+ def wrap_line(line, options)
60
+ initialize_line(options)
61
+
62
+ previous_segment = nil
63
+ whitespace_pattern = new_regexp("[#{whitespace}]")
64
+
65
+ line.scan(@scan_pattern).each do |segment|
66
+
67
+ # Don't let leading white space count against available space
68
+ if @output.empty? && segment =~ whitespace_pattern
69
+ @discarded_char_count += segment.length
70
+ next
71
+ end
72
+
73
+ segment_width = @document.width_of(segment, :kerning => @kerning)
74
+
75
+ if @accumulated_width + segment_width <= @width
76
+ @accumulated_width += segment_width
77
+ @output += segment
78
+ else
79
+ end_of_the_line(segment)
80
+ break
81
+ end
82
+ previous_segment = segment
83
+ end
84
+ raise Errors::CannotFit if @output.empty? && !line.strip.empty?
85
+
86
+ finalize_line
87
+
88
+ @space_count = @output.count(" ")
89
+ @output
90
+ end
91
+
92
+ private
93
+
94
+ def initialize_line(options)
95
+ @document = options[:document]
96
+ @kerning = options[:kerning]
97
+ @width = options[:width]
98
+
99
+ @scan_pattern = scan_pattern
100
+ @word_division_scan_pattern = word_division_scan_pattern
101
+
102
+ @discarded_char_count = 0
103
+ @accumulated_width = 0
104
+ @output = ""
105
+ end
106
+
107
+ def break_chars
108
+ "#{whitespace}#{soft_hyphen}#{hyphen}"
109
+ end
110
+
111
+ def whitespace
112
+ " \\t"
113
+ end
114
+
115
+ def hyphen
116
+ "-"
117
+ end
118
+
119
+ def soft_hyphen
120
+ @document.font.normalize_encoding("­")
121
+ end
122
+
123
+ # If there is more than one word on the line, then clean up the last
124
+ # word on the line; otherwise, wrap by character
125
+ #
126
+ def end_of_the_line(segment)
127
+ if @output =~ @word_division_scan_pattern
128
+ if segment =~ new_regexp("^#{hyphen}") &&
129
+ @output !~ new_regexp("[#{break_chars}]$")
130
+ remove_last_output_word
131
+ end
132
+ else
133
+ wrap_by_char(segment)
134
+ # since when adding characters individually, kerning has no effect,
135
+ # recompute width of the output after adding characters
136
+ # IMPORTANT: leave this computation here, rather than moving it into
137
+ # append_char because append_char is more general than
138
+ # end_of_the_line, so while end_of_the_line is likely to be
139
+ # overridden, wrap_by_char may not be, and we don't want to
140
+ # recompute line width except where necessary (for example, see
141
+ # Prawn::Core::Text::Formatted::LineWrap)
142
+ @accumulated_width = compute_output_width
143
+ end
144
+ end
145
+
146
+ def remove_last_output_word
147
+ segments = []
148
+ regexp = new_regexp("[^#{break_chars}]+|[#{break_chars}]+")
149
+ @output.scan(regexp).each { |segment| segments << segment }
150
+ segments.pop
151
+ @output = segments.join("")
152
+ end
153
+
154
+ def finalize_line
155
+ @consumed_char_count = @output.length + @discarded_char_count
156
+
157
+ @output = @output[0..-2].gsub(soft_hyphen, "") + @output[-1..-1]
158
+
159
+ strip_trailing_whitespace
160
+ end
161
+
162
+ def strip_trailing_whitespace
163
+ @accumulated_width = compute_output_width if @output.strip!
164
+ end
165
+
166
+ def compute_output_width
167
+ @document.width_of(@output, :kerning => @kerning)
168
+ end
169
+
170
+ def wrap_by_char(segment)
171
+ if @document.font.unicode?
172
+ segment.unpack("U*").each do |char_int|
173
+ break unless append_char([char_int].pack("U"))
174
+ end
175
+ else
176
+ segment.each_char do |char|
177
+ break unless append_char(char)
178
+ end
179
+ end
180
+ end
181
+
182
+ def append_char(char)
183
+ # kerning doesn't make sense in the context of a single character
184
+ char_width = @document.width_of(char)
185
+ @accumulated_width += char_width
186
+
187
+ if @accumulated_width >= @width
188
+ false
189
+ else
190
+ @output << char
191
+ true
192
+ end
193
+ end
194
+
195
+ def new_regexp(pattern)
196
+ regexp = ruby_19 {
197
+ Regexp.new(pattern)
198
+ }
199
+ regexp = regexp || ruby_18 {
200
+ lang = @document.font.unicode? ? 'U' : 'N'
201
+ Regexp.new(pattern, 0, lang)
202
+ }
203
+ regexp
204
+ end
205
+
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,80 @@
1
+ require "prawn/core/text/line_wrap"
2
+
3
+ module Prawn
4
+ module Core
5
+ module Text
6
+ module Wrap #:nodoc:
7
+
8
+ def initialize(text, options)
9
+ @line_wrap = Prawn::Core::Text::LineWrap.new
10
+ end
11
+
12
+ # #wrap is part of the developer API. Override it in extensions to Prawn
13
+ # that inherit Text::Box but need a different placement algorithm.
14
+ # #wrap is where the actual placement of text happens. If @inked is
15
+ # false, then all the placement computations should be performed, and
16
+ # unprinted text returned, but no text should actually be drawn to the
17
+ # PDF. This enables look-ahead computations that need to know whether all
18
+ # the text was printed under a set of conditions or how tall the text was
19
+ # under certain conditions.
20
+ #
21
+ # #wrap is called from several places within box.rb and relies on
22
+ # certain conditions established by render. Do not call #wrap from
23
+ # outside of Text::Box or its descendants.
24
+ #
25
+ # #wrap should set the following instance variables:
26
+ # <tt>@text</tt>:: the text that was printed
27
+ # <tt>@line_height</tt>:: the height of the last printed line
28
+ # <tt>@descender</tt>:: the descender height of the last printed line
29
+ # <tt>@ascender</tt>:: the ascender heigth of the last printed line
30
+ # <tt>@baseline_y</tt>:: the base line of the last printed line
31
+ #
32
+ # Returns any unprinted text
33
+ #
34
+ def wrap(text) #:nodoc:
35
+ @text = nil
36
+ remaining_text = text
37
+ @line_height = @document.font.height
38
+ @descender = @document.font.descender
39
+ @ascender = @document.font.ascender
40
+ @baseline_y = -@ascender
41
+
42
+ printed_lines = []
43
+
44
+ while remaining_text &&
45
+ remaining_text.length > 0 &&
46
+ @baseline_y.abs + @descender <= @height
47
+ line_to_print = @line_wrap.wrap_line(remaining_text.first_line,
48
+ :document => @document,
49
+ :kerning => @kerning,
50
+ :width => available_width)
51
+
52
+ remaining_text = remaining_text.slice(@line_wrap.consumed_char_count..
53
+ remaining_text.length)
54
+ include_ellipses = (@overflow == :ellipses && last_line? &&
55
+ remaining_text.length > 0)
56
+ printed_lines << draw_line(line_to_print, @line_wrap.width,
57
+ word_spacing_for_this_line, include_ellipses)
58
+ @baseline_y -= (@line_height + @leading)
59
+ break if @single_line
60
+ end
61
+
62
+ @text = printed_lines.join("\n")
63
+
64
+ remaining_text
65
+ end
66
+
67
+ private
68
+
69
+ def word_spacing_for_this_line
70
+ if @align != :justify || @line_wrap.width.to_f / available_width.to_f < 0.75
71
+ 0
72
+ else
73
+ (available_width - @line_wrap.width) / @line_wrap.space_count
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ end