prawn-icon 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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