prawn-core 0.5.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 (186) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +56 -0
  3. data/README +121 -0
  4. data/Rakefile +74 -0
  5. data/data/encodings/win_ansi.txt +29 -0
  6. data/data/fonts/Action Man.dfont +0 -0
  7. data/data/fonts/Activa.ttf +0 -0
  8. data/data/fonts/Chalkboard.ttf +0 -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/DejaVuSans.ttf +0 -0
  14. data/data/fonts/Dustismo_Roman.ttf +0 -0
  15. data/data/fonts/Helvetica-Bold.afm +2827 -0
  16. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  17. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  18. data/data/fonts/Helvetica.afm +3051 -0
  19. data/data/fonts/MustRead.html +19 -0
  20. data/data/fonts/Symbol.afm +213 -0
  21. data/data/fonts/Times-Bold.afm +2588 -0
  22. data/data/fonts/Times-BoldItalic.afm +2384 -0
  23. data/data/fonts/Times-Italic.afm +2667 -0
  24. data/data/fonts/Times-Roman.afm +2419 -0
  25. data/data/fonts/ZapfDingbats.afm +225 -0
  26. data/data/fonts/comicsans.ttf +0 -0
  27. data/data/fonts/gkai00mp.ttf +0 -0
  28. data/data/images/16bit.alpha +0 -0
  29. data/data/images/16bit.dat +0 -0
  30. data/data/images/16bit.png +0 -0
  31. data/data/images/arrow.png +0 -0
  32. data/data/images/arrow2.png +0 -0
  33. data/data/images/barcode_issue.png +0 -0
  34. data/data/images/dice.alpha +0 -0
  35. data/data/images/dice.dat +0 -0
  36. data/data/images/dice.png +0 -0
  37. data/data/images/dice_interlaced.png +0 -0
  38. data/data/images/fractal.jpg +0 -0
  39. data/data/images/letterhead.jpg +0 -0
  40. data/data/images/page_white_text.alpha +0 -0
  41. data/data/images/page_white_text.dat +0 -0
  42. data/data/images/page_white_text.png +0 -0
  43. data/data/images/pigs.jpg +0 -0
  44. data/data/images/rails.dat +0 -0
  45. data/data/images/rails.png +0 -0
  46. data/data/images/ruport.png +0 -0
  47. data/data/images/ruport_data.dat +0 -0
  48. data/data/images/ruport_transparent.png +0 -0
  49. data/data/images/ruport_type0.png +0 -0
  50. data/data/images/stef.jpg +0 -0
  51. data/data/images/tru256.bmp +0 -0
  52. data/data/images/web-links.dat +1 -0
  53. data/data/images/web-links.png +0 -0
  54. data/data/shift_jis_text.txt +1 -0
  55. data/examples/bounding_box/bounding_boxes.rb +44 -0
  56. data/examples/bounding_box/russian_boxes.rb +37 -0
  57. data/examples/column_box/column_box_example.rb +44 -0
  58. data/examples/general/background.rb +20 -0
  59. data/examples/general/canvas.rb +16 -0
  60. data/examples/general/measurement_units.rb +52 -0
  61. data/examples/general/metadata-info.rb +17 -0
  62. data/examples/general/multi_page_layout.rb +17 -0
  63. data/examples/general/page_geometry.rb +32 -0
  64. data/examples/graphics/basic_images.rb +24 -0
  65. data/examples/graphics/cmyk.rb +13 -0
  66. data/examples/graphics/curves.rb +12 -0
  67. data/examples/graphics/hexagon.rb +14 -0
  68. data/examples/graphics/image_fit.rb +16 -0
  69. data/examples/graphics/image_flow.rb +38 -0
  70. data/examples/graphics/image_position.rb +18 -0
  71. data/examples/graphics/line.rb +33 -0
  72. data/examples/graphics/png_types.rb +23 -0
  73. data/examples/graphics/polygons.rb +17 -0
  74. data/examples/graphics/remote_images.rb +12 -0
  75. data/examples/graphics/ruport_style_helpers.rb +20 -0
  76. data/examples/graphics/stroke_bounds.rb +21 -0
  77. data/examples/m17n/chinese_text_wrapping.rb +20 -0
  78. data/examples/m17n/euro.rb +16 -0
  79. data/examples/m17n/sjis.rb +29 -0
  80. data/examples/m17n/utf8.rb +14 -0
  81. data/examples/m17n/win_ansi_charset.rb +55 -0
  82. data/examples/text/alignment.rb +19 -0
  83. data/examples/text/dfont.rb +49 -0
  84. data/examples/text/family_based_styling.rb +25 -0
  85. data/examples/text/font_calculations.rb +92 -0
  86. data/examples/text/font_size.rb +34 -0
  87. data/examples/text/kerning.rb +31 -0
  88. data/examples/text/simple_text.rb +18 -0
  89. data/examples/text/simple_text_ttf.rb +18 -0
  90. data/examples/text/span.rb +30 -0
  91. data/examples/text/text_box.rb +26 -0
  92. data/examples/text/text_flow.rb +68 -0
  93. data/lib/prawn/compatibility.rb +38 -0
  94. data/lib/prawn/core.rb +79 -0
  95. data/lib/prawn/document.rb +399 -0
  96. data/lib/prawn/document/annotations.rb +63 -0
  97. data/lib/prawn/document/bounding_box.rb +377 -0
  98. data/lib/prawn/document/column_box.rb +89 -0
  99. data/lib/prawn/document/destinations.rb +81 -0
  100. data/lib/prawn/document/internals.rb +133 -0
  101. data/lib/prawn/document/page_geometry.rb +149 -0
  102. data/lib/prawn/document/span.rb +55 -0
  103. data/lib/prawn/document/text.rb +186 -0
  104. data/lib/prawn/document/text/box.rb +83 -0
  105. data/lib/prawn/document/text/wrapping.rb +59 -0
  106. data/lib/prawn/encoding.rb +121 -0
  107. data/lib/prawn/errors.rb +49 -0
  108. data/lib/prawn/font.rb +292 -0
  109. data/lib/prawn/font/afm.rb +202 -0
  110. data/lib/prawn/font/dfont.rb +31 -0
  111. data/lib/prawn/font/ttf.rb +327 -0
  112. data/lib/prawn/graphics.rb +257 -0
  113. data/lib/prawn/graphics/color.rb +141 -0
  114. data/lib/prawn/images.rb +339 -0
  115. data/lib/prawn/images/jpg.rb +45 -0
  116. data/lib/prawn/images/png.rb +217 -0
  117. data/lib/prawn/literal_string.rb +14 -0
  118. data/lib/prawn/measurement_extensions.rb +46 -0
  119. data/lib/prawn/measurements.rb +71 -0
  120. data/lib/prawn/name_tree.rb +165 -0
  121. data/lib/prawn/pdf_object.rb +77 -0
  122. data/lib/prawn/reference.rb +59 -0
  123. data/spec/annotations_spec.rb +90 -0
  124. data/spec/bounding_box_spec.rb +141 -0
  125. data/spec/destinations_spec.rb +15 -0
  126. data/spec/document_spec.rb +178 -0
  127. data/spec/font_spec.rb +274 -0
  128. data/spec/graphics_spec.rb +209 -0
  129. data/spec/images_spec.rb +79 -0
  130. data/spec/jpg_spec.rb +25 -0
  131. data/spec/measurement_units_spec.rb +23 -0
  132. data/spec/name_tree_spec.rb +103 -0
  133. data/spec/pdf_object_spec.rb +117 -0
  134. data/spec/png_spec.rb +236 -0
  135. data/spec/reference_spec.rb +42 -0
  136. data/spec/span_spec.rb +45 -0
  137. data/spec/spec_helper.rb +23 -0
  138. data/spec/text_box_spec.rb +83 -0
  139. data/spec/text_spec.rb +178 -0
  140. data/vendor/pdf-inspector/README +18 -0
  141. data/vendor/pdf-inspector/lib/pdf/inspector.rb +25 -0
  142. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +80 -0
  143. data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +16 -0
  144. data/vendor/pdf-inspector/lib/pdf/inspector/text.rb +31 -0
  145. data/vendor/pdf-inspector/lib/pdf/inspector/xobject.rb +19 -0
  146. data/vendor/ttfunk/data/fonts/DejaVuSans.ttf +0 -0
  147. data/vendor/ttfunk/data/fonts/comicsans.ttf +0 -0
  148. data/vendor/ttfunk/example.rb +45 -0
  149. data/vendor/ttfunk/lib/ttfunk.rb +102 -0
  150. data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
  151. data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
  152. data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
  153. data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
  154. data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
  155. data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
  156. data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
  157. data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +46 -0
  158. data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
  159. data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
  160. data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +51 -0
  161. data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
  162. data/vendor/ttfunk/lib/ttfunk/table.rb +46 -0
  163. data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +34 -0
  164. data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
  165. data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
  166. data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
  167. data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
  168. data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
  169. data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
  170. data/vendor/ttfunk/lib/ttfunk/table/head.rb +44 -0
  171. data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +41 -0
  172. data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +47 -0
  173. data/vendor/ttfunk/lib/ttfunk/table/kern.rb +79 -0
  174. data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
  175. data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
  176. data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +40 -0
  177. data/vendor/ttfunk/lib/ttfunk/table/name.rb +119 -0
  178. data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
  179. data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
  180. data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
  181. data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
  182. data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
  183. data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
  184. data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
  185. data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
  186. metadata +245 -0
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+
3
+ # text/box.rb : Implements simple text boxes
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
+
12
+ # Defines an invisible rectangle which you can flow text in. When the
13
+ # text overflows the box, you can either display :ellipses, :truncate
14
+ # the text, or allow it to overflow the bottom boundary with :expand.
15
+ #
16
+ # text_box "Oh hai text box. " * 200,
17
+ # :width => 300, :height => font.height * 5,
18
+ # :overflow => :ellipses,
19
+ # :at => [100,bounds.top]
20
+ #
21
+ def text_box(text,options)
22
+ Text::Box.new(text, options.merge(:for => self)).render
23
+ end
24
+
25
+ module Text
26
+ class Box #:nodoc:
27
+ def initialize(text,options={})
28
+ @document = options[:for]
29
+ @text = text
30
+ @at = options[:at] || [0, @document.y - @document.bounds.absolute_bottom]
31
+ @width = options[:width] || @document.bounds.width
32
+ @height = options[:height]
33
+ @overflow = options[:overflow] || :truncate
34
+ end
35
+
36
+ attr_reader :text, :width, :height, :overflow
37
+
38
+ def render
39
+ x,y = @at
40
+
41
+ if @overflow == :expand
42
+ @text = naive_wrap_text
43
+ else
44
+ original_y = @document.y
45
+ fit_text_to_box
46
+ end
47
+
48
+ @document.bounding_box([x,@document.bounds.top],
49
+ :width => @width, :height => @document.bounds.height) do
50
+ @document.y = @document.bounds.absolute_bottom + y
51
+ @document.text @text
52
+ end
53
+
54
+ unless @overflow == :expand
55
+ @document.y = y + @document.bounds.absolute_bottom - @height
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def fit_text_to_box
62
+ text = naive_wrap_text
63
+
64
+ max_lines = (@height / @document.font.height).floor
65
+
66
+ lines = text.lines.to_a
67
+
68
+ if lines.length > max_lines
69
+ @text = lines[0...max_lines].join
70
+ case(@overflow)
71
+ when :ellipses
72
+ @text[-3..-1] = "..." if @text.size > 3
73
+ end
74
+ end
75
+ end
76
+
77
+ def naive_wrap_text
78
+ @document.naive_wrap(@text, @width, @document.font_size)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ # wrapping.rb : Implementation of naive text wrap
4
+ #
5
+ # Copyright May 2008, Michael Daines. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ module Prawn
9
+ class Document
10
+ module Text
11
+ module Wrapping #:nodoc:
12
+ ruby_18 { $KCODE="U" }
13
+
14
+ # Gets height of text in PDF points at current font size.
15
+ # Text +:line_width+ must be specified in PDF points.
16
+ #
17
+ # If using an AFM, string *must* be encoded as WinAnsi
18
+ # (Use normalize_encoding to convert)
19
+ #
20
+ def height_of(string, line_width, size=font_size)
21
+ string = naive_wrap(string, line_width, size)
22
+ string.lines.to_a.length * font.height_at(size)
23
+ end
24
+
25
+ # TODO: Replace with TeX optimal algorithm
26
+ def naive_wrap(string, line_width, font_size, options = {})
27
+ scan_pattern = options[:mode] == :character ? /./ : /\S+|\s+/
28
+
29
+ output = ""
30
+ string.lines.each do |line|
31
+ accumulated_width = 0
32
+ segments = line.scan(scan_pattern)
33
+
34
+ segments.each do |segment|
35
+ segment_width = width_of(segment, :size => font_size, :kerning => options[:kerning])
36
+
37
+ if (accumulated_width + segment_width).round > line_width.round
38
+ output = "#{output.sub(/[ \t]*\n?(\n*)\z/, "\n\\1")}"
39
+
40
+ if segment =~ /\s/
41
+ accumulated_width = 0
42
+ else
43
+ output << segment
44
+ accumulated_width = segment_width
45
+ end
46
+ else
47
+ output << segment
48
+ accumulated_width += segment_width
49
+ end
50
+ end
51
+ end
52
+
53
+ output
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,121 @@
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
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
+
86
+ def initialize
87
+ @mapping_file = "#{Prawn::BASEDIR}/data/encodings/win_ansi.txt"
88
+ load_mapping if self.class.mapping.empty?
89
+ end
90
+
91
+ # Converts a Unicode codepoint into a valid WinAnsi single byte character.
92
+ #
93
+ # If there is no WinAnsi equivlant for a character, a _ will be substituted.
94
+ #
95
+ def [](codepoint)
96
+ # unicode codepoints < 255 map directly to the single byte value in WinAnsi
97
+ return codepoint if codepoint <= 255
98
+
99
+ # There are a handful of codepoints > 255 that have equivilants in WinAnsi.
100
+ # Replace anything else with an underscore
101
+ self.class.mapping[codepoint] || 95
102
+ end
103
+
104
+ def self.mapping
105
+ @mapping ||= {}
106
+ end
107
+
108
+ private
109
+
110
+ def load_mapping
111
+ RUBY_VERSION >= "1.9" ? mode = "r:BINARY" : mode = "r"
112
+ File.open(@mapping_file, mode) do |f|
113
+ f.each do |l|
114
+ m, single_byte, unicode = *l.match(/([0-9A-Za-z]+);([0-9A-F]{4})/)
115
+ self.class.mapping["0x#{unicode}".hex] = "0x#{single_byte}".hex if single_byte
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,49 @@
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
+
12
+ # This error is raised when Prawn::PdfObject() encounters a Ruby object it
13
+ # cannot convert to PDF
14
+ #
15
+ FailedObjectConversion = Class.new(StandardError)
16
+
17
+ # This error is raised when Document#page_layout is set to anything
18
+ # other than :portrait or :landscape
19
+ #
20
+ InvalidPageLayout = Class.new(StandardError)
21
+
22
+ # This error is raised when a method requiring a current page is called
23
+ # without being on a page.
24
+ #
25
+ NotOnPage = Class.new(StandardError)
26
+
27
+ # This error is raised when Prawn cannot find a specified font
28
+ #
29
+ UnknownFont = Class.new(StandardError)
30
+
31
+ # This error is raised when Prawn is being used on a M17N aware VM,
32
+ # and the user attempts to add text that isn't compatible with UTF-8
33
+ # to their document
34
+ #
35
+ IncompatibleStringEncoding = Class.new(StandardError)
36
+
37
+ # This error is raised when Prawn encounters an unknown key in functions
38
+ # that accept an options hash. This usually means there is a typo in your
39
+ # code or that the option you are trying to use has a different name than
40
+ # what you have specified.
41
+ #
42
+ UnknownOption = Class.new(StandardError)
43
+
44
+ # this error is raised when a user attempts to embed an image of an unsupported
45
+ # type. This can either a completely unsupported format, or a dialect of a
46
+ # supported format (ie. some types of PNG)
47
+ UnsupportedImageType = Class.new(StandardError)
48
+ end
49
+ end
@@ -0,0 +1,292 @@
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
+ require "prawn/font/afm"
9
+ require "prawn/font/ttf"
10
+ require "prawn/font/dfont"
11
+
12
+ module Prawn
13
+
14
+ class Document
15
+ # Without arguments, this returns the currently selected font. Otherwise,
16
+ # it sets the current font.
17
+ #
18
+ # The single parameter must be a string. It can be one of the 14 built-in
19
+ # fonts supported by PDF, or the location of a TTF file. The Font::AFM::BUILT_INS
20
+ # array specifies the valid built in font values.
21
+ #
22
+ # pdf.font "Times-Roman"
23
+ # pdf.font "Chalkboard.ttf"
24
+ #
25
+ # If a ttf font is specified, the glyphs necessary to render your document
26
+ # will be embedded in the rendered PDF. This should be your preferred option
27
+ # in most cases. It will increase the size of the resulting file, but also
28
+ # make it more portable.
29
+ #
30
+ def font(name=nil, options={})
31
+ return @font || font("Helvetica") if name.nil?
32
+
33
+ raise Errors::NotOnPage unless @current_page
34
+ new_font = find_font(name, options)
35
+
36
+ if block_given?
37
+ save_font do
38
+ set_font(new_font, options[:size])
39
+ yield
40
+ end
41
+ else
42
+ set_font(new_font, options[:size])
43
+ end
44
+
45
+ @font
46
+ end
47
+
48
+ # When called with no argument, returns the current font size.
49
+ # When called with a single argument but no block, sets the current font
50
+ # size. When a block is used, the font size is applied transactionally and
51
+ # is rolled back when the block exits. You may still change the font size
52
+ # within a transactional block for individual text segments, or nested calls
53
+ # to font_size.
54
+ #
55
+ # Prawn::Document.generate("font_size.pdf") do
56
+ # font_size 16
57
+ # text "At size 16"
58
+ #
59
+ # font_size(10) do
60
+ # text "At size 10"
61
+ # text "At size 6", :size => 6
62
+ # text "At size 10"
63
+ # end
64
+ #
65
+ # text "At size 16"
66
+ # end
67
+ #
68
+ # When called without an argument, this method returns the current font
69
+ # size.
70
+ #
71
+ def font_size(points=nil)
72
+ return @font_size unless points
73
+ size_before_yield = @font_size
74
+ @font_size = points
75
+ block_given? ? yield : return
76
+ @font_size = size_before_yield
77
+ end
78
+
79
+ # Sets the font directly, given an actual Font object
80
+ # and size.
81
+ def set_font(font, size=nil) # :nodoc:
82
+ @font = font
83
+ @font_size = size if size
84
+ end
85
+
86
+ # Saves the current font, and then yields. When the block
87
+ # finishes, the original font is restored.
88
+ def save_font
89
+ @font ||= find_font("Helvetica")
90
+ original_font = @font
91
+ original_size = @font_size
92
+
93
+ yield
94
+ ensure
95
+ set_font(original_font, original_size) if original_font
96
+ end
97
+
98
+ # Looks up the given font using the given criteria. Once a font has been
99
+ # found by that matches the criteria, it will be cached to subsequent lookups
100
+ # for that font will return the same object.
101
+ #--
102
+ # Challenges involved: the name alone is not sufficient to uniquely identify
103
+ # a font (think dfont suitcases that can hold multiple different fonts in a
104
+ # single file). Thus, the :name key is included in the cache key.
105
+ #
106
+ # It is further complicated, however, since fonts in some formats (like the
107
+ # dfont suitcases) can be identified either by numeric index, OR by their
108
+ # name within the suitcase, and both should hash to the same font object
109
+ # (to avoid the font being embedded multiple times). This is not yet implemented,
110
+ # which means if someone selects a font both by name, and by index, the
111
+ # font will be embedded twice. Since we do font subsetting, this double
112
+ # embedding won't be catastrophic, just annoying.
113
+ # ++
114
+ #
115
+ def find_font(name, options={}) #:nodoc:
116
+ if font_families.key?(name)
117
+ family, name = name, font_families[name][options[:style] || :normal]
118
+
119
+ if name.is_a?(Hash)
120
+ options = options.merge(name)
121
+ name = options[:file]
122
+ end
123
+ end
124
+
125
+ key = "#{name}:#{options[:font] || 0}"
126
+ font_registry[key] ||= Font.load(self, name, options.merge(:family => family))
127
+ end
128
+
129
+ # Hash of Font objects keyed by names
130
+ #
131
+ def font_registry #:nodoc:
132
+ @font_registry ||= {}
133
+ end
134
+
135
+ # Hash that maps font family names to their styled individual font names
136
+ #
137
+ # To add support for another font family, append to this hash, e.g:
138
+ #
139
+ # pdf.font_families.update(
140
+ # "MyTrueTypeFamily" => { :bold => "foo-bold.ttf",
141
+ # :italic => "foo-italic.ttf",
142
+ # :bold_italic => "foo-bold-italic.ttf",
143
+ # :normal => "foo.ttf" })
144
+ #
145
+ # This will then allow you to use the fonts like so:
146
+ #
147
+ # pdf.font("MyTrueTypeFamily", :style => :bold)
148
+ # pdf.text "Some bold text"
149
+ # pdf.font("MyTrueTypeFamily")
150
+ # pdf.text "Some normal text"
151
+ #
152
+ # This assumes that you have appropriate TTF fonts for each style you
153
+ # wish to support.
154
+ #
155
+ def font_families
156
+ @font_families ||= Hash.new { |h,k| h[k] = {} }.merge!(
157
+ { "Courier" => { :bold => "Courier-Bold",
158
+ :italic => "Courier-Oblique",
159
+ :bold_italic => "Courier-BoldOblique",
160
+ :normal => "Courier" },
161
+
162
+ "Times-Roman" => { :bold => "Times-Bold",
163
+ :italic => "Times-Italic",
164
+ :bold_italic => "Times-BoldItalic",
165
+ :normal => "Times-Roman" },
166
+
167
+ "Helvetica" => { :bold => "Helvetica-Bold",
168
+ :italic => "Helvetica-Oblique",
169
+ :bold_italic => "Helvetica-BoldOblique",
170
+ :normal => "Helvetica" }
171
+ })
172
+ end
173
+
174
+ # Returns the width of the given string using the given font. If :size is not
175
+ # specified as one of the options, the string is measured using the current
176
+ # font size. You can also pass :kerning as an option to indicate whether
177
+ # kerning should be used when measuring the width (defaults to +false+).
178
+ #
179
+ # Note that the string _must_ be encoded properly for the font being used.
180
+ # For AFM fonts, this is WinAnsi. For TTF, make sure the font is encoded as
181
+ # UTF-8. You can use the Font#normalize_encoding method to make sure strings
182
+ # are in an encoding appropriate for the current font.
183
+ #--
184
+ # For the record, this method used to be a method of Font (and still delegates
185
+ # to width computations on Font). However, having the primary interface for
186
+ # calculating string widths exist on Font made it tricky to write extensions
187
+ # for Prawn in which widths are computed differently (e.g., taking formatting
188
+ # tags into account, or the like).
189
+ #
190
+ # By putting width_of here, on Document itself, extensions may easily override
191
+ # it and redefine the width calculation behavior.
192
+ #++
193
+ def width_of(string, options={})
194
+ font.compute_width_of(string, options)
195
+ end
196
+ end
197
+
198
+ # Provides font information and helper functions.
199
+ #
200
+ class Font
201
+
202
+ # The current font name
203
+ attr_reader :name
204
+
205
+ # The current font family
206
+ attr_reader :family
207
+
208
+ # The options hash used to initialize the font
209
+ attr_reader :options
210
+
211
+ def self.load(document,name,options={})
212
+ case name
213
+ when /\.ttf$/ then TTF.new(document, name, options)
214
+ when /\.dfont$/ then DFont.new(document, name, options)
215
+ when /\.afm$/ then AFM.new(document, name, options)
216
+ else AFM.new(document, name, options)
217
+ end
218
+ end
219
+
220
+ def initialize(document,name,options={}) #:nodoc:
221
+ @document = document
222
+ @name = name
223
+ @options = options
224
+
225
+ @family = options[:family]
226
+
227
+ @document.proc_set :PDF, :Text
228
+ @identifier = :"F#{@document.font_registry.size + 1}"
229
+
230
+ @references = {}
231
+ end
232
+
233
+ def ascender
234
+ @ascender / 1000.0 * size
235
+ end
236
+
237
+ def descender
238
+ @descender / 1000.0 * size
239
+ end
240
+
241
+ def line_gap
242
+ @line_gap / 1000.0 * size
243
+ end
244
+
245
+ def identifier_for(subset)
246
+ "#{@identifier}.#{subset}"
247
+ end
248
+
249
+ def inspect
250
+ "#{self.class.name}< #{name}: #{size} >"
251
+ end
252
+
253
+ # Normalizes the encoding of the string to an encoding supported by the font.
254
+ # The string is expected to be UTF-8 going in, and will be reencoded in-place
255
+ # (the argument will be modified directly). The return value is not defined.
256
+ def normalize_encoding(string)
257
+ raise NotImplementedError, "subclasses of Prawn::Font must implement #normalize_encoding"
258
+ end
259
+
260
+ def normalize_encoding!(str)
261
+ str.replace(normalize_encoding(str))
262
+ end
263
+
264
+ def height_at(size)
265
+ @normalized_height ||= (@ascender - @descender + @line_gap) / 1000.0
266
+ @normalized_height * size
267
+ end
268
+
269
+ # Gets height of current font in PDF points at current font size
270
+ #
271
+ def height
272
+ height_at(size)
273
+ end
274
+
275
+ # Registers the given subset of the current font with the current PDF
276
+ # page. This is safe to call multiple times for a given font and subset,
277
+ # as it will only add the font the first time it is called.
278
+ #
279
+ def add_to_current_page(subset)
280
+ @references[subset] ||= register(subset)
281
+ @document.page_fonts.merge!(identifier_for(subset) => @references[subset])
282
+ end
283
+
284
+ private
285
+
286
+ def size
287
+ @document.font_size
288
+ end
289
+
290
+ end
291
+
292
+ end