fullcirclegroup-prawn 0.2.99.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +56 -0
  3. data/README +47 -0
  4. data/Rakefile +76 -0
  5. data/data/fonts/Activa.ttf +0 -0
  6. data/data/fonts/Chalkboard.ttf +0 -0
  7. data/data/fonts/Courier-Bold.afm +342 -0
  8. data/data/fonts/Courier-BoldOblique.afm +342 -0
  9. data/data/fonts/Courier-Oblique.afm +342 -0
  10. data/data/fonts/Courier.afm +342 -0
  11. data/data/fonts/DejaVuSans.ttf +0 -0
  12. data/data/fonts/Dustismo_Roman.ttf +0 -0
  13. data/data/fonts/Helvetica-Bold.afm +2827 -0
  14. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  16. data/data/fonts/Helvetica.afm +3051 -0
  17. data/data/fonts/MustRead.html +19 -0
  18. data/data/fonts/Symbol.afm +213 -0
  19. data/data/fonts/Times-Bold.afm +2588 -0
  20. data/data/fonts/Times-BoldItalic.afm +2384 -0
  21. data/data/fonts/Times-Italic.afm +2667 -0
  22. data/data/fonts/Times-Roman.afm +2419 -0
  23. data/data/fonts/ZapfDingbats.afm +225 -0
  24. data/data/fonts/comicsans.ttf +0 -0
  25. data/data/fonts/gkai00mp.ttf +0 -0
  26. data/data/images/arrow.png +0 -0
  27. data/data/images/arrow2.png +0 -0
  28. data/data/images/barcode_issue.png +0 -0
  29. data/data/images/dice.alpha +0 -0
  30. data/data/images/dice.dat +0 -0
  31. data/data/images/dice.png +0 -0
  32. data/data/images/page_white_text.alpha +0 -0
  33. data/data/images/page_white_text.dat +0 -0
  34. data/data/images/page_white_text.png +0 -0
  35. data/data/images/pigs.jpg +0 -0
  36. data/data/images/rails.dat +0 -0
  37. data/data/images/rails.png +0 -0
  38. data/data/images/ruport.png +0 -0
  39. data/data/images/ruport_data.dat +0 -0
  40. data/data/images/ruport_transparent.png +0 -0
  41. data/data/images/ruport_type0.png +0 -0
  42. data/data/images/stef.jpg +0 -0
  43. data/data/images/web-links.dat +1 -0
  44. data/data/images/web-links.png +0 -0
  45. data/data/shift_jis_text.txt +1 -0
  46. data/examples/addressbook.csv +6 -0
  47. data/examples/alignment.rb +16 -0
  48. data/examples/bounding_boxes.rb +30 -0
  49. data/examples/canvas.rb +12 -0
  50. data/examples/cell.rb +38 -0
  51. data/examples/chinese_text_wrapping.rb +17 -0
  52. data/examples/currency.csv +1834 -0
  53. data/examples/curves.rb +10 -0
  54. data/examples/family_based_styling.rb +21 -0
  55. data/examples/fancy_table.rb +61 -0
  56. data/examples/flowing_text_with_header_and_footer.rb +72 -0
  57. data/examples/font_size.rb +27 -0
  58. data/examples/hexagon.rb +14 -0
  59. data/examples/image.rb +23 -0
  60. data/examples/image2.rb +13 -0
  61. data/examples/image_flow.rb +34 -0
  62. data/examples/kerning.rb +27 -0
  63. data/examples/lazy_bounding_boxes.rb +19 -0
  64. data/examples/line.rb +31 -0
  65. data/examples/multi_page_layout.rb +14 -0
  66. data/examples/page_geometry.rb +28 -0
  67. data/examples/png_types.rb +23 -0
  68. data/examples/polygons.rb +16 -0
  69. data/examples/position_by_baseline.rb +26 -0
  70. data/examples/ruport_formatter.rb +50 -0
  71. data/examples/ruport_helpers.rb +18 -0
  72. data/examples/russian_boxes.rb +34 -0
  73. data/examples/simple_text.rb +15 -0
  74. data/examples/simple_text_ttf.rb +16 -0
  75. data/examples/sjis.rb +21 -0
  76. data/examples/span.rb +27 -0
  77. data/examples/table.rb +47 -0
  78. data/examples/table_header_color.rb +16 -0
  79. data/examples/text_flow.rb +65 -0
  80. data/examples/top_and_bottom_cells.rb +40 -0
  81. data/examples/utf8.rb +12 -0
  82. data/lib/prawn.rb +67 -0
  83. data/lib/prawn/compatibility.rb +46 -0
  84. data/lib/prawn/document.rb +309 -0
  85. data/lib/prawn/document/bounding_box.rb +362 -0
  86. data/lib/prawn/document/internals.rb +113 -0
  87. data/lib/prawn/document/page_geometry.rb +79 -0
  88. data/lib/prawn/document/span.rb +47 -0
  89. data/lib/prawn/document/table.rb +350 -0
  90. data/lib/prawn/document/text.rb +196 -0
  91. data/lib/prawn/errors.rb +48 -0
  92. data/lib/prawn/font.rb +356 -0
  93. data/lib/prawn/font/cmap.rb +59 -0
  94. data/lib/prawn/font/metrics.rb +378 -0
  95. data/lib/prawn/font/wrapping.rb +47 -0
  96. data/lib/prawn/graphics.rb +252 -0
  97. data/lib/prawn/graphics/cell.rb +264 -0
  98. data/lib/prawn/graphics/color.rb +132 -0
  99. data/lib/prawn/images.rb +336 -0
  100. data/lib/prawn/images/jpg.rb +45 -0
  101. data/lib/prawn/images/png.rb +199 -0
  102. data/lib/prawn/pdf_object.rb +73 -0
  103. data/lib/prawn/reference.rb +56 -0
  104. data/spec/bounding_box_spec.rb +141 -0
  105. data/spec/document_spec.rb +181 -0
  106. data/spec/font_spec.rb +141 -0
  107. data/spec/graphics_spec.rb +209 -0
  108. data/spec/images_spec.rb +68 -0
  109. data/spec/jpg_spec.rb +25 -0
  110. data/spec/metrics_spec.rb +62 -0
  111. data/spec/pdf_object_spec.rb +112 -0
  112. data/spec/png_spec.rb +196 -0
  113. data/spec/reference_spec.rb +42 -0
  114. data/spec/spec_helper.rb +23 -0
  115. data/spec/table_spec.rb +179 -0
  116. data/spec/text_spec.rb +135 -0
  117. metadata +181 -0
