prawn-git 2.0.1

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 (252) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/COPYING +2 -0
  4. data/GPLv2 +340 -0
  5. data/GPLv3 +674 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE +56 -0
  8. data/Rakefile +55 -0
  9. data/data/fonts/Courier-Bold.afm +342 -0
  10. data/data/fonts/Courier-BoldOblique.afm +342 -0
  11. data/data/fonts/Courier-Oblique.afm +342 -0
  12. data/data/fonts/Courier.afm +342 -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/images/16bit.alpha +0 -0
  25. data/data/images/16bit.color +0 -0
  26. data/data/images/16bit.png +0 -0
  27. data/data/images/arrow.png +0 -0
  28. data/data/images/arrow2.png +0 -0
  29. data/data/images/dice.alpha +0 -0
  30. data/data/images/dice.color +0 -0
  31. data/data/images/dice.png +0 -0
  32. data/data/images/dice_interlaced.png +0 -0
  33. data/data/images/fractal.jpg +0 -0
  34. data/data/images/indexed_color.dat +0 -0
  35. data/data/images/indexed_color.png +0 -0
  36. data/data/images/letterhead.jpg +0 -0
  37. data/data/images/license.md +8 -0
  38. data/data/images/page_white_text.alpha +0 -0
  39. data/data/images/page_white_text.color +0 -0
  40. data/data/images/page_white_text.png +0 -0
  41. data/data/images/pal_bk.png +0 -0
  42. data/data/images/pigs.jpg +0 -0
  43. data/data/images/prawn.png +0 -0
  44. data/data/images/ruport.png +0 -0
  45. data/data/images/ruport_data.dat +0 -0
  46. data/data/images/ruport_transparent.png +0 -0
  47. data/data/images/ruport_type0.png +0 -0
  48. data/data/images/stef.jpg +0 -0
  49. data/data/images/tru256.bmp +0 -0
  50. data/data/images/web-links.dat +1 -0
  51. data/data/images/web-links.png +0 -0
  52. data/data/pdfs/complex_template.pdf +0 -0
  53. data/data/pdfs/contains_ttf_font.pdf +0 -0
  54. data/data/pdfs/encrypted.pdf +0 -0
  55. data/data/pdfs/form.pdf +820 -0
  56. data/data/pdfs/hexagon.pdf +61 -0
  57. data/data/pdfs/indirect_reference.pdf +86 -0
  58. data/data/pdfs/multipage_template.pdf +127 -0
  59. data/data/pdfs/nested_pages.pdf +118 -0
  60. data/data/pdfs/page_without_mediabox.pdf +193 -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/lib/prawn.rb +89 -0
  66. data/lib/prawn/document.rb +706 -0
  67. data/lib/prawn/document/bounding_box.rb +539 -0
  68. data/lib/prawn/document/column_box.rb +144 -0
  69. data/lib/prawn/document/internals.rb +58 -0
  70. data/lib/prawn/document/span.rb +57 -0
  71. data/lib/prawn/encoding.rb +87 -0
  72. data/lib/prawn/errors.rb +80 -0
  73. data/lib/prawn/font.rb +413 -0
  74. data/lib/prawn/font/afm.rb +256 -0
  75. data/lib/prawn/font/dfont.rb +43 -0
  76. data/lib/prawn/font/ttf.rb +355 -0
  77. data/lib/prawn/font_metric_cache.rb +46 -0
  78. data/lib/prawn/graphics.rb +646 -0
  79. data/lib/prawn/graphics/cap_style.rb +47 -0
  80. data/lib/prawn/graphics/color.rb +232 -0
  81. data/lib/prawn/graphics/dash.rb +109 -0
  82. data/lib/prawn/graphics/join_style.rb +49 -0
  83. data/lib/prawn/graphics/patterns.rb +126 -0
  84. data/lib/prawn/graphics/transformation.rb +157 -0
  85. data/lib/prawn/graphics/transparency.rb +101 -0
  86. data/lib/prawn/grid.rb +279 -0
  87. data/lib/prawn/image_handler.rb +44 -0
  88. data/lib/prawn/images.rb +199 -0
  89. data/lib/prawn/images/image.rb +49 -0
  90. data/lib/prawn/images/jpg.rb +91 -0
  91. data/lib/prawn/images/png.rb +290 -0
  92. data/lib/prawn/measurement_extensions.rb +50 -0
  93. data/lib/prawn/measurements.rb +77 -0
  94. data/lib/prawn/outline.rb +289 -0
  95. data/lib/prawn/repeater.rb +124 -0
  96. data/lib/prawn/security.rb +288 -0
  97. data/lib/prawn/security/arcfour.rb +54 -0
  98. data/lib/prawn/soft_mask.rb +94 -0
  99. data/lib/prawn/stamp.rb +136 -0
  100. data/lib/prawn/text.rb +437 -0
  101. data/lib/prawn/text/box.rb +141 -0
  102. data/lib/prawn/text/formatted.rb +7 -0
  103. data/lib/prawn/text/formatted/arranger.rb +290 -0
  104. data/lib/prawn/text/formatted/box.rb +614 -0
  105. data/lib/prawn/text/formatted/fragment.rb +264 -0
  106. data/lib/prawn/text/formatted/line_wrap.rb +277 -0
  107. data/lib/prawn/text/formatted/parser.rb +224 -0
  108. data/lib/prawn/text/formatted/wrap.rb +160 -0
  109. data/lib/prawn/utilities.rb +46 -0
  110. data/lib/prawn/version.rb +5 -0
  111. data/lib/prawn/view.rb +91 -0
  112. data/manual/absolute_position.pdf +0 -0
  113. data/manual/basic_concepts/adding_pages.rb +27 -0
  114. data/manual/basic_concepts/basic_concepts.rb +36 -0
  115. data/manual/basic_concepts/creation.rb +39 -0
  116. data/manual/basic_concepts/cursor.rb +33 -0
  117. data/manual/basic_concepts/measurement.rb +25 -0
  118. data/manual/basic_concepts/origin.rb +38 -0
  119. data/manual/basic_concepts/other_cursor_helpers.rb +40 -0
  120. data/manual/basic_concepts/view.rb +42 -0
  121. data/manual/bounding_box/bounding_box.rb +39 -0
  122. data/manual/bounding_box/bounds.rb +49 -0
  123. data/manual/bounding_box/canvas.rb +24 -0
  124. data/manual/bounding_box/creation.rb +23 -0
  125. data/manual/bounding_box/indentation.rb +46 -0
  126. data/manual/bounding_box/nesting.rb +45 -0
  127. data/manual/bounding_box/russian_boxes.rb +40 -0
  128. data/manual/bounding_box/stretchy.rb +31 -0
  129. data/manual/contents.rb +29 -0
  130. data/manual/cover.rb +39 -0
  131. data/manual/document_and_page_options/background.rb +27 -0
  132. data/manual/document_and_page_options/document_and_page_options.rb +32 -0
  133. data/manual/document_and_page_options/metadata.rb +23 -0
  134. data/manual/document_and_page_options/page_margins.rb +38 -0
  135. data/manual/document_and_page_options/page_size.rb +34 -0
  136. data/manual/document_and_page_options/print_scaling.rb +20 -0
  137. data/manual/example_helper.rb +7 -0
  138. data/manual/graphics/circle_and_ellipse.rb +22 -0
  139. data/manual/graphics/color.rb +24 -0
  140. data/manual/graphics/common_lines.rb +30 -0
  141. data/manual/graphics/fill_and_stroke.rb +42 -0
  142. data/manual/graphics/fill_rules.rb +37 -0
  143. data/manual/graphics/gradients.rb +37 -0
  144. data/manual/graphics/graphics.rb +58 -0
  145. data/manual/graphics/helper.rb +24 -0
  146. data/manual/graphics/line_width.rb +35 -0
  147. data/manual/graphics/lines_and_curves.rb +41 -0
  148. data/manual/graphics/polygon.rb +29 -0
  149. data/manual/graphics/rectangle.rb +21 -0
  150. data/manual/graphics/rotate.rb +28 -0
  151. data/manual/graphics/scale.rb +41 -0
  152. data/manual/graphics/soft_masks.rb +46 -0
  153. data/manual/graphics/stroke_cap.rb +31 -0
  154. data/manual/graphics/stroke_dash.rb +48 -0
  155. data/manual/graphics/stroke_join.rb +30 -0
  156. data/manual/graphics/translate.rb +29 -0
  157. data/manual/graphics/transparency.rb +35 -0
  158. data/manual/how_to_read_this_manual.rb +40 -0
  159. data/manual/images/absolute_position.rb +23 -0
  160. data/manual/images/fit.rb +21 -0
  161. data/manual/images/horizontal.rb +25 -0
  162. data/manual/images/images.rb +40 -0
  163. data/manual/images/plain_image.rb +18 -0
  164. data/manual/images/scale.rb +22 -0
  165. data/manual/images/vertical.rb +28 -0
  166. data/manual/images/width_and_height.rb +25 -0
  167. data/manual/layout/boxes.rb +27 -0
  168. data/manual/layout/content.rb +25 -0
  169. data/manual/layout/layout.rb +28 -0
  170. data/manual/layout/simple_grid.rb +23 -0
  171. data/manual/outline/add_subsection_to.rb +61 -0
  172. data/manual/outline/insert_section_after.rb +47 -0
  173. data/manual/outline/outline.rb +32 -0
  174. data/manual/outline/sections_and_pages.rb +67 -0
  175. data/manual/repeatable_content/alternate_page_numbering.rb +32 -0
  176. data/manual/repeatable_content/page_numbering.rb +54 -0
  177. data/manual/repeatable_content/repeatable_content.rb +32 -0
  178. data/manual/repeatable_content/repeater.rb +55 -0
  179. data/manual/repeatable_content/stamp.rb +41 -0
  180. data/manual/security/encryption.rb +31 -0
  181. data/manual/security/permissions.rb +38 -0
  182. data/manual/security/security.rb +28 -0
  183. data/manual/table.rb +16 -0
  184. data/manual/text/alignment.rb +44 -0
  185. data/manual/text/color.rb +24 -0
  186. data/manual/text/column_box.rb +32 -0
  187. data/manual/text/fallback_fonts.rb +37 -0
  188. data/manual/text/font.rb +41 -0
  189. data/manual/text/font_size.rb +45 -0
  190. data/manual/text/font_style.rb +23 -0
  191. data/manual/text/formatted_callbacks.rb +60 -0
  192. data/manual/text/formatted_text.rb +50 -0
  193. data/manual/text/free_flowing_text.rb +51 -0
  194. data/manual/text/inline.rb +41 -0
  195. data/manual/text/kerning_and_character_spacing.rb +39 -0
  196. data/manual/text/leading.rb +25 -0
  197. data/manual/text/line_wrapping.rb +41 -0
  198. data/manual/text/paragraph_indentation.rb +34 -0
  199. data/manual/text/positioned_text.rb +38 -0
  200. data/manual/text/registering_families.rb +48 -0
  201. data/manual/text/rendering_and_color.rb +37 -0
  202. data/manual/text/right_to_left_text.rb +47 -0
  203. data/manual/text/rotation.rb +43 -0
  204. data/manual/text/single_usage.rb +37 -0
  205. data/manual/text/text.rb +73 -0
  206. data/manual/text/text_box_excess.rb +32 -0
  207. data/manual/text/text_box_extensions.rb +45 -0
  208. data/manual/text/text_box_overflow.rb +48 -0
  209. data/manual/text/utf8.rb +28 -0
  210. data/manual/text/win_ansi_charset.rb +60 -0
  211. data/prawn.gemspec +45 -0
  212. data/spec/acceptance/png.rb +25 -0
  213. data/spec/annotations_spec.rb +74 -0
  214. data/spec/bounding_box_spec.rb +510 -0
  215. data/spec/column_box_spec.rb +65 -0
  216. data/spec/data/curves.pdf +66 -0
  217. data/spec/destinations_spec.rb +15 -0
  218. data/spec/document_spec.rb +748 -0
  219. data/spec/extensions/encoding_helpers.rb +11 -0
  220. data/spec/extensions/mocha.rb +46 -0
  221. data/spec/font_metric_cache_spec.rb +52 -0
  222. data/spec/font_spec.rb +474 -0
  223. data/spec/formatted_text_arranger_spec.rb +421 -0
  224. data/spec/formatted_text_box_spec.rb +705 -0
  225. data/spec/formatted_text_fragment_spec.rb +298 -0
  226. data/spec/graphics_spec.rb +683 -0
  227. data/spec/grid_spec.rb +96 -0
  228. data/spec/image_handler_spec.rb +54 -0
  229. data/spec/images_spec.rb +153 -0
  230. data/spec/inline_formatted_text_parser_spec.rb +564 -0
  231. data/spec/jpg_spec.rb +25 -0
  232. data/spec/line_wrap_spec.rb +367 -0
  233. data/spec/measurement_units_spec.rb +25 -0
  234. data/spec/outline_spec.rb +430 -0
  235. data/spec/png_spec.rb +245 -0
  236. data/spec/reference_spec.rb +25 -0
  237. data/spec/repeater_spec.rb +160 -0
  238. data/spec/security_spec.rb +158 -0
  239. data/spec/soft_mask_spec.rb +79 -0
  240. data/spec/span_spec.rb +44 -0
  241. data/spec/spec_helper.rb +54 -0
  242. data/spec/stamp_spec.rb +160 -0
  243. data/spec/stroke_styles_spec.rb +211 -0
  244. data/spec/text_at_spec.rb +143 -0
  245. data/spec/text_box_spec.rb +1043 -0
  246. data/spec/text_rendering_mode_spec.rb +45 -0
  247. data/spec/text_spacing_spec.rb +93 -0
  248. data/spec/text_spec.rb +557 -0
  249. data/spec/text_with_inline_formatting_spec.rb +35 -0
  250. data/spec/transparency_spec.rb +91 -0
  251. data/spec/view_spec.rb +43 -0
  252. metadata +509 -0
