fullcirclegroup-fullcirclegroup-prawn 0.2.99.2

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,196 @@
1
+ # encoding: utf-8
2
+
3
+ # text.rb : Implements PDF text primitives
4
+ #
5
+ # Copyright May 2008, Gregory Brown. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ require "zlib"
9
+ require "prawn/document/text/box"
10
+
11
+ module Prawn
12
+ class Document
13
+ module Text
14
+ # Draws text on the page. If a point is specified via the +:at+
15
+ # option the text will begin exactly at that point, and the string is
16
+ # assumed to be pre-formatted to properly fit the page.
17
+ #
18
+ # pdf.text "Hello World", :at => [100,100]
19
+ # pdf.text "Goodbye World", :at => [50,50], :size => 16
20
+ #
21
+ # When +:at+ is not specified, Prawn attempts to wrap the text to
22
+ # fit within your current bounding box (or margin_box if no bounding box
23
+ # is being used ). Text will flow onto the next page when it reaches
24
+ # the bottom of the bounding box. Text wrap in Prawn does not re-flow
25
+ # linebreaks, so if you want fully automated text wrapping, be sure to
26
+ # remove newlines before attempting to draw your string.
27
+ #
28
+ # pdf.text "Will be wrapped when it hits the edge of your bounding box"
29
+ # pdf.text "This will be centered", :align => :center
30
+ # pdf.text "This will be right aligned", :align => :right
31
+ #
32
+ # Wrapping is done by splitting words by spaces by default. If your text
33
+ # does not contain spaces, you can wrap based on characters instead:
34
+ #
35
+ # pdf.text "This will be wrapped by character", :wrap => :character
36
+ #
37
+ # If your font contains kerning pairs data that Prawn can parse, the
38
+ # text will be kerned by default. You can disable this feature by passing
39
+ # <tt>:kerning => false</tt>.
40
+ #
41
+ # === Text Positioning Details:
42
+ #
43
+ # FIXME: If we go with this of using ascender for TTF and font height
44
+ # For AFM, we need to document the sucker.
45
+ #
46
+ # When using the +:at+ parameter, Prawn will position your text by its
47
+ # baseline, and flow along a single line.
48
+ #
49
+ # When using automatic text flow, Prawn will position your text exactly
50
+ # font.height *below* the baseline, and space each line of text by
51
+ # font.height + options[:spacing] (default 0)
52
+ #
53
+ # Finally, the drawing position will be moved to the baseline of final
54
+ # line of text, plus any additional spacing.
55
+ #
56
+ # If you wish to position your flowing text by it's baseline rather
57
+ # than +font.height+ below, simply call <tt>move_up font.height</tt>
58
+ # before your call to text()
59
+ #
60
+ # == Rotation
61
+ #
62
+ # Text can be rotated before it is placed on the canvas by specifying the
63
+ # :rotate option. Rotation occurs counter-clockwise.
64
+ #
65
+ # == Encoding
66
+ #
67
+ # Note that strings passed to this function should be encoded as UTF-8.
68
+ # If you get unexpected characters appearing in your rendered document,
69
+ # check this.
70
+ #
71
+ # If the current font is a built-in one, although the string must be
72
+ # encoded as UTF-8, only characters that are available in WinAnsi
73
+ # are allowed.
74
+ #
75
+ # If an empty box is rendered to your PDF instead of the character you
76
+ # wanted it usually means the current font doesn't include that character.
77
+ #
78
+ def text(text,options={})
79
+ # we'll be messing with the strings encoding, don't change the users
80
+ # original string
81
+ text = text.to_s.dup
82
+
83
+ # we might also mess with the font
84
+ original_font = font.name
85
+
86
+ options = text_options.merge(options)
87
+ process_text_options(options)
88
+
89
+ font.normalize_encoding(text) unless @skip_encoding
90
+
91
+ if options[:at]
92
+ x,y = translate(options[:at])
93
+ font.size(options[:size]) { add_text_content(text,x,y,options) }
94
+ else
95
+ if options[:rotate]
96
+ raise ArgumentError, "Rotated text may only be used with :at"
97
+ end
98
+ wrapped_text(text,options)
99
+ end
100
+
101
+ font(original_font)
102
+ end
103
+
104
+ # A hash of configuration options, to be used globally by text().
105
+ #
106
+ # pdf.text_options.update(:size => 16, :align => :right)
107
+ # pdf.text "Hello World" #=> Size 16 w. right alignment
108
+ #
109
+ def text_options
110
+ @text_options ||= {}
111
+ end
112
+
113
+ private
114
+
115
+ def process_text_options(options)
116
+ Prawn.verify_options [:style, :kerning, :size, :at, :wrap,
117
+ :spacing, :align, :rotate ], options
118
+
119
+ if options[:style]
120
+ raise "Bad font family" unless font.family
121
+ font(font.family,:style => options[:style])
122
+ end
123
+
124
+ unless options.key?(:kerning)
125
+ options[:kerning] = font.metrics.has_kerning_data?
126
+ end
127
+
128
+ options[:size] ||= font.size
129
+ end
130
+
131
+ def move_text_position(dy)
132
+ bottom = @bounding_box.stretchy? ? @margin_box.absolute_bottom :
133
+ @bounding_box.absolute_bottom
134
+ start_new_page if (y - dy) < bottom
135
+
136
+ self.y -= dy
137
+ end
138
+
139
+ def wrapped_text(text,options)
140
+ options[:align] ||= :left
141
+
142
+ font.size(options[:size]) do
143
+ text = font.metrics.naive_wrap(text, bounds.right, font.size,
144
+ :kerning => options[:kerning], :mode => options[:wrap])
145
+
146
+ lines = text.lines
147
+
148
+ lines.each do |e|
149
+ if font.metrics.type0?
150
+ move_text_position(font.ascender)
151
+ else
152
+ move_text_position(font.height)
153
+ end
154
+
155
+ line_width = font.width_of(e)
156
+ case(options[:align])
157
+ when :left
158
+ x = @bounding_box.absolute_left
159
+ when :center
160
+ x = @bounding_box.absolute_left +
161
+ (@bounding_box.width - line_width) / 2.0
162
+ when :right
163
+ x = @bounding_box.absolute_right - line_width
164
+ end
165
+
166
+ add_text_content(e,x,y,options)
167
+
168
+ if font.metrics.type0?
169
+ move_text_position(font.height - font.ascender)
170
+ end
171
+
172
+ move_text_position(options[:spacing]) if options[:spacing]
173
+ end
174
+ end
175
+ end
176
+
177
+ def add_text_content(text, x, y, options)
178
+ text = font.metrics.convert_text(text,options)
179
+
180
+ add_content "\nBT"
181
+ add_content "/#{font.identifier} #{font.size} Tf"
182
+ if options[:rotate]
183
+ rad = options[:rotate].to_i * Math::PI / 180
184
+ arr = [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ]
185
+ add_content "%.3f %.3f %.3f %.3f %.3f %.3f Tm" % arr
186
+ else
187
+ add_content "#{x} #{y} Td"
188
+ end
189
+ rad = 1.570796
190
+ add_content Prawn::PdfObject(text, true) <<
191
+ " #{options[:kerning] ? 'TJ' : 'Tj'}"
192
+ add_content "ET\n"
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,48 @@
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
+ class FailedObjectConversion < StandardError; end
16
+
17
+ # This error is raised when Document#page_layout is set to anything
18
+ # other than :portrait or :landscape
19
+ #
20
+ class InvalidPageLayout < StandardError; end
21
+
22
+ # This error is raised when Prawn cannot find a specified font
23
+ #
24
+ class UnknownFont < StandardError; end
25
+
26
+ # This error is raised when Prawn is being used on a M17N aware VM,
27
+ # and the user attempts to add text that isn't compatible with UTF-8
28
+ # to their document
29
+ #
30
+ class IncompatibleStringEncoding < StandardError; end
31
+
32
+ # This error is raised when Prawn encounters an unknown key in functions
33
+ # that accept an options hash. This usually means there is a typo in your
34
+ # code or that the option you are trying to use has a different name than
35
+ # what you have specified.
36
+ #
37
+ class UnknownOption < StandardError; end
38
+
39
+ # This error is raised when table data is malformed
40
+ #
41
+ class InvalidTableData < StandardError; end
42
+
43
+ # This error is raised when an empty or nil table is rendered
44
+ #
45
+ class EmptyTable < StandardError; end
46
+
47
+ end
48
+ end
data/lib/prawn/font.rb ADDED
@@ -0,0 +1,356 @@
1
+ # encoding: utf-8
2
+
3
+ require "prawn/font/wrapping"
4
+ require "prawn/font/metrics"
5
+ require "prawn/font/cmap"
6
+
7
+ module Prawn
8
+
9
+ class Document
10
+ # Without arguments, this returns the currently selected font. Otherwise,
11
+ # it sets the current font.
12
+ #
13
+ # The single parameter must be a string. It can be one of the 14 built-in
14
+ # fonts supported by PDF, or the location of a TTF file. The BUILT_INS
15
+ # array specifies the valid built in font values.
16
+ #
17
+ # pdf.font "Times-Roman"
18
+ # pdf.font "Chalkboard.ttf"
19
+ #
20
+ # If a ttf font is specified, the full file will be embedded in the
21
+ # rendered PDF. This should be your preferred option in most cases.
22
+ # It will increase the size of the resulting file, but also make it
23
+ # more portable.
24
+ #
25
+ def font(name=nil, options={})
26
+ return @font || font("Helvetica") if name.nil?
27
+
28
+ if block_given?
29
+ original_name = font.name
30
+ original_size = font.size
31
+ end
32
+
33
+ @font = find_font(name, options)
34
+ @font.add_to_current_page
35
+
36
+ @font.size = options[:size] if options[:size]
37
+
38
+ if block_given?
39
+ yield
40
+ font(original_name, :size => original_size)
41
+ else
42
+ @font
43
+ end
44
+ end
45
+
46
+ # Looks up the given font name. Once a font has been found by that name,
47
+ # it will be cached to subsequent lookups for that font will return the
48
+ # same object.
49
+ def find_font(name, options={}) #:nodoc:
50
+ if font_families.key?(name)
51
+ family, name = name, font_families[name][options[:style] || :normal]
52
+ end
53
+
54
+ font_registry[name] ||= Font.new(name, options.merge(:for => self, :family => family))
55
+ end
56
+
57
+ # Hash of Font objects keyed by names
58
+ #
59
+ def font_registry #:nodoc:
60
+ @font_registry ||= {}
61
+ end
62
+
63
+ # Hash that maps font family names to their styled individual font names
64
+ #
65
+ # To add support for another font family, append to this hash, e.g:
66
+ #
67
+ # pdf.font_families.update(
68
+ # "MyTrueTypeFamily" => { :bold => "foo-bold.ttf",
69
+ # :italic => "foo-italic.ttf",
70
+ # :bold_italic => "foo-bold-italic.ttf",
71
+ # :normal => "foo.ttf" })
72
+ #
73
+ # This will then allow you to use the fonts like so:
74
+ #
75
+ # pdf.font("MyTrueTypeFamily", :style => :bold)
76
+ # pdf.text "Some bold text"
77
+ # pdf.font("MyTrueTypeFamily")
78
+ # pdf.text "Some normal text"
79
+ #
80
+ # This assumes that you have appropriate TTF fonts for each style you
81
+ # wish to support.
82
+ #
83
+ def font_families
84
+ @font_families ||= Hash.new { |h,k| h[k] = {} }.merge!(
85
+ { "Courier" => { :bold => "Courier-Bold",
86
+ :italic => "Courier-Oblique",
87
+ :bold_italic => "Courier-BoldOblique",
88
+ :normal => "Courier" },
89
+
90
+ "Times-Roman" => { :bold => "Times-Bold",
91
+ :italic => "Times-Italic",
92
+ :bold_italic => "Times-BoldItalic",
93
+ :normal => "Times-Roman" },
94
+
95
+ "Helvetica" => { :bold => "Helvetica-Bold",
96
+ :italic => "Helvetica-Oblique",
97
+ :bold_italic => "Helvetica-BoldOblique",
98
+ :normal => "Helvetica" }
99
+ })
100
+ end
101
+ end
102
+
103
+ # Provides font information and helper functions.
104
+ #
105
+ class Font
106
+
107
+ BUILT_INS = %w[ Courier Helvetica Times-Roman Symbol ZapfDingbats
108
+ Courier-Bold Courier-Oblique Courier-BoldOblique
109
+ Times-Bold Times-Italic Times-BoldItalic
110
+ Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique ]
111
+
112
+ DEFAULT_SIZE = 12
113
+
114
+ # The font metrics object
115
+ attr_reader :metrics
116
+
117
+ # The current font name
118
+ attr_reader :name
119
+
120
+ # The current font family
121
+ attr_reader :family
122
+
123
+ attr_reader :identifier, :reference #:nodoc:
124
+
125
+ # Sets the size of the current font:
126
+ #
127
+ # font.size = 16
128
+ #
129
+ attr_writer :size
130
+
131
+ def initialize(name,options={}) #:nodoc:
132
+ @name = name
133
+ @family = options[:family]
134
+
135
+ @metrics = Prawn::Font::Metrics[name]
136
+ @document = options[:for]
137
+
138
+ @document.proc_set :PDF, :Text
139
+ @size = DEFAULT_SIZE
140
+ @identifier = :"F#{@document.font_registry.size + 1}"
141
+
142
+ @reference = nil
143
+ end
144
+
145
+ def inspect
146
+ "Prawn::Font< #{name}: #{size} >"
147
+ end
148
+
149
+ # Sets the default font size for use within a block. Individual overrides
150
+ # can be used as desired. The previous font size will be restored after the
151
+ # block.
152
+ #
153
+ # Prawn::Document.generate("font_size.pdf") do
154
+ # font.size = 16
155
+ # text "At size 16"
156
+ #
157
+ # font.size(10) do
158
+ # text "At size 10"
159
+ # text "At size 6", :size => 6
160
+ # text "At size 10"
161
+ # end
162
+ #
163
+ # text "At size 16"
164
+ # end
165
+ #
166
+ # When called without an argument, this method returns the current font
167
+ # size.
168
+ #
169
+ def size(points=nil)
170
+ return @size unless points
171
+ size_before_yield = @size
172
+ @size = points
173
+ yield
174
+ @size = size_before_yield
175
+ end
176
+
177
+ # Gets width of string in PDF points at current font size
178
+ #
179
+ # If using an AFM, string *must* be encoded as WinAnsi
180
+ # (Use normalize_encoding to convert)
181
+ #
182
+ def width_of(string)
183
+ @metrics.string_width(string,@size)
184
+ end
185
+
186
+ # Gets height of text in PDF points at current font size.
187
+ # Text +:line_width+ must be specified in PDF points.
188
+ #
189
+ # If using an AFM, string *must* be encoded as WinAnsi
190
+ # (Use normalize_encoding to convert)
191
+ #
192
+ def height_of(text,options={})
193
+ @metrics.string_height( text, :font_size => @size,
194
+ :line_width => options[:line_width] )
195
+ end
196
+
197
+ # Gets height of current font in PDF points at current font size
198
+ #
199
+ def height
200
+ @metrics.font_height(@size)
201
+ end
202
+
203
+ # The height of the ascender at the current font size in PDF points
204
+ #
205
+ def ascender
206
+ @metrics.ascender / 1000.0 * @size
207
+ end
208
+
209
+ # The height of the descender at the current font size in PDF points
210
+ #
211
+ def descender
212
+ @metrics.descender / 1000.0 * @size
213
+ end
214
+
215
+ def line_gap
216
+ @metrics.line_gap / 1000.0 * @size
217
+ end
218
+
219
+ def normalize_encoding(text) # :nodoc:
220
+ # check the string is encoded sanely
221
+ # - UTF-8 for TTF fonts
222
+ # - ISO-8859-1 for Built-In fonts
223
+ if @metrics.type0?
224
+ normalize_ttf_encoding(text)
225
+ else
226
+ normalize_builtin_encoding(text)
227
+ end
228
+ end
229
+
230
+ def add_to_current_page #:nodoc:
231
+ embed! unless @reference
232
+ @document.page_fonts.merge!(@identifier => @reference)
233
+ end
234
+
235
+ private
236
+
237
+ def embed!
238
+ case(name)
239
+ when /\.ttf$/i
240
+ embed_ttf(name)
241
+ else
242
+ register_builtin(name)
243
+ end
244
+ end
245
+
246
+ # built-in fonts only work with winansi encoding, so translate the string
247
+ def normalize_builtin_encoding(text)
248
+ enc = Prawn::Encoding::WinAnsi.new
249
+ text.replace text.unpack("U*").collect { |i| enc[i] }.pack("C*")
250
+ end
251
+
252
+ def normalize_ttf_encoding(text)
253
+ # TODO: if the current font is a built in one, we can't use the utf-8
254
+ # string provided by the user. We should convert it to WinAnsi or
255
+ # MacRoman or some such.
256
+ if text.respond_to?(:encode!)
257
+ # if we're running under a M17n aware VM, ensure the string provided is
258
+ # UTF-8 (by converting it if necessary)
259
+ begin
260
+ text.encode!("UTF-8")
261
+ rescue
262
+ raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
263
+ "#{text.encoding} can not be transparently converted to UTF-8. " +
264
+ "Please ensure the encoding of the string you are attempting " +
265
+ "to use is set correctly"
266
+ end
267
+ else
268
+ # on a non M17N aware VM, use unpack as a hackish way to verify the
269
+ # string is valid utf-8. I thought it was better than loading iconv
270
+ # though.
271
+ begin
272
+ text.unpack("U*")
273
+ rescue
274
+ raise Prawn::Errors::IncompatibleStringEncoding, "The string you " +
275
+ "are attempting to render is not encoded in valid UTF-8."
276
+ end
277
+ end
278
+ end
279
+
280
+ def register_builtin(name)
281
+ unless BUILT_INS.include?(name)
282
+ raise Prawn::Errors::UnknownFont, "#{name} is not a known font."
283
+ end
284
+
285
+ @reference = @document.ref( :Type => :Font,
286
+ :Subtype => :Type1,
287
+ :BaseFont => name.to_sym,
288
+ :Encoding => :WinAnsiEncoding)
289
+ end
290
+
291
+ def embed_ttf(file)
292
+ unless File.file?(file)
293
+ raise ArgumentError, "file #{file} does not exist"
294
+ end
295
+
296
+ basename = @metrics.basename
297
+
298
+ raise "Can't detect a postscript name for #{file}" if basename.nil?
299
+
300
+ @encodings = @metrics.cmap
301
+
302
+ if @encodings.nil?
303
+ raise "#{file} missing the required encoding table"
304
+ end
305
+
306
+ font_content = File.open(file,"rb") { |f| f.read }
307
+ compressed_font = Zlib::Deflate.deflate(font_content)
308
+
309
+ fontfile = @document.ref(:Length => compressed_font.size,
310
+ :Length1 => font_content.size,
311
+ :Filter => :FlateDecode )
312
+ fontfile << compressed_font
313
+
314
+ # TODO: Not sure what to do about CapHeight, as ttf2afm doesn't
315
+ # pick it up. Missing proper StemV and flags
316
+ #
317
+ descriptor = @document.ref(:Type => :FontDescriptor,
318
+ :FontName => basename,
319
+ :FontFile2 => fontfile,
320
+ :FontBBox => @metrics.bbox,
321
+ :Flags => 32, # FIXME: additional flags
322
+ :StemV => 0,
323
+ :ItalicAngle => 0,
324
+ :Ascent => @metrics.ascender,
325
+ :Descent => @metrics.descender )
326
+
327
+ descendant = @document.ref(:Type => :Font,
328
+ :Subtype => :CIDFontType2, # CID, TTF
329
+ :BaseFont => basename,
330
+ :CIDSystemInfo => { :Registry => "Adobe",
331
+ :Ordering => "Identity",
332
+ :Supplement => 0 },
333
+ :FontDescriptor => descriptor,
334
+ :W => @metrics.glyph_widths,
335
+ :CIDToGIDMap => :Identity )
336
+
337
+ to_unicode_content = @metrics.to_unicode_cmap.to_s
338
+ compressed_to_unicode = Zlib::Deflate.deflate(to_unicode_content)
339
+
340
+ to_unicode = @document.ref(:Length => compressed_to_unicode.size,
341
+ :Length1 => to_unicode_content.size,
342
+ :Filter => :FlateDecode )
343
+ to_unicode << compressed_to_unicode
344
+
345
+ @reference = @document.ref(:Type => :Font,
346
+ :Subtype => :Type0,
347
+ :BaseFont => basename,
348
+ :DescendantFonts => [descendant],
349
+ :Encoding => :"Identity-H",
350
+ :ToUnicode => to_unicode)
351
+
352
+ end
353
+
354
+ end
355
+
356
+ end