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,4 @@
1
+ require "prawn/core/text/formatted/wrap"
2
+ require "prawn/text/formatted/box"
3
+ require "prawn/text/formatted/parser"
4
+ require "prawn/text/formatted/fragment"
@@ -0,0 +1,228 @@
1
+ # encoding: utf-8
2
+
3
+ # text/formatted/rectangle.rb : Implements text boxes with formatted text
4
+ #
5
+ # Copyright February 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 Text
12
+ module Formatted
13
+
14
+ # Draws the requested formatted text into a box. When the text overflows
15
+ # the rectangle shrink to fit or truncate the text. Text boxes are
16
+ # independent of the document y position.
17
+ #
18
+ # == Formatted Text Array
19
+ #
20
+ # Formatted text is comprised of an array of hashes, where each hash
21
+ # defines text and format information. As of the time of writing, the
22
+ # following hash options are supported:
23
+ #
24
+ # <tt>:text</tt>::
25
+ # the text to format according to the other hash options
26
+ # <tt>:styles</tt>::
27
+ # an array of styles to apply to this text. Available styles include
28
+ # :bold, :italic, :underline, :strikethrough, :subscript, and
29
+ # :superscript
30
+ # <tt>:size</tt>::
31
+ # a number denoting the font size to apply to this text
32
+ # <tt>:character_spacing</tt>::
33
+ # a number denoting how much to increase or decrease the default
34
+ # spacing between characters
35
+ # <tt>:font</tt>::
36
+ # the name of a font. The name must be an AFM font with the desired
37
+ # faces or must be a font that is already registered using
38
+ # Prawn::Document#font_families
39
+ # <tt>:color</tt>::
40
+ # anything compatible with Prawn::Graphics::Color#fill_color and
41
+ # Prawn::Graphics::Color#stroke_color
42
+ # <tt>:link</tt>::
43
+ # a URL to which to create a link. A clickable link will be created
44
+ # to that URL. Note that you must explicitly underline and color using
45
+ # the appropriate tags if you which to draw attention to the link
46
+ # <tt>:anchor</tt>::
47
+ # a destination that has already been or will be registered using
48
+ # Prawn::Core::Destinations#add_dest. A clickable link will be
49
+ # created to that destination. Note that you must explicitly underline
50
+ # and color using the appropriate tags if you which to draw attention
51
+ # to the link
52
+ # <tt>:callback</tt>::
53
+ # an object (or array of such objects) with two methods:
54
+ # #render_behind and #render_in_front, which are called immediately
55
+ # prior to and immediately after rendring the text fragment and which
56
+ # are passed the fragment as an argument
57
+ #
58
+ # == Example
59
+ #
60
+ # formatted_text_box([{ :text => "hello" },
61
+ # { :text => "world",
62
+ # :size => 24,
63
+ # :styles => [:bold, :italic] }])
64
+ #
65
+ # == Options
66
+ #
67
+ # Accepts the same options as Text::Box with the below exceptions
68
+ #
69
+ # <tt>:overflow</tt>::
70
+ # does not accept :ellipses
71
+ #
72
+ # == Returns
73
+ #
74
+ # Returns a formatted text array representing any text that did not print
75
+ # under the current settings.
76
+ #
77
+ # == Exceptions
78
+ #
79
+ # Raises "Bad font family" if no font family is defined for the current font
80
+ #
81
+ # Raises <tt>Prawn::Errrors::CannotFit</tt> if not wide enough to print
82
+ # any text
83
+ #
84
+ # Raises <tt>NotImplementedError</tt> if <tt>:ellipses</tt> <tt>overflow</tt>
85
+ # option included
86
+ #
87
+ def formatted_text_box(array, options)
88
+ Text::Formatted::Box.new(array, options.merge(:document => self)).render
89
+ end
90
+
91
+ # Generally, one would use the Prawn::Text::Formatted#formatted_text_box
92
+ # convenience method. However, using Text::Formatted::Box.new in
93
+ # conjunction with #render(:dry_run => true) enables one to do look-ahead
94
+ # calculations prior to placing text on the page, or to determine how much
95
+ # vertical space was consumed by the printed text
96
+ #
97
+ class Box < Prawn::Text::Box
98
+ include Prawn::Core::Text::Formatted::Wrap
99
+
100
+ def initialize(array, options={})
101
+ super(array, options)
102
+ if @overflow == :ellipses
103
+ raise NotImplementedError, "ellipses overflow unavailable with" +
104
+ "formatted box"
105
+ end
106
+ end
107
+
108
+ # The height actually used during the previous <tt>render</tt>
109
+ #
110
+ def height
111
+ return 0 if @baseline_y.nil? || @descender.nil?
112
+ @baseline_y.abs + @line_height - @ascender
113
+ end
114
+
115
+ # <tt>fragment</tt> is a Prawn::Text::Formatted::Fragment object
116
+ #
117
+ def draw_fragment(fragment, accumulated_width=0, line_width=0, word_spacing=0) #:nodoc:
118
+ case(@align)
119
+ when :left, :justify
120
+ x = @at[0]
121
+ when :center
122
+ x = @at[0] + @width * 0.5 - line_width * 0.5
123
+ when :right
124
+ x = @at[0] + @width - line_width
125
+ end
126
+
127
+ x += accumulated_width
128
+
129
+ y = @at[1] + @baseline_y
130
+
131
+ y += fragment.y_offset
132
+
133
+ fragment.left = x
134
+ fragment.baseline = y
135
+
136
+ draw_fragment_underlays(fragment)
137
+
138
+ if @inked
139
+ if @align == :justify
140
+ @document.word_spacing(word_spacing) {
141
+ @document.draw_text!(fragment.text, :at => [x, y],
142
+ :kerning => @kerning)
143
+ }
144
+ else
145
+ @document.draw_text!(fragment.text, :at => [x, y],
146
+ :kerning => @kerning)
147
+ end
148
+
149
+ draw_fragment_overlays(fragment)
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def original_text
156
+ @original_array.collect { |hash| hash.dup }
157
+ end
158
+
159
+ def original_text=(array)
160
+ @original_array = array
161
+ end
162
+
163
+ def normalize_encoding
164
+ array = original_text
165
+ array.each do |hash|
166
+ hash[:text] = @document.font.normalize_encoding(hash[:text])
167
+ end
168
+ array
169
+ end
170
+
171
+ def move_baseline_down
172
+ if @baseline_y == 0
173
+ @baseline_y = -@ascender
174
+ else
175
+ @baseline_y -= (@line_height + @leading)
176
+ end
177
+ end
178
+
179
+ def draw_fragment_underlays(fragment)
180
+ fragment.callback_objects.each do |obj|
181
+ obj.render_behind(fragment) if obj.respond_to?(:render_behind)
182
+ end
183
+ end
184
+
185
+ def draw_fragment_overlays(fragment)
186
+ draw_fragment_overlay_styles(fragment)
187
+ draw_fragment_overlay_link(fragment)
188
+ draw_fragment_overlay_anchor(fragment)
189
+ fragment.callback_objects.each do |obj|
190
+ obj.render_in_front(fragment) if obj.respond_to?(:render_in_front)
191
+ end
192
+ end
193
+
194
+ def draw_fragment_overlay_link(fragment)
195
+ return unless fragment.link
196
+ box = fragment.absolute_bounding_box
197
+ @document.link_annotation(box,
198
+ :Border => [0, 0, 0],
199
+ :A => { :Type => :Action,
200
+ :S => :URI,
201
+ :URI => Prawn::Core::LiteralString.new(fragment.link) })
202
+ end
203
+
204
+ def draw_fragment_overlay_anchor(fragment)
205
+ return unless fragment.anchor
206
+ box = fragment.absolute_bounding_box
207
+ @document.link_annotation(box,
208
+ :Border => [0, 0, 0],
209
+ :Dest => fragment.anchor)
210
+ end
211
+
212
+ def draw_fragment_overlay_styles(fragment)
213
+ underline = fragment.styles.include?(:underline)
214
+ if underline
215
+ @document.stroke_line(fragment.underline_points)
216
+ end
217
+
218
+ strikethrough = fragment.styles.include?(:strikethrough)
219
+ if strikethrough
220
+ @document.stroke_line(fragment.strikethrough_points)
221
+ end
222
+ end
223
+
224
+ end
225
+
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,181 @@
1
+ # encoding: utf-8
2
+
3
+ # text/formatted/fragment.rb : Implements information about a formatted fragment
4
+ #
5
+ # Copyright March 2010, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module Prawn
10
+ module Text
11
+ module Formatted
12
+
13
+ # Prawn::Text::Formatted::Fragment is a state store for a formatted text
14
+ # fragment. It does not render anything.
15
+ #
16
+ class Fragment
17
+
18
+ attr_reader :text, :format_state
19
+ attr_writer :width
20
+ attr_accessor :line_height, :descender, :ascender
21
+ attr_accessor :word_spacing, :left, :baseline
22
+
23
+ def initialize(text, format_state, document)
24
+ @text = text
25
+ @format_state = format_state
26
+ @document = document
27
+ @word_spacing = 0
28
+ end
29
+
30
+ def width
31
+ if @word_spacing == 0 then @width
32
+ else @width + @word_spacing * @text.count(" ")
33
+ end
34
+ end
35
+
36
+ def height
37
+ top - bottom
38
+ end
39
+
40
+ def subscript?
41
+ styles.include?(:subscript)
42
+ end
43
+
44
+ def superscript?
45
+ styles.include?(:superscript)
46
+ end
47
+
48
+ def y_offset
49
+ if subscript? then -descender
50
+ elsif superscript? then 0.85 * ascender
51
+ else 0
52
+ end
53
+ end
54
+
55
+ def bounding_box
56
+ [left, bottom, right, top]
57
+ end
58
+
59
+ def absolute_bounding_box
60
+ box = bounding_box
61
+ box[0] += @document.bounds.absolute_left
62
+ box[2] += @document.bounds.absolute_left
63
+ box[1] += @document.bounds.absolute_bottom
64
+ box[3] += @document.bounds.absolute_bottom
65
+ box
66
+ end
67
+
68
+ def underline_points
69
+ y = baseline - 1.25
70
+ [[left, y], [right, y]]
71
+ end
72
+
73
+ def strikethrough_points
74
+ y = baseline + ascender * 0.3
75
+ [[left, y], [right, y]]
76
+ end
77
+
78
+ def styles
79
+ @format_state[:styles] || []
80
+ end
81
+
82
+ def link
83
+ @format_state[:link]
84
+ end
85
+
86
+ def anchor
87
+ @format_state[:anchor]
88
+ end
89
+
90
+ def color
91
+ @format_state[:color]
92
+ end
93
+
94
+ def font
95
+ @format_state[:font]
96
+ end
97
+
98
+ def size
99
+ @format_state[:size]
100
+ end
101
+
102
+ def character_spacing
103
+ @format_state[:character_spacing] ||
104
+ @document.character_spacing
105
+ end
106
+
107
+ def callback_objects
108
+ callback = @format_state[:callback]
109
+ if callback.nil?
110
+ []
111
+ elsif callback.is_a?(Array)
112
+ callback
113
+ else
114
+ [callback]
115
+ end
116
+ end
117
+
118
+ def right
119
+ left + width
120
+ end
121
+
122
+ def top
123
+ baseline + ascender
124
+ end
125
+
126
+ def bottom
127
+ baseline - descender
128
+ end
129
+
130
+ def top_left
131
+ [left, top]
132
+ end
133
+
134
+ def top_right
135
+ [right, top]
136
+ end
137
+
138
+ def bottom_right
139
+ [right, bottom]
140
+ end
141
+
142
+ def bottom_left
143
+ [left, bottom]
144
+ end
145
+
146
+ def absolute_left
147
+ absolute_bounding_box[0]
148
+ end
149
+
150
+ def absolute_right
151
+ absolute_bounding_box[2]
152
+ end
153
+
154
+ def absolute_top
155
+ absolute_bounding_box[3]
156
+ end
157
+
158
+ def absolute_bottom
159
+ absolute_bounding_box[1]
160
+ end
161
+
162
+ def absolute_top_left
163
+ [absolute_left, absolute_top]
164
+ end
165
+
166
+ def absolute_top_right
167
+ [absolute_right, absolute_top]
168
+ end
169
+
170
+ def absolute_bottom_left
171
+ [absolute_left, absolute_bottom]
172
+ end
173
+
174
+ def absolute_bottom_right
175
+ [absolute_right, absolute_bottom]
176
+ end
177
+
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,213 @@
1
+ # encoding: utf-8
2
+
3
+ # text/formatted/parser.rb : Implements a bi-directional parser between a subset
4
+ # of html and formatted text arrays
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 Text
13
+ module Formatted
14
+
15
+ class Parser
16
+
17
+ def self.to_array(string)
18
+ regex_string = "\n|" +
19
+ "<b>|</b>|" +
20
+ "<i>|</i>|" +
21
+ "<u>|</u>|" +
22
+ "<strikethrough>|</strikethrough>|" +
23
+ "<sub>|</sub>|" +
24
+ "<sup>|</sup>|" +
25
+ "<link[^>]*>|</link>|" +
26
+ "<color[^>]*>|</color>|" +
27
+ "<font[^>]*>|</font>|" +
28
+ "<strong>|</strong>|" +
29
+ "<em>|</em>|" +
30
+ "<a[^>]*>|</a>|" +
31
+ "[^<\n]+"
32
+ regex = Regexp.new(regex_string, Regexp::MULTILINE)
33
+ tokens = string.scan(regex)
34
+ self.array_from_tokens(tokens)
35
+ end
36
+
37
+ def self.to_string(array)
38
+ prefixes = { :bold => "<b>",
39
+ :italic => "<i>",
40
+ :underline => "<u>",
41
+ :strikethrough => "<strikethrough>",
42
+ :subscript => "<sub>",
43
+ :superscript => "<sup>" }
44
+ suffixes = { :bold => "</b>",
45
+ :italic => "</i>",
46
+ :underline => "</u>",
47
+ :strikethrough => "</strikethrough>",
48
+ :subscript => "</sub>",
49
+ :superscript => "</sup>" }
50
+ array.collect do |hash|
51
+ prefix = ""
52
+ suffix = ""
53
+ if hash[:styles]
54
+ hash[:styles].each do |style|
55
+ prefix = prefix + prefixes[style]
56
+ suffix = suffixes[style] + suffix
57
+ end
58
+ end
59
+
60
+ font = hash[:font] ? " name='#{hash[:font]}'" : nil
61
+ size = hash[:size] ? " size='#{hash[:size]}'" : nil
62
+ if hash[:character_spacing]
63
+ character_spacing = " character_spacing='#{hash[:character_spacing]}'"
64
+ else
65
+ character_spacing = nil
66
+ end
67
+ if font || size || character_spacing
68
+ prefix = prefix + "<font#{font}#{size}#{character_spacing}>"
69
+ suffix = "</font>"
70
+ end
71
+
72
+ link = hash[:link] ? " href='#{hash[:link]}'" : nil
73
+ anchor = hash[:anchor] ? " anchor='#{hash[:anchor]}'" : nil
74
+ if link || anchor
75
+ prefix = prefix + "<link#{link}#{anchor}>"
76
+ suffix = "</link>"
77
+ end
78
+
79
+ if hash[:color]
80
+ if hash[:color].kind_of?(Array)
81
+ prefix = prefix + "<color c='#{hash[:color][0]}'" +
82
+ " m='#{hash[:color][1]}'" +
83
+ " y='#{hash[:color][2]}'" +
84
+ " k='#{hash[:color][3]}'>"
85
+ else
86
+ prefix = prefix + "<color rgb='#{hash[:color]}'>"
87
+ end
88
+ suffix = "</color>"
89
+ end
90
+
91
+ string = hash[:text].gsub("&", "&amp;").gsub(">", "&gt;").gsub("<", "&lt;")
92
+ prefix + string + suffix
93
+ end.join("")
94
+ end
95
+
96
+ def self.array_paragraphs(array) #:nodoc:
97
+ paragraphs = []
98
+ paragraph = []
99
+ previous_string = "\n"
100
+ scan_pattern = /[^\n]+|\n/
101
+ array.each do |hash|
102
+ hash[:text].scan(scan_pattern).each do |string|
103
+ if string == "\n"
104
+ paragraph << hash.dup.merge(:text => "\n") if previous_string == "\n"
105
+ paragraphs << paragraph unless paragraph.empty?
106
+ paragraph = []
107
+ else
108
+ paragraph << hash.dup.merge(:text => string)
109
+ end
110
+ previous_string = string
111
+ end
112
+ end
113
+ paragraphs << paragraph unless paragraph.empty?
114
+ end
115
+
116
+ private
117
+
118
+ def self.array_from_tokens(tokens)
119
+ array = []
120
+ styles = []
121
+ colors = []
122
+ link = nil
123
+ anchor = nil
124
+ fonts = []
125
+ sizes = []
126
+ character_spacings = []
127
+
128
+ while token = tokens.shift
129
+ case token
130
+ when "<b>", "<strong>"
131
+ styles << :bold
132
+ when "<i>", "<em>"
133
+ styles << :italic
134
+ when "<u>"
135
+ styles << :underline
136
+ when "<strikethrough>"
137
+ styles << :strikethrough
138
+ when "<sub>"
139
+ styles << :subscript
140
+ when "<sup>"
141
+ styles << :superscript
142
+ when "</b>", "</strong>"
143
+ styles.delete(:bold)
144
+ when "</i>", "</em>"
145
+ styles.delete(:italic)
146
+ when "</u>"
147
+ styles.delete(:underline)
148
+ when "</strikethrough>"
149
+ styles.delete(:strikethrough)
150
+ when "</sub>"
151
+ styles.delete(:subscript)
152
+ when "</sup>"
153
+ styles.delete(:superscript)
154
+ when "</link>", "</a>"
155
+ link = nil
156
+ anchor = nil
157
+ when "</color>"
158
+ colors.pop
159
+ when "</font>"
160
+ fonts.pop
161
+ sizes.pop
162
+ character_spacings.pop
163
+ else
164
+ if token =~ /^<link[^>]*>$/ or token =~ /^<a[^>]*>$/
165
+ matches = /href="([^"]*)"/.match(token) || /href='([^']*)'/.match(token)
166
+ link = matches[1] unless matches.nil?
167
+
168
+ matches = /anchor="([^"]*)"/.match(token) || /anchor='([^']*)'/.match(token)
169
+ anchor = matches[1] unless matches.nil?
170
+ elsif token =~ /^<color[^>]*>$/
171
+ matches = /rgb="#?([^"]*)"/.match(token) || /rgb='#?([^']*)'/.match(token)
172
+ colors << matches[1] if matches
173
+
174
+ matches = /c="#?([^"]*)" +m="#?([^"]*)" +y="#?([^"]*)" +k="#?([^"]*)"/.match(token) ||
175
+ /c='#?([^']*)' +m='#?([^']*)' +y='#?([^']*)' +k='#?([^']*)'/.match(token)
176
+ colors << [matches[1].to_i, matches[2].to_i, matches[3].to_i, matches[4].to_i] if matches
177
+
178
+ # intend to support rgb="#ffffff" or rgb='#ffffff',
179
+ # r="255" g="255" b="255" or r='255' g='255' b='255',
180
+ # and c="100" m="100" y="100" k="100" or
181
+ # c='100' m='100' y='100' k='100'
182
+ # color = { :rgb => "#ffffff" }
183
+ # color = { :r => 255, :g => 255, :b => 255 }
184
+ # color = { :c => 100, :m => 100, :y => 100, :k => 100 }
185
+ elsif token =~ /^<font[^>]*>$/
186
+ matches = /name="([^"]*)"/.match(token) || /name='([^']*)'/.match(token)
187
+ fonts << matches[1] unless matches.nil?
188
+
189
+ matches = /size="([^"]*)"/.match(token) || /size='([^']*)'/.match(token)
190
+ sizes << matches[1].to_f unless matches.nil?
191
+
192
+ matches = /character_spacing="([^"]*)"/.match(token) || /character_spacing='([^']*)'/.match(token)
193
+ character_spacings << matches[1].to_f unless matches.nil?
194
+ else
195
+ string = token.gsub("&lt;", "<").gsub("&gt;", ">").gsub("&amp;", "&")
196
+ array << { :text => string,
197
+ :styles => styles.dup,
198
+ :color => colors.last,
199
+ :link => link,
200
+ :anchor => anchor,
201
+ :font => fonts.last,
202
+ :size => sizes.last,
203
+ :character_spacing => character_spacings.last }
204
+ end
205
+ end
206
+ end
207
+ array
208
+ end
209
+
210
+ end
211
+ end
212
+ end
213
+ end