diff --git a/lib/prawn/document.rb b/lib/prawn/document.rb
index 439667d5a..f12983450 100644
--- a/lib/prawn/document.rb
+++ b/lib/prawn/document.rb
@@ -218,6 +218,7 @@ def initialize(options = {}, &block)
@background = options[:background]
@background_scale = options[:background_scale] || 1
+ @font = nil
@font_size = 12
@bounding_box = nil
diff --git a/lib/prawn/font.rb b/lib/prawn/font.rb
index 6eb4dac94..0959ea355 100644
--- a/lib/prawn/font.rb
+++ b/lib/prawn/font.rb
@@ -47,14 +47,15 @@ class Document
# font files.
# See font_families for more information.
#
- def font(name = nil, options = DEFAULT_OPTS)
- return((defined?(@font) && @font) || font('Helvetica')) if name.nil?
+ def font(name = nil, options = nil)
+ return @font || font('Helvetica') if name.nil? && options.nil?
if state.pages.empty? && !state.page.in_stamp_stream?
raise Prawn::Errors::NotOnPage
end
- new_font = find_font(name.to_s, options)
+ options ||= DEFAULT_OPTS
+ new_font = find_font(name, options)
if block_given?
save_font do
@@ -68,6 +69,54 @@ def font(name = nil, options = DEFAULT_OPTS)
@font
end
+ # Returns the family name of the current font, if the font was selected using
+ # a font family name, or nil, if the font was specified as a specific built-in
+ # font or a TTF file.
+ def font_family
+ @font ? @font.family : 'Helvetica'
+ end
+
+ # @method font_style(points=nil)
+ #
+ # When called with no argument, returns the current font style.
+ #
+ # When called with a single argument but no block, sets the current font
+ # style. When a block is used, the font style is applied transactionally and
+ # is rolled back when the block exits. You may still change the font style
+ # within a transactional block for individual text segments, or nested calls
+ # to font_style.
+ #
+ # Prawn::Document.generate("font_style.pdf") do
+ # font_style :bold
+ # text "Bold text"
+ #
+ # font_style(:italic) do
+ # text "Italic text"
+ # text "Bold text", :style => :bold
+ # text "Italic text"
+ # end
+ #
+ # text "Bold text"
+ # end
+ #
+ # Font styles are not additive, i.e. if the current style is :bold, setting
+ # the style to :italic results in italic, not bold-italic text. Use
+ # :bold_italic instead.
+ #
+ # Font style can only be applied, if the current font is specified using a
+ # font family name, not a specific built-in font or TTF file.
+ #
+ def font_style(style = nil, &block)
+ return @font ? @font.style : :normal if style.nil?
+
+ font(nil, style: style, &block)
+ end
+
+ # Sets the font style
+ def font_style=(style = nil)
+ font_style(style)
+ end
+
# @method font_size(points=nil)
#
# When called with no argument, returns the current font size.
@@ -91,9 +140,6 @@ def font(name = nil, options = DEFAULT_OPTS)
# text "At size 16"
# end
#
- # When called without an argument, this method returns the current font
- # size.
- #
def font_size(points = nil)
return @font_size unless points
@@ -237,13 +283,22 @@ def save_font
#
# @private
def find_font(name, options = {}) #:nodoc:
+ name ||= font_family || font.name
+ style = options[:style] || :normal
if font_families.key?(name)
family = name
- name = font_families[name][options[:style] || :normal]
+ if !font_families[family].key?(style)
+ raise Prawn::Errors::UnknownFont,
+ "Font family `#{family}` has no `:#{style}` style."
+ end
+ name = font_families[family][style]
if name.is_a?(::Hash)
options = options.merge(name)
name = options[:file]
end
+ elsif style != :normal
+ options[:style] = :normal
+ warn "Style not supported for `#{name}`."
end
key = "#{name}:#{options[:font] || 0}"
@@ -299,6 +354,9 @@ class Font
# The current font family
attr_reader :family
+ # The current font style
+ attr_reader :style
+
# The options hash used to initialize the font
attr_reader :options
@@ -334,6 +392,7 @@ def initialize(document, name, options = {}) #:nodoc:
@options = options
@family = options[:family]
+ @style = options[:style] || :normal
@identifier = generate_unique_id
diff --git a/lib/prawn/font_metric_cache.rb b/lib/prawn/font_metric_cache.rb
index 1f49f8a53..2db8b16c2 100644
--- a/lib/prawn/font_metric_cache.rb
+++ b/lib/prawn/font_metric_cache.rb
@@ -25,7 +25,7 @@ def width_of(string, options)
f =
if options[:style]
# override style with :style => :bold
- @document.find_font(@document.font.family, style: options[:style])
+ @document.find_font(nil, style: options[:style])
else
@document.font
end
diff --git a/lib/prawn/fonts/afm.rb b/lib/prawn/fonts/afm.rb
index dabd772b9..8d7795858 100644
--- a/lib/prawn/fonts/afm.rb
+++ b/lib/prawn/fonts/afm.rb
@@ -55,7 +55,7 @@ def initialize(document, name, options = {}) #:nodoc:
name ||= options[:family]
unless BUILT_INS.include?(name)
raise Prawn::Errors::UnknownFont,
- "#{name} (#{options[:style] || 'normal'}) is not a known font."
+ "`#{name}` is not a known font."
end
super
diff --git a/manual/text/font.rb b/manual/text/font.rb
index e4b353f5b..97bc4f530 100644
--- a/manual/text/font.rb
+++ b/manual/text/font.rb
@@ -5,11 +5,12 @@
# If we don't pass it any arguments it will return the current font being used
# to render text.
#
-# If we just pass it a font name it will use that font for rendering text
-# through the rest of the document.
+# If we just pass it a font name and an options hash, it will use that font for
+# rendering text through the rest of the document. Both the font name and
+# the options can be omitted. Valid option keys are :style and :size.
#
-# It can also be used by passing a font name and a block. In this case the
-# specified font will only be used to render text inside the block.
+# It can also be used by passing a font name, an options hash and a block. In this
+# case the specified font will only be used to render text inside the block.
#
# The default font is Helvetica.
@@ -18,6 +19,7 @@
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
Prawn::ManualBuilder::Example.generate(filename) do
text "Let's see which font we are using: #{font.inspect}"
+ text "Family is #{font_family}, style is #{font_style}, and size is #{font_size}"
move_down 20
font 'Times-Roman'
@@ -31,6 +33,11 @@
move_down 20
text 'Written in Times again as we left the previous block.'
+ move_down 20
+ font nil, size: 16, style: :bold do
+ text 'Written in 16pt bold, still Times.'
+ end
+
move_down 20
text "Let's see which font we are using again: #{font.inspect}"
diff --git a/manual/text/font_style.rb b/manual/text/font_style.rb
index c6fd844e2..19f8cc5aa 100644
--- a/manual/text/font_style.rb
+++ b/manual/text/font_style.rb
@@ -1,25 +1,51 @@
# frozen_string_literal: true
+# The font_style
method works just like the font
+# method.
+#
+# In fact we can even use font
with the :style
option
+# to declare which size we want.
+#
+# Another way to change the font size is by supplying the :style
+# option to the text methods.
+#
# Most font families come with some styles other than normal. Most common are
# bold
, italic
and bold_italic
.
-#
-# The style can be set the using the :style
option, with either the
-# font
method which will set the font and style for rest of the
-# document, or with the inline text methods.
require_relative '../example_helper'
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
Prawn::ManualBuilder::Example.generate(filename) do
- fonts = %w[Courier Helvetica Times-Roman]
- styles = %i[bold bold_italic italic normal]
+ text "The default style is normal"
+
+ move_down 10
+ font_style :bold
+ text 'This is bold'
- fonts.each do |example_font|
- move_down 20
+ move_down 10
+ font_style :italic
+ text 'This is italic (not bold, i.e. existing style is overwritten)'
- styles.each do |style|
- font example_font, style: style
- text "I'm writing in #{example_font} (#{style})"
- end
+ move_down 10
+ font_style :bold_italic
+ text 'This is bold italic'
+
+ move_down 10
+ font_style :normal
+ text 'Back to normal'
+
+ move_down 10
+ text 'A single line of italic', style: :italic
+
+ move_down 10
+ font_style :bold do
+ text 'A single line of bold'
end
+
+ move_down 10
+ font 'Courier', style: :bold_italic
+ text 'This is Courier bold italic'
+
+ font 'Courier'
+ text 'This is Courier normal (style is reset when changing font)'
end
diff --git a/spec/prawn/font_spec.rb b/spec/prawn/font_spec.rb
index 60ed4ebf0..53e920aa6 100644
--- a/spec/prawn/font_spec.rb
+++ b/spec/prawn/font_spec.rb
@@ -71,12 +71,19 @@
end
end
- it 'reports missing font with style' do
+ it 'raises on missing style in family' do
expect do
- pdf.font('Nada', style: :bold) do
- pdf.width_of('hello')
+ pdf.font('Helvetica') do
+ pdf.width_of('hello', style: :heavy)
end
- end.to raise_error(Prawn::Errors::UnknownFont, /Nada \(bold\)/)
+ end.to raise_error(Prawn::Errors::UnknownFont, 'Font family `Helvetica` has no `:heavy` style.')
+ end
+
+ it 'warns on style with single font' do
+ expect(pdf).to receive(:warn).with('Style not supported for `Courier-Bold`.').once
+ pdf.font('Courier-Bold') do
+ pdf.width_of('hello', style: :bold)
+ end
end
it 'calculates styled widths correctly using TTFs' do
@@ -111,11 +118,153 @@
end
end
+ describe '#font' do
+ it 'allows setting of size directly when font is created' do
+ pdf.font 'Courier', size: 16 #, style: :bold
+ expect(pdf.font_size).to eq(16)
+ end
+
+ it 'allows temporary setting of a new font using a transaction' do
+ pdf.font 'Helvetica', size: 12
+
+ pdf.font 'Courier', size: 16, style: :bold do
+ expect(pdf.font_family).to eq('Courier')
+ expect(pdf.font_size).to eq(16)
+ expect(pdf.font_style).to eq(:bold)
+ end
+
+ expect(pdf.font.name).to eq('Helvetica')
+ expect(pdf.font_size).to eq(12)
+ expect(pdf.font_style).to eq(:normal)
+ end
+
+ it 'masks font size when using a transacation' do
+ pdf.font 'Courier', size: 16 do
+ expect(pdf.font_size).to eq(16)
+ end
+
+ pdf.font 'Times-Roman'
+ pdf.font 'Courier'
+
+ expect(pdf.font_size).to eq(12)
+ end
+
+ it 'raises on unknown font' do
+ expect do
+ pdf.font 'Arial Black'
+ end.to raise_error(Prawn::Errors::UnknownFont, '`Arial Black` is not a known font.')
+ end
+
+ it 'raises on missing style in family' do
+ expect do
+ pdf.font 'Helvetica', style: :heavy
+ end.to raise_error(Prawn::Errors::UnknownFont, 'Font family `Helvetica` has no `:heavy` style.')
+ end
+
+ it 'warns on style with single font' do
+ expect(pdf).to receive(:warn).with('Style not supported for `Courier-Bold`.').once
+ pdf.font 'Courier-Bold', style: :bold
+ expect(pdf.font.name).to eq('Courier-Bold')
+ expect(pdf.font_style).to eq(:normal)
+ end
+ end
+
describe '#font_size' do
+ it 'returns default font size' do
+ expect(pdf.font_size).to eq(12)
+ end
+
it 'allows setting font size in DSL style' do
pdf.font_size 20
expect(pdf.font_size).to eq(20)
end
+
+ it 'allows setting font size in DSL style using a transaction' do
+ pdf.font_size 20 do
+ expect(pdf.font_size).to eq(20)
+ end
+
+ expect(pdf.font_size).to eq(12)
+ end
+
+ it 'allows setting font size as assignment' do
+ pdf.font_size = 20
+ expect(pdf.font_size).to eq(20)
+ end
+ end
+
+ describe '#font_style' do
+ it 'returns default font style' do
+ expect(pdf.font_style).to eq(:normal)
+ end
+
+ it 'allows setting font style in DSL style' do
+ pdf.font_style :bold
+ expect(pdf.font_style).to eq(:bold)
+ expect(pdf.font.name).to eq('Helvetica-Bold')
+ end
+
+ it 'allows setting font style in DSL style using a transaction' do
+ pdf.font_style :bold do
+ expect(pdf.font_style).to eq(:bold)
+ expect(pdf.font.name).to eq('Helvetica-Bold')
+ end
+
+ expect(pdf.font_style).to eq(:normal)
+ expect(pdf.font.name).to eq('Helvetica')
+ end
+
+ it 'allows setting font style for a TTF font' do
+ pdf.font_families.update(
+ 'deja' => {
+ normal: "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf",
+ bold: "#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf"
+ }
+ )
+ pdf.font 'deja'
+ expect(pdf.font_style).to eq(:normal)
+ pdf.font_style :bold
+ expect(pdf.font_style).to eq(:bold)
+ expect(pdf.font_family).to eq('deja')
+ expect(pdf.font.name).to eq("#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf")
+ end
+
+ it 'allows setting font style as assignment' do
+ pdf.font_style = :bold
+ expect(pdf.font_style).to eq(:bold)
+ end
+
+ it 'raises on missing style in family' do
+ expect do
+ pdf.font('Helvetica') do
+ pdf.font_style :heavy
+ end
+ end.to raise_error(Prawn::Errors::UnknownFont, 'Font family `Helvetica` has no `:heavy` style.')
+ end
+
+ it 'warns on style with single font' do
+ expect(pdf).to receive(:warn).with('Style not supported for `Courier-Bold`.').once
+ pdf.font('Courier-Bold') do
+ pdf.font_style :bold
+ expect(pdf.font_style).to eq(:normal)
+ end
+ end
+ end
+
+ describe '#font_family' do
+ it 'returns default font family' do
+ expect(pdf.font_family).to eq('Helvetica')
+ end
+
+ it 'returns font family' do
+ pdf.font 'Courier', style: :bold
+ expect(pdf.font_family).to eq('Courier')
+ end
+
+ it 'returns nil font family for specific font' do
+ pdf.font 'ZapfDingbats'
+ expect(pdf.font_family).to be_nil
+ end
end
describe 'font style support' do
@@ -142,11 +291,14 @@
pdf.font 'Helvetica'
pdf.text 'In Normal Helvetica'
+ pdf.font nil, style: :bold
+ pdf.text 'In Bold Helvetica'
+
text = PDF::Inspector::Text.analyze(pdf.render)
expect(text.font_settings.map { |e| e[:name] }).to eq(
%i[
Courier-Bold Courier-BoldOblique Courier-Oblique
- Courier Helvetica
+ Courier Helvetica Helvetica-Bold
]
)
end
@@ -233,36 +385,6 @@
end
end
- describe 'Transactional font handling' do
- it 'allows setting of size directly when font is created' do
- pdf.font 'Courier', size: 16
- expect(pdf.font_size).to eq(16)
- end
-
- it 'allows temporary setting of a new font using a transaction' do
- pdf.font 'Helvetica', size: 12
-
- pdf.font 'Courier', size: 16 do
- expect(pdf.font.name).to eq('Courier')
- expect(pdf.font_size).to eq(16)
- end
-
- expect(pdf.font.name).to eq('Helvetica')
- expect(pdf.font_size).to eq(12)
- end
-
- it 'masks font size when using a transacation' do
- pdf.font 'Courier', size: 16 do
- expect(pdf.font_size).to eq(16)
- end
-
- pdf.font 'Times-Roman'
- pdf.font 'Courier'
-
- expect(pdf.font_size).to eq(12)
- end
- end
-
describe 'Document#page_fonts' do
it 'registers fonts properly by page' do
pdf.font 'Courier'