fullcirclegroup-prawn 0.2.99.3
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.
- data/COPYING +340 -0
- data/LICENSE +56 -0
- data/README +47 -0
- data/Rakefile +76 -0
- data/data/fonts/Activa.ttf +0 -0
- data/data/fonts/Chalkboard.ttf +0 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/Dustismo_Roman.ttf +0 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/data/fonts/gkai00mp.ttf +0 -0
- data/data/images/arrow.png +0 -0
- data/data/images/arrow2.png +0 -0
- data/data/images/barcode_issue.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.dat +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.dat +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/rails.dat +0 -0
- data/data/images/rails.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/web-links.dat +1 -0
- data/data/images/web-links.png +0 -0
- data/data/shift_jis_text.txt +1 -0
- data/examples/addressbook.csv +6 -0
- data/examples/alignment.rb +16 -0
- data/examples/bounding_boxes.rb +30 -0
- data/examples/canvas.rb +12 -0
- data/examples/cell.rb +38 -0
- data/examples/chinese_text_wrapping.rb +17 -0
- data/examples/currency.csv +1834 -0
- data/examples/curves.rb +10 -0
- data/examples/family_based_styling.rb +21 -0
- data/examples/fancy_table.rb +61 -0
- data/examples/flowing_text_with_header_and_footer.rb +72 -0
- data/examples/font_size.rb +27 -0
- data/examples/hexagon.rb +14 -0
- data/examples/image.rb +23 -0
- data/examples/image2.rb +13 -0
- data/examples/image_flow.rb +34 -0
- data/examples/kerning.rb +27 -0
- data/examples/lazy_bounding_boxes.rb +19 -0
- data/examples/line.rb +31 -0
- data/examples/multi_page_layout.rb +14 -0
- data/examples/page_geometry.rb +28 -0
- data/examples/png_types.rb +23 -0
- data/examples/polygons.rb +16 -0
- data/examples/position_by_baseline.rb +26 -0
- data/examples/ruport_formatter.rb +50 -0
- data/examples/ruport_helpers.rb +18 -0
- data/examples/russian_boxes.rb +34 -0
- data/examples/simple_text.rb +15 -0
- data/examples/simple_text_ttf.rb +16 -0
- data/examples/sjis.rb +21 -0
- data/examples/span.rb +27 -0
- data/examples/table.rb +47 -0
- data/examples/table_header_color.rb +16 -0
- data/examples/text_flow.rb +65 -0
- data/examples/top_and_bottom_cells.rb +40 -0
- data/examples/utf8.rb +12 -0
- data/lib/prawn.rb +67 -0
- data/lib/prawn/compatibility.rb +46 -0
- data/lib/prawn/document.rb +309 -0
- data/lib/prawn/document/bounding_box.rb +362 -0
- data/lib/prawn/document/internals.rb +113 -0
- data/lib/prawn/document/page_geometry.rb +79 -0
- data/lib/prawn/document/span.rb +47 -0
- data/lib/prawn/document/table.rb +350 -0
- data/lib/prawn/document/text.rb +196 -0
- data/lib/prawn/errors.rb +48 -0
- data/lib/prawn/font.rb +356 -0
- data/lib/prawn/font/cmap.rb +59 -0
- data/lib/prawn/font/metrics.rb +378 -0
- data/lib/prawn/font/wrapping.rb +47 -0
- data/lib/prawn/graphics.rb +252 -0
- data/lib/prawn/graphics/cell.rb +264 -0
- data/lib/prawn/graphics/color.rb +132 -0
- data/lib/prawn/images.rb +336 -0
- data/lib/prawn/images/jpg.rb +45 -0
- data/lib/prawn/images/png.rb +199 -0
- data/lib/prawn/pdf_object.rb +73 -0
- data/lib/prawn/reference.rb +56 -0
- data/spec/bounding_box_spec.rb +141 -0
- data/spec/document_spec.rb +181 -0
- data/spec/font_spec.rb +141 -0
- data/spec/graphics_spec.rb +209 -0
- data/spec/images_spec.rb +68 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/metrics_spec.rb +62 -0
- data/spec/pdf_object_spec.rb +112 -0
- data/spec/png_spec.rb +196 -0
- data/spec/reference_spec.rb +42 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/table_spec.rb +179 -0
- data/spec/text_spec.rb +135 -0
- 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
|
data/lib/prawn/errors.rb
ADDED
|
@@ -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
|