# frozen_string_literal: true
# interface.rb: Prawn extension module and logic.
#
# Copyright October 2016, Jesse Doyle. All rights reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
module Prawn
# Easy icon font usage within Prawn. Currently
# supported icon fonts include: FontAwesome,
# Zurb Foundicons and PaymentFont.
#
# = Icon Keys
#
# Icon keys must be supplied to most +Prawn::Icon+
# methods. Keys map directly to a unicode character
# within the font that produces a given icon. As a
# rule, included icon keys should match the keys from
# the font provider. The icon key mapping is specified
# in the font's +legend_file+, which is a +YAML+ file
# located in {Prawn::Icon.configuration.font_directory}/font/font.yml.
#
# Prawn::Icon::
# Houses the methods and interfaces necessary for
# rendering icons to the Prawn::Document.
#
# Prawn::Icon::FontData::
# Used to store various information about an icon font,
# including the key-to-unicode mapping information.
# Also houses methods to cache and lazily load the
# requested font data on a document basis.
#
# Prawn::Icon::Parser::
# Used to initially parse icons that are used with the
# inline_format: true option. The input string is parsed
# once for tags, then the output is provided
# to Prawn's internal formatted text parser.
#
class Icon
# @deprecated Use {Prawn::Icon.configuration.font_directory} instead
FONTDIR = Icon::Base::FONTDIR
module Interface
# Set up and draw an icon on this document. This
# method operates much like +Prawn::Text::Box+.
#
# == Parameters:
# key::
# Contains the key to a particular icon within
# a font family. If :inline_format is true,
# then key may contain formatted text marked
# with tags and any tag supported
# by Prawn's parser.
#
# opts::
# A hash of options that may be supplied to
# the underlying +text+ method call.
#
# == Examples:
# pdf.icon 'fas-beer'
# pdf.icon 'fas-user-circle',
# inline_format: true
#
def icon(key, opts = {})
key = translate_key(key)
make_icon(key, opts).tap { |i| i && i.render }
end
# Initialize a new icon object.
#
# == Parameters:
# key::
# Contains the key to a particular icon within
# a font family. If :inline_format is true,
# then key may contain formatted text marked
# with tags and any tag supported
# by Prawn's parser.
#
# opts::
# A hash of options that may be supplied to
# the underlying text method call.
#
def make_icon(key, opts = {})
key = translate_key(key)
if opts.fetch(:inline_format, false)
inline_icon(key, opts)
else
Icon.new(key, self, opts)
end
end
# Render formatted icon content to the document from
# a string containing icons. Content will correctly
# transition to a new page when necessary.
#
# == Parameters:
# text::
# Input text to be parsed initially for
# tags, then passed to Prawn's formatted text
# parser.
#
# opts::
# A hash of options that may be supplied to the
# underlying text call.
#
def inline_icon(text, opts = {})
parsed = Icon::Parser.format(self, text)
content = Text::Formatted::Parser.format(parsed)
formatted_text(content, opts)
end
# Initialize a formatted icon box from an icon-conatining
# string. Content is not directly rendered to the document,
# instead a +Prawn::Text::Formatted::Box+ instance is returned
# that responds to the +render+ method.
#
# == Parameters:
# text::
# Input text to be parsed initially for
# tags, then passed to Prawn's formatted text
# parser.
#
# opts::
# A hash of options that may be supplied to the
# underlying text call.
#
def formatted_icon_box(text, opts = {})
parsed = Icon::Parser.format(self, text)
content = Text::Formatted::Parser.format(parsed)
position = opts.fetch(:at) do
[
opts.fetch(:x) { bounds.left },
opts.fetch(:y) { cursor }
]
end
box_options = opts.merge(
inline_format: true,
document: self,
at: position
)
icon_box(content, box_options)
end
# Initialize a new Prawn::Icon, but don't render
# the icon to a document. Intended to be used as
# an entry of a data array when combined with
# Prawn::Table.
#
# == Parameters:
# key::
# Contains the key to a particular icon within
# a font family. The key may contain a string
# with format tags if +inline_format: true+ in
# the +opts+ hash.
#
# opts::
# A hash of options that may be supplied to the
# underlying text call.
#
# == Returns:
# A Hash containing +font+ and +content+ keys
# that match the data necessary for the
# specified icon.
#
# eg. { font: 'fas', content: "\uf2b9" }
#
# Note that the +font+ key will not be set
# if +inline_format: true+.
#
# == Examples:
# require 'prawn/table'
#
# data = [
# # Explicit brackets must be used here
# [pdf.table_icon('fas-coffee'), 'Cell 1'],
# ['Cell 2', 'Cell 3']
# ]
#
# pdf.table(data) => (2 x 2 table)
#
def table_icon(key, opts = {})
key = translate_key(key)
if opts[:inline_format]
content = Icon::Parser.format(self, key)
opts.merge(content: content)
else
make_icon(key, opts).format_hash
end
end
private
def translate_key(key)
Compatibility.new(key: key).translate
end
def icon_box(content, opts = {}) # :nodoc:
Text::Formatted::Box.new(content, opts).tap do |box|
box.render(dry_run: true)
self.y -= box.height
self.y -= box.line_gap + box.leading unless opts.fetch(:final_gap, true)
end
end
end
attr_reader :set, :unicode
def initialize(key, document, opts = {})
@pdf = document
@set = opts.fetch(:set) { FontData.specifier_from_key(key) }
@data = FontData.load(document, @set)
@key = strip_specifier_from_key(key)
@unicode = @data.unicode(@key)
@options = opts
end
def format_hash
base = { font: @set.to_s, content: @unicode }
opts = @options.dup
# Prawn::Table renames :color to :text_color
opts[:text_color] = opts.delete(:color)
base.merge(opts)
end
def render
@pdf.font(@data.path) do
@pdf.text @unicode, @options
end
end
private
def strip_specifier_from_key(key) # :nodoc:
reg = Regexp.new "#{@data.specifier}-"
key.sub(reg, '') # Only one specifier
end
end
end