prawn-icon 2.1.0 → 3.0.0

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.
@@ -11,8 +11,9 @@ STYLES = {
11
11
 
12
12
  STYLES.each do |specifier, type|
13
13
  Prawn::Document.generate("fontawesome_#{type.downcase}.pdf") do
14
- deja_path = File.join \
15
- Prawn::Icon::Base::FONTDIR, 'DejaVuSans.ttf'
14
+ deja_path = Prawn::Icon.configuration.font_directory
15
+ .join('DejaVuSans.ttf')
16
+ .to_s
16
17
 
17
18
  font_families.update(
18
19
  'deja' => { normal: deja_path }
@@ -4,8 +4,9 @@ require_relative '../lib/prawn/icon'
4
4
  require_relative 'example_helper'
5
5
 
6
6
  Prawn::Document.generate('foundation_icons.pdf') do
7
- deja_path = File.join \
8
- Prawn::Icon::Base::FONTDIR, 'DejaVuSans.ttf'
7
+ deja_path = Prawn::Icon.configuration.font_directory
8
+ .join('DejaVuSans.ttf')
9
+ .to_s
9
10
 
10
11
  font_families.update({
11
12
  'deja' => { normal: deja_path }
@@ -4,8 +4,9 @@ require_relative '../lib/prawn/icon'
4
4
  require_relative 'example_helper'
5
5
 
6
6
  Prawn::Document.generate('paymentfont.pdf') do
7
- deja_path = File.join \
8
- Prawn::Icon::Base::FONTDIR, 'DejaVuSans.ttf'
7
+ deja_path = Prawn::Icon.configuration.font_directory
8
+ .join('DejaVuSans.ttf')
9
+ .to_s
9
10
 
10
11
  font_families.update({
11
12
  'deja' => { normal: deja_path }
@@ -19,7 +20,7 @@ Prawn::Document.generate('paymentfont.pdf') do
19
20
  define_grid(columns: 6, rows: 12, gutter: 16)
20
21
 
21
22
  sub_header = 'PaymentFont'
22
- link = 'http://paymentfont.io'
23
+ link = 'https://paymentfont.com'
23
24
  page_header sub_header, link
24
25
 
25
26
  first_page_icons icons do |icon_key|
@@ -6,7 +6,9 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
+ require 'pathname'
9
10
  require_relative 'icon/version'
11
+ require_relative 'icon/configuration'
10
12
  require_relative 'icon/base'
11
13
  require_relative 'icon/font_data'
12
14
  require_relative 'icon/parser'
@@ -11,9 +11,21 @@ require_relative 'errors'
11
11
 
12
12
  module Prawn
13
13
  class Icon
14
+ class << self
15
+ attr_writer :configuration
16
+
17
+ def configuration
18
+ @configuration ||= Configuration.new
19
+ end
20
+
21
+ def configure
22
+ yield(configuration)
23
+ end
24
+ end
25
+
14
26
  module Base
15
- FONTDIR = File.join \
16
- File.expand_path('../../../..', __FILE__), 'data/fonts'
27
+ # @deprecated Use {Prawn::Icon.configuration.font_directory} instead
28
+ FONTDIR = Prawn::Icon.configuration.font_directory.to_s
17
29
  end
18
30
  end
19
31
  end
@@ -6,137 +6,24 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
- # rubocop:disable Metrics/ClassLength
10
9
  module Prawn
11
10
  class Icon
12
11
  class Compatibility
13
- SHIMS = {
14
- 'fa-area-chart' => 'fas-chart-area',
15
- 'fa-arrow-circle-o-down' => 'far-arrow-alt-circle-down',
16
- 'fa-arrow-circle-o-left' => 'far-arrow-alt-circle-left',
17
- 'fa-arrow-circle-o-right' => 'far-arrow-alt-circle-right',
18
- 'fa-arrow-circle-o-up' => 'far-arrow-alt-circle-up',
19
- 'fa-arrows' => 'fas-arrows-alt',
20
- 'fa-arrows-alt' => 'fas-expand-arrows-alt',
21
- 'fa-arrows-h' => 'fas-arrows-alt-h',
22
- 'fa-arrows-v' => 'fas-arrows-alt-v',
23
- 'fa-bar-chart' => 'far-chart-bar',
24
- 'fa-bitbucket-square' => 'fab-bitbucket',
25
- 'fa-calendar' => 'fas-calendar-alt',
26
- 'fa-calendar-o' => 'far-calendar',
27
- 'fa-caret-square-o-down' => 'far-caret-square-down',
28
- 'fa-caret-square-o-left' => 'far-caret-square-left',
29
- 'fa-caret-square-o-right' => 'far-caret-square-right',
30
- 'fa-caret-square-o-up' => 'far-caret-square-up',
31
- 'fa-cc' => 'far-closed-captioning',
32
- 'fa-chain-broken' => 'fas-unlink',
33
- 'fa-circle-o-notch' => 'fas-circle-notch',
34
- 'fa-circle-thin' => 'far-circle',
35
- 'fa-clipboard' => 'far-clipboard',
36
- 'fa-clone' => 'far-clone',
37
- 'fa-cloud-download' => 'fas-cloud-download-alt',
38
- 'fa-cloud-upload' => 'fas-cloud-upload-alt',
39
- 'fa-code-fork' => 'fas-code-branch',
40
- 'fa-commenting' => 'fas-comment-alt',
41
- 'fa-compass' => 'far-compass',
42
- 'fa-copyright' => 'far-copyright',
43
- 'fa-creative-commons' => 'fab-creative-commons',
44
- 'fa-credit-card' => 'far-credit-card',
45
- 'fa-credit-card-alt' => 'fas-credit-card',
46
- 'fa-cutlery' => 'fas-utensils',
47
- 'fa-diamond' => 'far-gem',
48
- 'fa-eercast' => 'fab-sellcast',
49
- 'fa-eur' => 'fas-euro-sign',
50
- 'fa-exchange' => 'fas-exchange-alt',
51
- 'fa-external-link' => 'fas-external-link-alt',
52
- 'fa-external-link-square' => 'fas-external-link-square-alt',
53
- 'fa-eye-dropper' => 'far-eye-dropper',
54
- 'fa-eye-slash' => 'far-eye-slash',
55
- 'fa-eyedropper' => 'fas-eye-dropper',
56
- 'fa-facebook' => 'fab-facebook-f',
57
- 'fa-facebook-official' => 'fab-facebook',
58
- 'fa-file-text' => 'fas-file-alt',
59
- 'fa-files-o' => 'far-copy',
60
- 'fa-floppy-o' => 'far-save',
61
- 'fa-gbp' => 'fas-pound-sign',
62
- 'fa-glass' => 'fas-glass-martini',
63
- 'fa-google-plus' => 'fab-google-plus-g',
64
- 'fa-google-plus-circle' => 'fab-google-plus',
65
- 'fa-google-plus-official' => 'fab-google-plus',
66
- 'fa-hand-o-down' => 'far-hand-point-down',
67
- 'fa-hand-o-left' => 'far-hand-point-left',
68
- 'fa-hand-o-right' => 'far-hand-point-right',
69
- 'fa-hand-o-up' => 'far-hand-point-up',
70
- 'fa-header' => 'fas-heading',
71
- 'fa-id-badge' => 'far-id-badge',
72
- 'fa-ils' => 'fas-shekel-sign',
73
- 'fa-inr' => 'fas-rupee-sign',
74
- 'fa-intersex' => 'fas-transgender',
75
- 'fa-jpy' => 'fas-yen-sign',
76
- 'fa-krw' => 'fas-won-sign',
77
- 'fa-level-down' => 'fas-level-down-alt',
78
- 'fa-level-up' => 'fas-level-up-alt',
79
- 'fa-life-ring' => 'far-life-ring',
80
- 'fa-line-chart' => 'fas-chart-line',
81
- 'fa-linkedin' => 'fab-linkedin-in',
82
- 'fa-linkedin-square' => 'fab-linkedin',
83
- 'fa-list-alt' => 'far-list-alt',
84
- 'fa-long-arrow-down' => 'fas-long-arrow-alt-down',
85
- 'fa-long-arrow-left' => 'fas-long-arrow-alt-left',
86
- 'fa-long-arrow-right' => 'fas-long-arrow-alt-right',
87
- 'fa-long-arrow-up' => 'fas-long-arrow-alt-up',
88
- 'fa-map-marker' => 'fas-map-marker-alt',
89
- 'fa-meanpath' => 'fab-font-awesome',
90
- 'fa-mobile' => 'fas-mobile-alt',
91
- 'fa-money' => 'far-money-bill-alt',
92
- 'fa-object-group' => 'far-object-group',
93
- 'fa-object-ungroup' => 'far-object-ungroup',
94
- 'fa-paste' => 'far-paste',
95
- 'fa-pencil' => 'fas-pencil-alt',
96
- 'fa-pencil-square' => 'fas-pen-square',
97
- 'fa-pencil-square-o' => 'far-edit',
98
- 'fa-picture' => 'fas-image',
99
- 'fa-pie-chart' => 'fas-chart-pie',
100
- 'fa-refresh' => 'fas-sync',
101
- 'fa-registered' => 'far-registered',
102
- 'fa-repeat' => 'fas-redo',
103
- 'fa-rub' => 'fas-ruble-sign',
104
- 'fa-scissors' => 'fas-cut',
105
- 'fa-shield' => 'fas-shield-alt',
106
- 'fa-sign-in' => 'fas-sign-in-alt',
107
- 'fa-sign-out' => 'fas-sign-out-alt',
108
- 'fa-sliders' => 'fas-sliders-h',
109
- 'fa-sort-alpha-asc' => 'fas-sort-alpha-down',
110
- 'fa-sort-alpha-desc' => 'fas-sort-alpha-up',
111
- 'fa-sort-amount-asc' => 'fas-sort-amount-down',
112
- 'fa-sort-amount-desc' => 'fas-sort-amount-up',
113
- 'fa-sort-asc' => 'fas-sort-up',
114
- 'fa-sort-desc' => 'fas-sort-down',
115
- 'fa-sort-numeric-asc' => 'fas-sort-numeric-down',
116
- 'fa-sort-numeric-desc' => 'fas-sort-numeric-up',
117
- 'fa-spoon' => 'fas-utensil-spoon',
118
- 'fa-star-half-empty' => 'fas-star-half',
119
- 'fa-star-half-full' => 'fas-star-half',
120
- 'fa-support' => 'far-life-ring',
121
- 'fa-tablet' => 'fas-tablet-alt',
122
- 'fa-tachometer' => 'fas-tachometer-alt',
123
- 'fa-television' => 'fas-tv',
124
- 'fa-thumb-tack' => 'fas-thumbtack',
125
- 'fa-thumbs-o-down' => 'far-thumbs-down',
126
- 'fa-thumbs-o-up' => 'far-thumbs-up',
127
- 'fa-ticket' => 'fas-ticket-alt',
128
- 'fa-trash' => 'fas-trash-alt',
129
- 'fa-trash-o' => 'far-trash-alt',
130
- 'fa-try' => 'fas-lira-sign',
131
- 'fa-usd' => 'fas-dollar-sign',
132
- 'fa-video-camera' => 'fas-video',
133
- 'fa-vimeo' => 'fab-vimeo-v',
134
- 'fa-volume-control-phone' => 'fas-phone-volume',
135
- 'fa-wheelchair-alt' => 'fab-accessible-icon',
136
- 'fa-window-maximize' => 'far-window-maximize',
137
- 'fa-window-restore' => 'far-window-restore',
138
- 'fa-youtube-play' => 'fab-youtube'
139
- }.freeze
12
+ # @deprecated Use {Prawn::Icon::Compatibility.shims} instead
13
+ SHIMS = YAML.load_file(
14
+ Prawn::Icon.configuration.font_directory.join(
15
+ 'fa4',
16
+ 'shims.yml'
17
+ )
18
+ ).freeze
19
+
20
+ class << self
21
+ def shims
22
+ @shims ||= YAML.load_file(
23
+ Icon.configuration.font_directory.join('fa4', 'shims.yml').to_s
24
+ )
25
+ end
26
+ end
140
27
 
141
28
  attr_accessor :key
142
29
 
@@ -157,7 +44,7 @@ module Prawn
157
44
  private
158
45
 
159
46
  def map
160
- SHIMS.fetch(key) do
47
+ self.class.shims.fetch(key) do
161
48
  # FontAwesome shim metadata assumes "fas" as the default
162
49
  # font family if not explicity referenced.
163
50
  "fas-#{key.sub(/fa-/, '')}"
@@ -169,7 +56,7 @@ module Prawn
169
56
  [Prawn::Icon - DEPRECATION WARNING]
170
57
  FontAwesome 4 icon was referenced as '#{old_key}'.
171
58
  Use the FontAwesome 5 icon '#{new_key}' instead.
172
- This compatibility layer will be removed in Prawn::Icon 3.0.0.
59
+ This compatibility layer will be removed in Prawn::Icon 4.0.0.
173
60
  DEPRECATION
174
61
  end
175
62
  end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ #
3
+ # configuration.rb: Prawn icon configuration.
4
+ #
5
+ # Copyright October 2020, Jesse Doyle. All rights reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module Prawn
10
+ class Icon
11
+ class Configuration
12
+ def font_directory=(path)
13
+ @font_directory = Pathname.new(path)
14
+ end
15
+
16
+ def font_directory
17
+ @font_directory ||= default_font_directory
18
+ end
19
+
20
+ private
21
+
22
+ def default_font_directory
23
+ Pathname.new(gem_path).join('data', 'fonts')
24
+ end
25
+
26
+ # :nocov:
27
+ def gem_path
28
+ spec = Gem.loaded_specs.fetch('prawn-icon') do
29
+ Struct.new(:full_gem_path).new(failsafe_gem_path)
30
+ end
31
+ spec.full_gem_path
32
+ end
33
+
34
+ def failsafe_gem_path
35
+ File.expand_path('../../../..', __FILE__)
36
+ end
37
+ # :nocov:
38
+ end
39
+ end
40
+ end
@@ -63,15 +63,17 @@ module Prawn
63
63
  end
64
64
 
65
65
  def path
66
- ttf = File.join(Icon::Base::FONTDIR, @set.to_s, '*.ttf')
67
- font = Dir[ttf].first
66
+ font = Icon.configuration.font_directory
67
+ .join(@set.to_s)
68
+ .glob('*.ttf')
69
+ .first
68
70
 
69
71
  if font.nil?
70
72
  raise Prawn::Errors::UnknownFont,
71
73
  "Icon font not found for set: #{@set}"
72
74
  end
73
75
 
74
- @path ||= font
76
+ @path ||= font.to_s
75
77
  end
76
78
 
77
79
  def specifier
@@ -19,7 +19,7 @@ module Prawn
19
19
  # rule, included icon keys should match the keys from
20
20
  # the font provider. The icon key mapping is specified
21
21
  # in the font's +legend_file+, which is a +YAML+ file
22
- # located in Prawn::Icon::Base::FONTDIR/font/font.yml.
22
+ # located in {Prawn::Icon.configuration.font_directory}/font/font.yml.
23
23
  #
24
24
  # Prawn::Icon::
25
25
  # Houses the methods and interfaces necessary for
@@ -38,6 +38,7 @@ module Prawn
38
38
  # to Prawn's internal formatted text parser.
39
39
  #
40
40
  class Icon
41
+ # @deprecated Use {Prawn::Icon.configuration.font_directory} instead
41
42
  FONTDIR = Icon::Base::FONTDIR
42
43
 
43
44
  module Interface
@@ -63,11 +64,10 @@ module Prawn
63
64
  #
64
65
  def icon(key, opts = {})
65
66
  key = translate_key(key)
66
- make_icon(key, opts).tap(&:render)
67
+ make_icon(key, opts).tap { |i| i && i.render }
67
68
  end
68
69
 
69
- # Initialize a new icon object, but do
70
- # not render it to the document.
70
+ # Initialize a new icon object.
71
71
  #
72
72
  # == Parameters:
73
73
  # key::
@@ -90,9 +90,9 @@ module Prawn
90
90
  end
91
91
  end
92
92
 
93
- # Initialize a new formatted text box containing
94
- # icon information, but don't render it to the
95
- # document.
93
+ # Render formatted icon content to the document from
94
+ # a string containing icons. Content will correctly
95
+ # transition to a new page when necessary.
96
96
  #
97
97
  # == Parameters:
98
98
  # text::
@@ -107,10 +107,37 @@ module Prawn
107
107
  def inline_icon(text, opts = {})
108
108
  parsed = Icon::Parser.format(self, text)
109
109
  content = Text::Formatted::Parser.format(parsed)
110
+ formatted_text(content, opts)
111
+ end
112
+
113
+ # Initialize a formatted icon box from an icon-conatining
114
+ # string. Content is not directly rendered to the document,
115
+ # instead a +Prawn::Text::Formatted::Box+ instance is returned
116
+ # that responds to the +render+ method.
117
+ #
118
+ # == Parameters:
119
+ # text::
120
+ # Input text to be parsed initially for <icon>
121
+ # tags, then passed to Prawn's formatted text
122
+ # parser.
123
+ #
124
+ # opts::
125
+ # A hash of options that may be supplied to the
126
+ # underlying text call.
127
+ #
128
+ def formatted_icon_box(text, opts = {})
129
+ parsed = Icon::Parser.format(self, text)
130
+ content = Text::Formatted::Parser.format(parsed)
131
+ position = opts.fetch(:at) do
132
+ [
133
+ opts.fetch(:x) { bounds.left },
134
+ opts.fetch(:y) { cursor }
135
+ ]
136
+ end
110
137
  box_options = opts.merge(
111
138
  inline_format: true,
112
139
  document: self,
113
- at: [bounds.left, cursor]
140
+ at: position
114
141
  )
115
142
  icon_box(content, box_options)
116
143
  end
@@ -8,6 +8,6 @@
8
8
 
9
9
  module Prawn
10
10
  class Icon
11
- VERSION = '2.1.0'.freeze
11
+ VERSION = '3.0.0'.freeze
12
12
  end
13
13
  end
@@ -47,20 +47,28 @@ describe Prawn::Icon::Interface do
47
47
  pdf.move_down 10
48
48
  pdf.text 'More'
49
49
  pdf.move_down 20
50
- icon = pdf.icon icon_text, inline_format: true
50
+ pdf.icon icon_text, inline_format: true
51
51
  pdf.move_down 30
52
52
  pdf.text 'End'
53
+ inspector = PDF::Inspector::Text.analyze(pdf.render)
54
+ x, y = inspector.positions[2]
53
55
 
54
- expect(icon.at.first).to eq(0)
55
- expect(icon.at.last.round).to eq(734)
56
+ expect(x).to eq(0)
57
+ expect(y.round).to eq(724)
56
58
  end
57
59
 
58
60
  context 'with final_gap: false' do
59
61
  it 'renders the icon without a final gap' do
60
- icon = pdf.icon '<icon size="60">far-address-book</icon>',
62
+ pdf.icon(
63
+ '<icon size="60">far-address-book</icon>',
61
64
  inline_format: true,
62
65
  final_gap: false
63
- expect(icon.at.last.round).to eq(792)
66
+ )
67
+ pdf.text('Hello')
68
+ inspector = PDF::Inspector::Text.analyze(pdf.render)
69
+ y = inspector.positions[1].last.round
70
+
71
+ expect(y).to eq(723)
64
72
  end
65
73
  end
66
74
  end
@@ -77,17 +85,17 @@ describe Prawn::Icon::Interface do
77
85
 
78
86
  context 'invalid icon key' do
79
87
  it 'should raise IconNotFound' do
80
- proc = Proc.new { pdf.icon 'far-__INVALID' }
81
-
82
- expect(proc).to raise_error(Prawn::Icon::Errors::IconNotFound)
88
+ expect { pdf.icon('far-__INVALID') }.to raise_error(
89
+ Prawn::Icon::Errors::IconNotFound
90
+ )
83
91
  end
84
92
  end
85
93
 
86
94
  context 'invalid specifier' do
87
95
  it 'should raise UnknownFont' do
88
- proc = Proc.new { pdf.icon '__INVALID__' }
89
-
90
- expect(proc).to raise_error(Prawn::Errors::UnknownFont)
96
+ expect { pdf.icon('__INVALID__') }.to raise_error(
97
+ Prawn::Errors::UnknownFont
98
+ )
91
99
  end
92
100
  end
93
101
  end
@@ -102,19 +110,63 @@ describe Prawn::Icon::Interface do
102
110
  end
103
111
 
104
112
  context ':inline_format => true' do
105
- it 'should return a Prawn::::Text::Formatted::Box instance' do
113
+ it 'returns nil' do
106
114
  icon = pdf.make_icon '<icon>far-address-book</icon>', inline_format: true
107
115
 
108
- expect(icon).to be_a(Prawn::Text::Formatted::Box)
116
+ expect(icon).to be_nil
109
117
  end
110
118
  end
111
119
  end
112
120
 
113
121
  describe '::inline_icon' do
114
- it 'should return a Prawn::Text::Formatted::Box instance' do
122
+ it 'returns nil' do
115
123
  icon = pdf.inline_icon '<icon>far-address-book</icon>'
116
124
 
117
- expect(icon).to be_a(Prawn::Text::Formatted::Box)
125
+ expect(icon).to be_nil
126
+ end
127
+
128
+ it 'starts a new page if necessary', github_issue: '49' do
129
+ text = 209.times.map { 'Hello, World!' }.join(' ')
130
+ pdf.text(text, size: 18)
131
+ pdf.icon('Hello, <icon>fas-globe</icon>', inline_format: true, size: 18)
132
+ inspector = PDF::Inspector::Page.analyze(pdf.render)
133
+
134
+ expect(inspector.pages.size).to eq(2)
135
+ end
136
+ end
137
+
138
+ describe '::formatted_icon_box' do
139
+ it 'returns a Prawn::Text::Formatted::Box instance' do
140
+ icon_text = <<~CONTENT
141
+ <icon size="20">fas-broom</icon>
142
+ <strikethrough>cancel that</strikethrough>
143
+ <icon>fas-check</icon>
144
+ CONTENT
145
+ box = pdf.formatted_icon_box(icon_text, inline_format: true)
146
+
147
+ expect(box).to be_a(Prawn::Text::Formatted::Box)
148
+ end
149
+
150
+ it 'accepts an absolute position parameter' do
151
+ icon_text = 'Hello, <icon>fas-globe</icon>!'
152
+ pdf.formatted_icon_box(icon_text, inline_format: true, x: 200, y: 100).render
153
+ inspector = PDF::Inspector::Text.analyze(pdf.render)
154
+ x, y = inspector.positions[0]
155
+
156
+ expect(x).to eq(200)
157
+ expect(y.round).to eq(90)
158
+ end
159
+
160
+ it 'handles final_gap: false correctly' do
161
+ icon_text = <<~CONTENT
162
+ Hello, <icon size="60">fas-globe</icon>
163
+ Next line.
164
+ CONTENT
165
+ pdf.formatted_icon_box(icon_text, inline_format: true, final_gap: false).render
166
+ inspector = PDF::Inspector::Text.analyze(pdf.render)
167
+ x = inspector.positions[1].first
168
+
169
+ expect(x.round).to eq(34)
118
170
  end
119
171
  end
120
172