@@ -0,0 +1,144 @@
1
+ # encoding: utf-8
2
+ #
3
+ # column_box.rb: Extends BoundingBox to allow for columns of text
4
+ #
5
+ # Author Paul Ostazeski.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require_relative "bounding_box"
10
+
11
+ module Prawn
12
+ class Document
13
+
14
+ # @group Experimental API
15
+
16
+ # A column box is a bounding box with the additional property that when
17
+ # text flows past the bottom, it will wrap first to another column on the
18
+ # same page, and only flow to the next page when all the columns are
19
+ # filled.
20
+ #
21
+ # column_box accepts the same parameters as bounding_box, as well as the
22
+ # number of :columns and a :spacer (in points) between columns. If resetting
23
+ # the top margin is desired on a new page (e.g. to allow for initial page
24
+ # wide column titles) the option :reflow_margins => true can be set.
25
+ #
26
+ # Defaults are :columns = 3, :spacer = font_size, and
27
+ # :reflow_margins => false
28
+ #
29
+ # Under PDF::Writer, "spacer" was known as "gutter"
30
+ #
31
+ def column_box(*args, &block)
32
+ init_column_box(block) do |parent_box|
33
+ map_to_absolute!(args[0])
34
+ @bounding_box = ColumnBox.new(self, parent_box, *args)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def init_column_box(user_block, options={}, &init_block)
41
+ parent_box = @bounding_box
42
+
43
+ init_block.call(parent_box)
44
+
45
+ self.y = @bounding_box.absolute_top
46
+ user_block.call
47
+ self.y = @bounding_box.absolute_bottom unless options[:hold_position]
48
+
49
+ @bounding_box = parent_box
50
+ end
51
+
52
+ # Implements the necessary functionality to allow Document#column_box to
53
+ # work.
54
+ #
55
+ class ColumnBox < BoundingBox
56
+
57
+ def initialize(document, parent, point, options={}) #:nodoc:
58
+ super
59
+ @columns = options[:columns] || 3
60
+ @spacer = options[:spacer] || @document.font_size
61
+ @current_column = 0
62
+ @reflow_margins = options[:reflow_margins]
63
+ end
64
+
65
+ # The column width, not the width of the whole box,
66
+ # before left and/or right padding
67
+ def bare_column_width
68
+ (@width - @spacer * (@columns - 1)) / @columns
69
+ end
70
+
71
+ # The column width after padding.
72
+ # Used to calculate how long a line of text can be.
73
+ #
74
+ def width
75
+ bare_column_width - (@total_left_padding + @total_right_padding)
76
+ end
77
+
78
+ # Column width including the spacer.
79
+ #
80
+ def width_of_column
81
+ bare_column_width + @spacer
82
+ end
83
+
84
+ # x coordinate of the left edge of the current column
85
+ #
86
+ def left_side
87
+ absolute_left + (width_of_column * @current_column)
88
+ end
89
+
90
+ # Relative position of the left edge of the current column
91
+ #
92
+ def left
93
+ width_of_column * @current_column
94
+ end
95
+
96
+ # x co-orordinate of the right edge of the current column
97
+ #
98
+ def right_side
99
+ columns_from_right = @columns - (1 + @current_column)
100
+ absolute_right - (width_of_column * columns_from_right)
101
+ end
102
+
103
+ # Relative position of the right edge of the current column.
104
+ #
105
+ def right
106
+ left + width
107
+ end
108
+
109
+ # Moves to the next column or starts a new page if currently positioned at
110
+ # the rightmost column.
111
+ def move_past_bottom
112
+ @current_column = (@current_column + 1) % @columns
113
+ @document.y = @y
114
+ if 0 == @current_column
115
+ if @reflow_margins
116
+ @y = @parent.absolute_top
117
+ end
118
+ @document.start_new_page
119
+ end
120
+ end
121
+
122
+ # Override the padding functions so as not to split the padding amount
123
+ # between all columns on the page.
124
+
125
+ def add_left_padding(left_padding)
126
+ @total_left_padding += left_padding
127
+ @x += left_padding
128
+ end
129
+
130
+ def subtract_left_padding(left_padding)
131
+ @total_left_padding -= left_padding
132
+ @x -= left_padding
133
+ end
134
+
135
+ def add_right_padding(right_padding)
136
+ @total_right_padding += right_padding
137
+ end
138
+
139
+ def subtract_right_padding(right_padding)
140
+ @total_right_padding -= right_padding
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ #
3
+ # internals.rb : Implements document internals for Prawn
4
+ #
5
+ # Copyright August 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require "forwardable"
10
+
11
+ module Prawn
12
+ class Document
13
+
14
+ # This module exposes a few low-level PDF features for those who want
15
+ # to extend Prawn's core functionality. If you are not comfortable with
16
+ # low level PDF functionality as defined by Adobe's specification, chances
17
+ # are you won't need anything you find here.
18
+ #
19
+ # @private
20
+ module Internals
21
+ extend Forwardable
22
+
23
+ # These methods are not officially part of Prawn's public API,
24
+ # but they are used in documentation and possibly in extensions.
25
+ # Perhaps they will become part of the extension API?
26
+ # Anyway, for now it's not clear what we should do w. them.
27
+ delegate [ :graphic_state,
28
+ :save_graphics_state,
29
+ :restore_graphics_state,
30
+ :on_page_create ] => :renderer
31
+
32
+ # FIXME: This is a circular reference, because in theory Prawn should
33
+ # be passing instances of renderer to PDF::Core::Page, but it's
34
+ # passing Prawn::Document objects instead.
35
+ #
36
+ # A proper design would probably not require Prawn to directly instantiate
37
+ # PDF::Core::Page objects at all!
38
+ delegate [:compression_enabled?] => :renderer
39
+
40
+ # FIXME: More circular references in PDF::Core::Page.
41
+ delegate [ :ref, :ref!, :deref ] => :renderer
42
+
43
+ # FIXME: Another circular reference, because we mix in a module from
44
+ # PDF::Core to provide destinations, which in theory should not
45
+ # rely on a Prawn::Document object but is currently wired up that way.
46
+ delegate [:names] => :renderer
47
+
48
+ # FIXME: Circular reference because we mix PDF::Core::Text into
49
+ # Prawn::Document. PDF::Core::Text should either be split up or
50
+ # moved in its entirety back up into Prawn.
51
+ delegate [:add_content] => :renderer
52
+
53
+ def renderer
54
+ @renderer ||= PDF::Core::Renderer.new(state)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ # span.rb : Implements text columns
4
+ #
5
+ # Copyright September 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module Prawn
10
+ class Document
11
+ # @group Stable API
12
+
13
+ # A span is a special purpose bounding box that allows a column of
14
+ # elements to be positioned relative to the margin_box.
15
+ #
16
+ # Arguments:
17
+ # +width+:: The width of the column in PDF points
18
+ #
19
+ # Options:
20
+ # <tt>:position</tt>:: One of :left, :center, :right or an x offset
21
+ #
22
+ # This method is typically used for flowing a column of text from one
23
+ # page to the next.
24
+ #
25
+ # span(350, :position => :center) do
26
+ # text "Here's some centered text in a 350 point column. " * 100
27
+ # end
28
+ #
29
+ def span(width, options={})
30
+ Prawn.verify_options [:position], options
31
+ original_position = self.y
32
+
33
+ # FIXME: Any way to move this upstream?
34
+ left_boundary = case(options[:position] || :left)
35
+ when :left
36
+ margin_box.absolute_left
37
+ when :center
38
+ margin_box.absolute_left + margin_box.width / 2.0 - width / 2.0
39
+ when :right
40
+ margin_box.absolute_right - width
41
+ when Numeric
42
+ margin_box.absolute_left + options[:position]
43
+ else
44
+ raise ArgumentError, "Invalid option for :position"
45
+ end
46
+
47
+ # we need to bust out of whatever nested bounding boxes we're in.
48
+ canvas do
49
+ bounding_box([left_boundary,
50
+ margin_box.absolute_top], :width => width) do
51
+ self.y = original_position
52
+ yield
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright September 2008, Gregory Brown, James Healy All Rights Reserved.
4
+ #
5
+ # This is free software. Please see the LICENSE and COPYING files for details.
6
+ #
7
+ module Prawn
8
+ module Encoding # @private
9
+ # Map between unicode and WinAnsiEnoding
10
+ #
11
+ class WinAnsi #:nodoc:
12
+ CHARACTERS = %w[
13
+ .notdef .notdef .notdef .notdef
14
+ .notdef .notdef .notdef .notdef
15
+ .notdef .notdef .notdef .notdef
16
+ .notdef .notdef .notdef .notdef
17
+ .notdef .notdef .notdef .notdef
18
+ .notdef .notdef .notdef .notdef
19
+ .notdef .notdef .notdef .notdef
20
+ .notdef .notdef .notdef .notdef
21
+
22
+ space exclam quotedbl numbersign
23
+ dollar percent ampersand quotesingle
24
+ parenleft parenright asterisk plus
25
+ comma hyphen period slash
26
+ zero one two three
27
+ four five six seven
28
+ eight nine colon semicolon
29
+ less equal greater question
30
+
31
+ at A B C
32
+ D E F G
33
+ H I J K
34
+ L M N O
35
+ P Q R S
36
+ T U V W
37
+ X Y Z bracketleft
38
+ backslash bracketright asciicircum underscore
39
+
40
+ grave a b c
41
+ d e f g
42
+ h i j k
43
+ l m n o
44
+ p q r s
45
+ t u v w
46
+ x y z braceleft
47
+ bar braceright asciitilde .notdef
48
+
49
+ Euro .notdef quotesinglbase florin
50
+ quotedblbase ellipsis dagger daggerdbl
51
+ circumflex perthousand Scaron guilsinglleft
52
+ OE .notdef Zcaron .notdef
53
+ .notdef quoteleft quoteright quotedblleft
54
+ quotedblright bullet endash emdash
55
+ tilde trademark scaron guilsinglright
56
+ oe .notdef zcaron ydieresis
57
+
58
+ space exclamdown cent sterling
59
+ currency yen brokenbar section
60
+ dieresis copyright ordfeminine guillemotleft
61
+ logicalnot hyphen registered macron
62
+ degree plusminus twosuperior threesuperior
63
+ acute mu paragraph periodcentered
64
+ cedilla onesuperior ordmasculine guillemotright
65
+ onequarter onehalf threequarters questiondown
66
+
67
+ Agrave Aacute Acircumflex Atilde
68
+ Adieresis Aring AE Ccedilla
69
+ Egrave Eacute Ecircumflex Edieresis
70
+ Igrave Iacute Icircumflex Idieresis
71
+ Eth Ntilde Ograve Oacute
72
+ Ocircumflex Otilde Odieresis multiply
73
+ Oslash Ugrave Uacute Ucircumflex
74
+ Udieresis Yacute Thorn germandbls
75
+
76
+ agrave aacute acircumflex atilde
77
+ adieresis aring ae ccedilla
78
+ egrave eacute ecircumflex edieresis
79
+ igrave iacute icircumflex idieresis
80
+ eth ntilde ograve oacute
81
+ ocircumflex otilde odieresis divide
82
+ oslash ugrave uacute ucircumflex
83
+ udieresis yacute thorn ydieresis
84
+ ]
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ #
3
+ # errors.rb : Implements custom error classes for Prawn
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
+ module Prawn
10
+ module Errors
11
+ # Raised when a table is spanned in an impossible way.
12
+ #
13
+ InvalidTableSpan = Class.new(StandardError)
14
+
15
+ # This error is raised when a method requiring a current page is called
16
+ # without being on a page.
17
+ #
18
+ NotOnPage = Class.new(StandardError)
19
+
20
+ # This error is raised when Prawn cannot find a specified font
21
+ #
22
+ UnknownFont = Class.new(StandardError)
23
+
24
+ # Raised when Prawn is asked to draw something into a too-small box
25
+ #
26
+ CannotFit = Class.new(StandardError)
27
+
28
+ # Raised if group() is called with a block that is too big to be
29
+ # rendered in the current context.
30
+ #
31
+ CannotGroup = Class.new(StandardError)
32
+
33
+ # This error is raised when Prawn is being used on a M17N aware VM,
34
+ # and the user attempts to add text that isn't compatible with UTF-8
35
+ # to their document
36
+ #
37
+ IncompatibleStringEncoding = Class.new(StandardError)
38
+
39
+ # This error is raised when Prawn encounters an unknown key in functions
40
+ # that accept an options hash. This usually means there is a typo in your
41
+ # code or that the option you are trying to use has a different name than
42
+ # what you have specified.
43
+ #
44
+ UnknownOption = Class.new(StandardError)
45
+
46
+ # this error is raised when a user attempts to embed an image of an unsupported
47
+ # type. This can either a completely unsupported format, or a dialect of a
48
+ # supported format (ie. some types of PNG)
49
+ UnsupportedImageType = Class.new(StandardError)
50
+
51
+ # This error is raised when a named element has alredy been
52
+ # created. For example, in the stamp module, stamps must have
53
+ # unique names within a document
54
+ NameTaken = Class.new(StandardError)
55
+
56
+ # This error is raised when a name is not a valid format
57
+ InvalidName = Class.new(StandardError)
58
+
59
+ # This error is raised when an object is attempted to be
60
+ # referenced by name, but no such name is associated with an object
61
+ UndefinedObjectName = Class.new(StandardError)
62
+
63
+ # This error is raised when a required option has not been set
64
+ RequiredOption = Class.new(StandardError)
65
+
66
+ # This error is raised when a requested outline item with a given title does not exist
67
+ UnknownOutlineTitle = Class.new(StandardError)
68
+
69
+ # This error is raised when a block is required, but not provided
70
+ BlockRequired = Class.new(StandardError)
71
+
72
+ # This error is rased when a graphics method is called with improper arguments
73
+ InvalidGraphicsPath = Class.new(StandardError)
74
+
75
+ # Raised when unrecognized content is provided for a table cell.
76
+ #
77
+ UnrecognizedTableContent = Class.new(StandardError)
78
+
79
+ end
80
+ end
@@ -0,0 +1,413 @@
1
+ # encoding: utf-8
2
+ #
3
+ # font.rb : The Prawn font class
4
+ #
5
+ # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ require_relative "font/afm"
10
+ require_relative "font/ttf"
11
+ require_relative "font/dfont"
12
+ require_relative "font_metric_cache"
13
+
14
+ module Prawn
15
+
16
+ class Document
17
+ # @group Stable API
18
+
19
+ # Without arguments, this returns the currently selected font. Otherwise,
20
+ # it sets the current font. When a block is used, the font is applied
21
+ # transactionally and is rolled back when the block exits.
22
+ #
23
+ # Prawn::Document.generate("font.pdf") do
24
+ # text "Default font is Helvetica"
25
+ #
26
+ # font "Times-Roman"
27
+ # text "Now using Times-Roman"
28
+ #
29
+ # font("DejaVuSans.ttf") do
30
+ # text "Using TTF font from file DejaVuSans.ttf"
31
+ # font "Courier", :style => :bold
32
+ # text "You see this in bold Courier"
33
+ # end
34
+ #
35
+ # text "Times-Roman, again"
36
+ # end
37
+ #
38
+ # The :name parameter must be a string. It can be one of the 14 built-in
39
+ # fonts supported by PDF, or the location of a TTF file. The Font::AFM::BUILT_INS
40
+ # array specifies the valid built in font values.
41
+ #
42
+ # If a ttf font is specified, the glyphs necessary to render your document
43
+ # will be embedded in the rendered PDF. This should be your preferred option
44
+ # in most cases. It will increase the size of the resulting file, but also
45
+ # make it more portable.
46
+ #
47
+ # The options parameter is an optional hash providing size and style. To use
48
+ # the :style option you need to map those font styles to their respective font files.
49
+ # See font_families for more information.
50
+ #
51
+ def font(name=nil, options={})
52
+ return((defined?(@font) && @font) || font("Helvetica")) if name.nil?
53
+
54
+ if state.pages.empty? && !state.page.in_stamp_stream?
55
+ raise Prawn::Errors::NotOnPage
56
+ end
57
+
58
+ new_font = find_font(name.to_s, options)
59
+
60
+ if block_given?
61
+ save_font do
62
+ set_font(new_font, options[:size])
63
+ yield
64
+ end
65
+ else
66
+ set_font(new_font, options[:size])
67
+ end
68
+
69
+ @font
70
+ end
71
+
72
+ # @method font_size(points=nil)
73
+ #
74
+ # When called with no argument, returns the current font size.
75
+ #
76
+ # When called with a single argument but no block, sets the current font
77
+ # size. When a block is used, the font size is applied transactionally and
78
+ # is rolled back when the block exits. You may still change the font size
79
+ # within a transactional block for individual text segments, or nested calls
80
+ # to font_size.
81
+ #
82
+ # Prawn::Document.generate("font_size.pdf") do
83
+ # font_size 16
84
+ # text "At size 16"
85
+ #
86
+ # font_size(10) do
87
+ # text "At size 10"
88
+ # text "At size 6", :size => 6
89
+ # text "At size 10"
90
+ # end
91
+ #
92
+ # text "At size 16"
93
+ # end
94
+ #
95
+ # When called without an argument, this method returns the current font
96
+ # size.
97
+ #
98
+ def font_size(points=nil)
99
+ return @font_size unless points
100
+ size_before_yield = @font_size
101
+ @font_size = points
102
+ block_given? ? yield : return
103
+ @font_size = size_before_yield
104
+ end
105
+
106
+ # Sets the font size
107
+ def font_size=(size)
108
+ font_size(size)
109
+ end
110
+
111
+ # Returns the width of the given string using the given font. If :size is not
112
+ # specified as one of the options, the string is measured using the current
113
+ # font size. You can also pass :kerning as an option to indicate whether
114
+ # kerning should be used when measuring the width (defaults to +false+).
115
+ #
116
+ # Note that the string _must_ be encoded properly for the font being used.
117
+ # For AFM fonts, this is WinAnsi. For TTF, make sure the font is encoded as
118
+ # UTF-8. You can use the Font#normalize_encoding method to make sure strings
119
+ # are in an encoding appropriate for the current font.
120
+ #--
121
+ # For the record, this method used to be a method of Font (and still delegates
122
+ # to width computations on Font). However, having the primary interface for
123
+ # calculating string widths exist on Font made it tricky to write extensions
124
+ # for Prawn in which widths are computed differently (e.g., taking formatting
125
+ # tags into account, or the like).
126
+ #
127
+ # By putting width_of here, on Document itself, extensions may easily override
128
+ # it and redefine the width calculation behavior.
129
+ #++
130
+ def width_of(string, options={})
131
+ if p = options[:inline_format]
132
+ p = [] unless p.is_a?(Array)
133
+
134
+ # Build up an Arranger with the entire string on one line, finalize it,
135
+ # and find its width.
136
+ arranger = Prawn::Text::Formatted::Arranger.new(self, options)
137
+ arranger.consumed = self.text_formatter.format(string, *p)
138
+ arranger.finalize_line
139
+
140
+ arranger.line_width
141
+ else
142
+ width_of_string(string, options)
143
+ end
144
+ end
145
+
146
+ # Hash that maps font family names to their styled individual font names.
147
+ #
148
+ # To add support for another font family, append to this hash, e.g:
149
+ #
150
+ # pdf.font_families.update(
151
+ # "MyTrueTypeFamily" => { :bold => "foo-bold.ttf",
152
+ # :italic => "foo-italic.ttf",
153
+ # :bold_italic => "foo-bold-italic.ttf",
154
+ # :normal => "foo.ttf" })
155
+ #
156
+ # This will then allow you to use the fonts like so:
157
+ #
158
+ # pdf.font("MyTrueTypeFamily", :style => :bold)
159
+ # pdf.text "Some bold text"
160
+ # pdf.font("MyTrueTypeFamily")
161
+ # pdf.text "Some normal text"
162
+ #
163
+ # This assumes that you have appropriate TTF fonts for each style you
164
+ # wish to support.
165
+ #
166
+ # By default the styles :bold, :italic, :bold_italic, and :normal are
167
+ # defined for fonts "Courier", "Times-Roman" and "Helvetica". When
168
+ # defining your own font families, you can map any or all of these
169
+ # styles to whatever font files you'd like.
170
+ #
171
+ def font_families
172
+ @font_families ||= {}.merge!(
173
+ { "Courier" => { :bold => "Courier-Bold",
174
+ :italic => "Courier-Oblique",
175
+ :bold_italic => "Courier-BoldOblique",
176
+ :normal => "Courier" },
177
+
178
+ "Times-Roman" => { :bold => "Times-Bold",
179
+ :italic => "Times-Italic",
180
+ :bold_italic => "Times-BoldItalic",
181
+ :normal => "Times-Roman" },
182
+
183
+ "Helvetica" => { :bold => "Helvetica-Bold",
184
+ :italic => "Helvetica-Oblique",
185
+ :bold_italic => "Helvetica-BoldOblique",
186
+ :normal => "Helvetica" }
187
+ })
188
+ end
189
+
190
+ # @group Experimental API
191
+
192
+ # Sets the font directly, given an actual Font object
193
+ # and size.
194
+ #
195
+ def set_font(font, size=nil) # :nodoc:
196
+ @font = font
197
+ @font_size = size if size
198
+ end
199
+
200
+ # Saves the current font, and then yields. When the block
201
+ # finishes, the original font is restored.
202
+ #
203
+ def save_font
204
+ @font ||= find_font("Helvetica")
205
+ original_font = @font
206
+ original_size = @font_size
207
+
208
+ yield
209
+ ensure
210
+ set_font(original_font, original_size) if original_font
211
+ end
212
+
213
+ # Looks up the given font using the given criteria. Once a font has been
214
+ # found by that matches the criteria, it will be cached to subsequent lookups
215
+ # for that font will return the same object.
216
+ #--
217
+ # Challenges involved: the name alone is not sufficient to uniquely identify
218
+ # a font (think dfont suitcases that can hold multiple different fonts in a
219
+ # single file). Thus, the :name key is included in the cache key.
220
+ #
221
+ # It is further complicated, however, since fonts in some formats (like the
222
+ # dfont suitcases) can be identified either by numeric index, OR by their
223
+ # name within the suitcase, and both should hash to the same font object
224
+ # (to avoid the font being embedded multiple times). This is not yet implemented,
225
+ # which means if someone selects a font both by name, and by index, the
226
+ # font will be embedded twice. Since we do font subsetting, this double
227
+ # embedding won't be catastrophic, just annoying.
228
+ # ++
229
+ #
230
+ # @private
231
+ def find_font(name, options={}) #:nodoc:
232
+ if font_families.key?(name)
233
+ family, name = name, font_families[name][options[:style] || :normal]
234
+ if name.is_a?(::Hash)
235
+ options = options.merge(name)
236
+ name = options[:file]
237
+ end
238
+ end
239
+ key = "#{name}:#{options[:font] || 0}"
240
+
241
+ if name.is_a? Prawn::Font
242
+ font_registry[key] = name
243
+ else
244
+ font_registry[key] ||= Font.load( self,
245
+ name,
246
+ options.merge(family: family)
247
+ )
248
+ end
249
+ end
250
+
251
+ # Hash of Font objects keyed by names
252
+ #
253
+ def font_registry #:nodoc:
254
+ @font_registry ||= {}
255
+ end
256
+
257
+ private
258
+
259
+ def width_of_inline_formatted_string(string, options={})
260
+ # Build up an Arranger with the entire string on one line, finalize it,
261
+ # and find its width.
262
+ arranger = Prawn::Text::Formatted::Arranger.new(self, options)
263
+ arranger.consumed = Text::Formatted::Parser.format(string)
264
+ arranger.finalize_line
265
+
266
+ arranger.line_width
267
+ end
268
+
269
+ def width_of_string(string, options={})
270
+ font_metric_cache.width_of( string, options )
271
+ end
272
+ end
273
+
274
+ # Provides font information and helper functions.
275
+ #
276
+ class Font
277
+
278
+ # The current font name
279
+ attr_reader :name
280
+
281
+ # The current font family
282
+ attr_reader :family
283
+
284
+ # The options hash used to initialize the font
285
+ attr_reader :options
286
+
287
+ # Shortcut interface for constructing a font object. Filenames of the form
288
+ # *.ttf will call Font::TTF.new, *.dfont Font::DFont.new, and anything else
289
+ # will be passed through to Font::AFM.new()
290
+ def self.load(document, src, options={})
291
+ case font_format(src, options)
292
+ when 'ttf' then TTF.new(document, src, options)
293
+ when 'dfont' then DFont.new(document, src, options)
294
+ else AFM.new(document, src, options)
295
+ end
296
+ end
297
+
298
+ def self.font_format(src, options)
299
+ return options.fetch(:format, 'ttf') if src.respond_to? :read
300
+
301
+ case src.to_s
302
+ when /\.ttf$/i then return 'ttf'
303
+ when /\.dfont$/i then return 'dfont'
304
+ else return 'afm'
305
+ end
306
+ end
307
+
308
+ def initialize(document,name,options={}) #:nodoc:
309
+ @document = document
310
+ @name = name
311
+ @options = options
312
+
313
+ @family = options[:family]
314
+
315
+ @identifier = generate_unique_id
316
+
317
+ @references = {}
318
+ end
319
+
320
+ # The size of the font ascender in PDF points
321
+ #
322
+ def ascender
323
+ @ascender / 1000.0 * size
324
+ end
325
+
326
+ # The size of the font descender in PDF points
327
+ #
328
+ def descender
329
+ -@descender / 1000.0 * size
330
+ end
331
+
332
+ # The size of the recommended gap between lines of text in PDF points
333
+ #
334
+ def line_gap
335
+ @line_gap / 1000.0 * size
336
+ end
337
+
338
+ # Normalizes the encoding of the string to an encoding supported by the
339
+ # font. The string is expected to be UTF-8 going in. It will be re-encoded
340
+ # and the new string will be returned. For an in-place (destructive)
341
+ # version, see normalize_encoding!.
342
+ def normalize_encoding(string)
343
+ raise NotImplementedError, "subclasses of Prawn::Font must implement #normalize_encoding"
344
+ end
345
+
346
+ # Destructive version of normalize_encoding; normalizes the encoding of a
347
+ # string in place.
348
+ #
349
+ def normalize_encoding!(str)
350
+ str.replace(normalize_encoding(str))
351
+ end
352
+
353
+ # Gets height of current font in PDF points at the given font size
354
+ #
355
+ def height_at(size)
356
+ @normalized_height ||= (@ascender - @descender + @line_gap) / 1000.0
357
+ @normalized_height * size
358
+ end
359
+
360
+ # Gets height of current font in PDF points at current font size
361
+ #
362
+ def height
363
+ height_at(size)
364
+ end
365
+
366
+ # Registers the given subset of the current font with the current PDF
367
+ # page. This is safe to call multiple times for a given font and subset,
368
+ # as it will only add the font the first time it is called.
369
+ #
370
+ def add_to_current_page(subset)
371
+ @references[subset] ||= register(subset)
372
+ @document.state.page.fonts.merge!(identifier_for(subset) => @references[subset])
373
+ end
374
+
375
+ def identifier_for(subset) #:nodoc:
376
+ "#{@identifier}.#{subset}"
377
+ end
378
+
379
+ def inspect #:nodoc:
380
+ "#{self.class.name}< #{name}: #{size} >"
381
+ end
382
+
383
+ # Return a hash (as in Object#hash) for the font based on the output of
384
+ # #inspect. This is required since font objects are used as keys in hashes
385
+ # that cache certain values (See
386
+ # Prawn::Table::Text#styled_with_of_single_character)
387
+ #
388
+ def hash #:nodoc:
389
+ [ self.class, self.name, self.family, size ].hash
390
+ end
391
+
392
+ # Compliments the #hash implementation above
393
+ #
394
+ def eql?( other ) #:nodoc:
395
+ self.class == other.class && self.name == other.name &&
396
+ self.family == other.family && size == other.send(:size)
397
+ end
398
+
399
+ private
400
+
401
+ # generate a font identifier that hasn't been used on the current page yet
402
+ #
403
+ def generate_unique_id
404
+ :"F#{@document.font_registry.size + 1}"
405
+ end
406
+
407
+ def size
408
+ @document.font_size
409
+ end
410
+
411
+ end
412
+
413
+ end