@@ -0,0 +1,252 @@
1
+ # encoding: utf-8
2
+
3
+ # graphics.rb : Implements PDF drawing primitives
4
+ #
5
+ # Copyright April 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require "enumerator"
10
+ require "prawn/graphics/cell"
11
+ require "prawn/graphics/color"
12
+
13
+ module Prawn
14
+
15
+ # Implements the drawing facilities for Prawn::Document.
16
+ # Use this to draw the most beautiful imaginable things.
17
+ #
18
+ # This file lifts and modifies several of PDF::Writer's graphics functions
19
+ # ruby-pdf.rubyforge.org
20
+ #
21
+ module Graphics
22
+
23
+ include Color
24
+
25
+ #######################################################################
26
+ # Low level drawing operations must translate to absolute coords! #
27
+ #######################################################################
28
+
29
+ # Moves the drawing position to a given point. The point can be
30
+ # specified as a tuple or a flattened argument list
31
+ #
32
+ # pdf.move_to [100,50]
33
+ # pdf.move_to(100,50)
34
+ #
35
+ def move_to(*point)
36
+ x,y = translate(point)
37
+ add_content("%.3f %.3f m" % [ x, y ])
38
+ end
39
+
40
+ # Draws a line from the current drawing position to the specified point.
41
+ # The destination may be described as a tuple or a flattened list:
42
+ #
43
+ # pdf.line_to [50,50]
44
+ # pdf.line_to(50,50)
45
+ #
46
+ def line_to(*point)
47
+ x,y = translate(point)
48
+ add_content("%.3f %.3f l" % [ x, y ])
49
+ end
50
+
51
+ # Draws a Bezier curve from the current drawing position to the
52
+ # specified point, bounded by two additional points.
53
+ #
54
+ # pdf.curve_to [100,100], :bounds => [[90,90],[75,75]]
55
+ #
56
+ def curve_to(dest,options={})
57
+ options[:bounds] or raise Prawn::Errors::InvalidGraphicsPath,
58
+ "Bounding points for bezier curve must be specified "+
59
+ "as :bounds => [[x1,y1],[x2,y2]]"
60
+
61
+ curve_points = (options[:bounds] << dest).map { |e| translate(e) }
62
+ add_content("%.3f %.3f %.3f %.3f %.3f %.3f c" %
63
+ curve_points.flatten )
64
+ end
65
+
66
+ # Draws a rectangle given <tt>point</tt>, <tt>width</tt> and
67
+ # <tt>height</tt>. The rectangle is bounded by its upper-left corner.
68
+ #
69
+ # pdf.rectangle [300,300], 100, 200
70
+ #
71
+ def rectangle(point,width,height)
72
+ x,y = translate(point)
73
+ add_content("%.3f %.3f %.3f %.3f re" % [ x, y - height, width, height ])
74
+ end
75
+
76
+ ###########################################################
77
+ # Higher level functions: May use relative coords #
78
+ ###########################################################
79
+
80
+ # Sets line thickness to the <tt>width</tt> specified.
81
+ #
82
+ def line_width=(width)
83
+ @line_width = width
84
+ add_content("#{width} w")
85
+ end
86
+
87
+ # The current line thickness
88
+ #
89
+ def line_width(width=nil)
90
+ if width
91
+ self.line_width = width
92
+ else
93
+ @line_width || 1
94
+ end
95
+ end
96
+
97
+ # Draws a line from one point to another. Points may be specified as
98
+ # tuples or flattened argument list:
99
+ #
100
+ # pdf.line [100,100], [200,250]
101
+ # pdf.line(100,100,200,250)
102
+ #
103
+ def line(*points)
104
+ x0,y0,x1,y1 = points.flatten
105
+ move_to(x0, y0)
106
+ line_to(x1, y1)
107
+ end
108
+
109
+ # Draws a horizontal line from <tt>x1</tt> to <tt>x2</tt> at the
110
+ # current <tt>y</tt> position, or the position specified by the :at option.
111
+ #
112
+ # # draw a line from [25, 75] to [100, 75]
113
+ # horizontal_line 25, 100, :at => 75
114
+ #
115
+ def horizontal_line(x1,x2,options={})
116
+ if options[:at]
117
+ y1 = options[:at]
118
+ else
119
+ y1 = y - bounds.absolute_bottom
120
+ end
121
+
122
+ line(x1,y1,x2,y1)
123
+ end
124
+
125
+ # Draws a horizontal line from the left border to the right border of the
126
+ # bounding box at the current <tt>y</tt> position.
127
+ #
128
+ def horizontal_rule
129
+ horizontal_line(bounds.left, bounds.right)
130
+ end
131
+
132
+ # Draws a vertical line at the x cooordinate given by :at from y1 to y2.
133
+ #
134
+ # # draw a line from [25, 100] to [25, 300]
135
+ # vertical_line 100, 300, :at => 25
136
+ #
137
+ def vertical_line(y1,y2,params)
138
+ line(params[:at],y1,params[:at],y2)
139
+ end
140
+
141
+ # Draws a Bezier curve between two points, bounded by two additional
142
+ # points
143
+ #
144
+ # pdf.curve [50,100], [100,100], :bounds => [[90,90],[75,75]]
145
+ #
146
+ def curve(origin,dest, options={})
147
+ move_to(*origin)
148
+ curve_to(dest,options)
149
+ end
150
+
151
+ # This constant is used to approximate a symmetrical arc using a cubic
152
+ # Bezier curve.
153
+ #
154
+ KAPPA = 4.0 * ((Math.sqrt(2) - 1.0) / 3.0)
155
+
156
+ # Draws a circle of radius <tt>:radius</tt> with the centre-point at <tt>point</tt>
157
+ # as a complete subpath. The drawing point will be moved to the
158
+ # centre-point upon completion of the drawing the circle.
159
+ #
160
+ # pdf.circle_at [100,100], :radius => 25
161
+ #
162
+ def circle_at(point, options)
163
+ x,y = point
164
+ ellipse_at [x, y], options[:radius]
165
+ end
166
+
167
+ # Draws an ellipse of +x+ radius <tt>r1</tt> and +y+ radius <tt>r2</tt>
168
+ # with the centre-point at <tt>point</tt> as a complete subpath. The
169
+ # drawing point will be moved to the centre-point upon completion of the
170
+ # drawing the ellipse.
171
+ #
172
+ # # draws an ellipse with x-radius 25 and y-radius 50
173
+ # pdf.ellipse_at [100,100], 25, 50
174
+ #
175
+ def ellipse_at(point, r1, r2 = r1)
176
+ x, y = point
177
+ l1 = r1 * KAPPA
178
+ l2 = r2 * KAPPA
179
+
180
+ move_to(x + r1, y)
181
+
182
+ # Upper right hand corner
183
+ curve_to [x, y + r2],
184
+ :bounds => [[x + r1, y + l1], [x + l2, y + r2]]
185
+
186
+ # Upper left hand corner
187
+ curve_to [x - r1, y],
188
+ :bounds => [[x - l2, y + r2], [x - r1, y + l1]]
189
+
190
+ # Lower left hand corner
191
+ curve_to [x, y - r2],
192
+ :bounds => [[x - r1, y - l1], [x - l2, y - r2]]
193
+
194
+ # Lower right hand corner
195
+ curve_to [x + r1, y],
196
+ :bounds => [[x + l2, y - r2], [x + r1, y - l1]]
197
+
198
+ move_to(x, y)
199
+ end
200
+
201
+ # Draws a polygon from the specified points.
202
+ #
203
+ # # draws a snazzy triangle
204
+ # pdf.polygon [100,100], [100,200], [200,200]
205
+ #
206
+ def polygon(*points)
207
+ move_to points[0]
208
+ (points << points[0]).each_cons(2) do |p1,p2|
209
+ line_to(*p2)
210
+ end
211
+ end
212
+
213
+ # Strokes and closes the current path. See Graphic::Color for color details
214
+ #
215
+ def stroke
216
+ yield if block_given?
217
+ add_content "S"
218
+ end
219
+
220
+ # Draws and strokes a rectangle represented by the current bounding box
221
+ #
222
+ def stroke_bounds
223
+ stroke_rectangle bounds.top_left, bounds.width, bounds.height
224
+ end
225
+
226
+ # Fills and closes the current path. See Graphic::Color for color details
227
+ #
228
+ def fill
229
+ yield if block_given?
230
+ add_content "f"
231
+ end
232
+
233
+ # Fills, strokes, and closes the current path. See Graphic::Color for color details
234
+ #
235
+ def fill_and_stroke
236
+ yield if block_given?
237
+ add_content "b"
238
+ end
239
+
240
+ private
241
+
242
+ def translate(*point)
243
+ x,y = point.flatten
244
+ [@bounding_box.absolute_left + x, @bounding_box.absolute_bottom + y]
245
+ end
246
+
247
+ def translate!(point)
248
+ point.replace(translate(point))
249
+ end
250
+
251
+ end
252
+ end
@@ -0,0 +1,264 @@
1
+ # encoding: utf-8
2
+
3
+
4
+ # cell.rb : Table support functions
5
+ #
6
+ # Copyright June 2008, Gregory Brown. All Rights Reserved.
7
+ #
8
+ # This is free software. Please see the LICENSE and COPYING files for details.
9
+ module Prawn
10
+
11
+ class Document
12
+ # Builds and renders a Graphics::Cell. A cell is essentially a
13
+ # special-purpose bounding box designed for flowing text within a bordered
14
+ # area. For available options, see Graphics::Cell#new.
15
+ #
16
+ # Prawn::Document.generate("cell.pdf") do
17
+ # cell [100,500],
18
+ # :width => 200,
19
+ # :text => "The rain in Spain falls mainly on the plains"
20
+ # end
21
+ #
22
+ def cell(point, options={})
23
+ Prawn::Graphics::Cell.new(
24
+ options.merge(:document => self, :point => point)).draw
25
+ end
26
+ end
27
+
28
+ module Graphics
29
+ # A cell is a special-purpose bounding box designed to flow text within a
30
+ # bordered area. This is used by Prawn's Document::Table implementation but
31
+ # can also be used standalone for drawing text boxes via Document#cell
32
+ #
33
+ class Cell
34
+
35
+ # Creates a new cell object. Generally used indirectly via Document#cell
36
+ #
37
+ # Of the available options listed below, <tt>:point</tt>, <tt>:width</tt>,
38
+ # and <tt>:text</tt> must be provided. If you are not using the
39
+ # Document#cell shortcut, the <tt>:document</tt> must also be provided.
40
+ #
41
+ # <tt>:point</tt>:: Absolute [x,y] coordinate of the top-left corner of the cell.
42
+ # <tt>:document</tt>:: The Prawn::Document object to render on.
43
+ # <tt>:text</tt>:: The text to be flowed within the cell
44
+ # <tt>:width</tt>:: The width in PDF points of the cell.
45
+ # <tt>:height</tt>:: The height in PDF points of the cell.
46
+ # <tt>:horizontal_padding</tt>:: The horizontal padding in PDF points
47
+ # <tt>:vertical_padding</tt>:: The vertical padding in PDF points
48
+ # <tt>:padding</tt>:: Overrides both horizontal and vertical padding
49
+ # <tt>:align</tt>:: One of <tt>:left</tt>, <tt>:right</tt>, <tt>:center</tt>
50
+ # <tt>:borders</tt>:: An array of sides which should have a border. Any of <tt>:top</tt>, <tt>:left</tt>, <tt>:right</tt>, <tt>:bottom</tt>
51
+ # <tt>:border_width</tt>:: The border line width. Defaults to 1.
52
+ # <tt>:border_style</tt>:: One of <tt>:all</tt>, <tt>:no_top</tt>, <tt>:no_bottom</tt>, <tt>:sides</tt>, <tt>:none</tt>, <tt>:bottom_only</tt> or <tt>:top_and_bottom</tt>. Defaults to :all.
53
+ #
54
+ def initialize(options={})
55
+ @point = options[:point]
56
+ @document = options[:document]
57
+ @text = options[:text].to_s
58
+ @text_color = options[:text_color]
59
+ @width = options[:width]
60
+ @height = options[:height]
61
+ @borders = options[:borders]
62
+ @border_width = options[:border_width] || 1
63
+ @border_style = options[:border_style] || :all
64
+ @border_color = options[:border_color]
65
+ @background_color = options[:background_color]
66
+ @align = options[:align] || :left
67
+ @font_size = options[:font_size]
68
+
69
+ @horizontal_padding = options[:horizontal_padding] || 0
70
+ @vertical_padding = options[:vertical_padding] || 0
71
+
72
+ if options[:padding]
73
+ @horizontal_padding = @vertical_padding = options[:padding]
74
+ end
75
+ end
76
+
77
+ attr_accessor :point, :border_style, :border_width, :background_color,
78
+ :document, :horizontal_padding, :vertical_padding, :align,
79
+ :borders, :text_color, :border_color
80
+
81
+ attr_writer :height, :width #:nodoc:
82
+
83
+ # Returns the cell's text as a string.
84
+ #
85
+ def to_s
86
+ @text
87
+ end
88
+
89
+ # The width of the text area excluding the horizonal padding
90
+ #
91
+ def text_area_width
92
+ width - 2*@horizontal_padding
93
+ end
94
+
95
+ # The width of the cell in PDF points
96
+ #
97
+ def width
98
+ @width || (@document.font.metrics.string_width(@text,
99
+ @font_size || @document.font.size)) + 2*@horizontal_padding
100
+ end
101
+
102
+ # The height of the cell in PDF points
103
+ #
104
+ def height
105
+ @height || text_area_height + 2*@vertical_padding
106
+ end
107
+
108
+ # The height of the text area excluding the vertical padding
109
+ #
110
+ def text_area_height
111
+ @document.font.height_of(@text, :line_width => text_area_width)
112
+ end
113
+
114
+ # Draws the cell onto the PDF document
115
+ #
116
+ def draw
117
+ rel_point = @point
118
+
119
+ if @background_color
120
+ @document.mask(:fill_color) do
121
+ @document.fill_color @background_color
122
+ h = borders.include?(:bottom) ?
123
+ height - border_width : height + border_width / 2.0
124
+ @document.fill_rectangle [rel_point[0] + border_width / 2.0,
125
+ rel_point[1] - border_width / 2.0 ],
126
+ width - border_width, h
127
+ end
128
+ end
129
+
130
+ if @border_width > 0
131
+ @document.mask(:line_width) do
132
+ @document.line_width = @border_width
133
+
134
+ @document.mask(:stroke_color) do
135
+ @document.stroke_color @border_color if @border_color
136
+
137
+ if borders.include?(:left)
138
+ @document.stroke_line [rel_point[0], rel_point[1] + (@border_width / 2.0)],
139
+ [rel_point[0], rel_point[1] - height - @border_width / 2.0 ]
140
+ end
141
+
142
+ if borders.include?(:right)
143
+ @document.stroke_line(
144
+ [rel_point[0] + width, rel_point[1] + (@border_width / 2.0)],
145
+ [rel_point[0] + width, rel_point[1] - height - @border_width / 2.0] )
146
+ end
147
+
148
+ if borders.include?(:top)
149
+ @document.stroke_line(
150
+ [ rel_point[0] + @border_width / 2.0, rel_point[1] ],
151
+ [ rel_point[0] - @border_width / 2.0 + width, rel_point[1] ])
152
+ end
153
+
154
+ if borders.include?(:bottom)
155
+ @document.stroke_line [rel_point[0], rel_point[1] - height ],
156
+ [rel_point[0] + width, rel_point[1] - height]
157
+ end
158
+ end
159
+
160
+ end
161
+
162
+ borders
163
+
164
+ end
165
+
166
+ @document.bounding_box( [@point[0] + @horizontal_padding,
167
+ @point[1] - @vertical_padding],
168
+ :width => text_area_width,
169
+ :height => height - @vertical_padding) do
170
+ @document.move_up @document.font.line_gap
171
+
172
+ options = {:align => @align}
173
+
174
+ options[:size] = @font_size if @font_size
175
+
176
+ @document.mask(:fill_color) do
177
+ @document.fill_color @text_color if @text_color
178
+ @document.text @text, options
179
+ end
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def borders
186
+ @borders ||= case @border_style
187
+ when :all
188
+ [:top,:left,:right,:bottom]
189
+ when :sides
190
+ [:left,:right]
191
+ when :no_top
192
+ [:left,:right,:bottom]
193
+ when :no_bottom
194
+ [:left,:right,:top]
195
+ when :bottom_only
196
+ [:bottom]
197
+ when :top_and_bottom
198
+ [:top, :bottom]
199
+ when :none
200
+ []
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+ class CellBlock #:nodoc:
207
+
208
+ # Not sure if this class is something I want to expose in the public API.
209
+
210
+ def initialize(document)
211
+ @document = document
212
+ @cells = []
213
+ @width = 0
214
+ @height = 0
215
+ end
216
+
217
+ attr_reader :width, :height, :cells
218
+ attr_accessor :background_color, :text_color, :border_color
219
+
220
+ def <<(cell)
221
+ @cells << cell
222
+ @height = cell.height if cell.height > @height
223
+ @width += cell.width
224
+ self
225
+ end
226
+
227
+ def draw
228
+ y = @document.y
229
+ x = @document.bounds.absolute_left
230
+
231
+ @cells.each do |e|
232
+ e.point = [x - @document.bounds.absolute_left,
233
+ y - @document.bounds.absolute_bottom]
234
+ e.height = @height
235
+ e.background_color ||= @background_color
236
+ e.text_color ||= @text_color
237
+ e.border_color ||= @border_color
238
+ e.draw
239
+ x += e.width
240
+ end
241
+
242
+ @document.y = y - @height
243
+ end
244
+
245
+ def border_width
246
+ @cells[0].border_width
247
+ end
248
+
249
+ def border_style=(s)
250
+ @cells.each { |e| e.border_style = s }
251
+ end
252
+
253
+ def align=(align)
254
+ @cells.each { |e| e.align = align }
255
+ end
256
+
257
+ def border_style
258
+ @cells[0].border_style
259
+ end
260
+
261
+ end
262
+ end
263
+
264
+ end