prawn-svg 0.37.0 → 0.38.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.
@@ -0,0 +1,174 @@
1
+ module Prawn::SVG
2
+ class Elements::TextNode
3
+ Chunk = Struct.new(:text, :x, :y, :dx, :dy, :rotate, :base_width, :offset, :fixed_width)
4
+
5
+ attr_reader :component, :chunks
6
+ attr_accessor :text
7
+
8
+ def initialize(component, text, leading_space, trailing_space)
9
+ @component = component
10
+ @text = text
11
+ @leading_space = leading_space
12
+ @trailing_space = trailing_space
13
+ end
14
+
15
+ def leading_space?
16
+ @leading_space
17
+ end
18
+
19
+ def trailing_space?
20
+ @trailing_space
21
+ end
22
+
23
+ def calculated_width
24
+ @chunks.reduce(0) do |total, chunk|
25
+ total + (chunk.fixed_width || chunk.base_width) + chunk.offset
26
+ end
27
+ end
28
+
29
+ def total_flexible_and_fixed_width
30
+ flexible = fixed = 0
31
+ chunks.each do |chunk|
32
+ if chunk.fixed_width.nil?
33
+ flexible += chunk.base_width
34
+ fixed += chunk.offset
35
+ else
36
+ fixed += chunk.offset + chunk.fixed_width
37
+ end
38
+ end
39
+ [flexible, fixed]
40
+ end
41
+
42
+ def lay_out(prawn)
43
+ remaining_text = @text
44
+ @chunks = []
45
+
46
+ while remaining_text != ''
47
+ x = y = dx = dy = rotate = nil
48
+ remaining = rotation_remaining = false
49
+
50
+ comp = component
51
+ while comp
52
+ shifted = comp.x_values.shift
53
+ x ||= shifted
54
+ shifted = comp.y_values.shift
55
+ y ||= shifted
56
+ shifted = comp.dx.shift
57
+ dx ||= shifted
58
+ shifted = comp.dy.shift
59
+ dy ||= shifted
60
+
61
+ shifted = comp.rotation.length > 1 ? comp.rotation.shift : comp.rotation.first
62
+ if shifted && rotate.nil?
63
+ rotate = shifted
64
+ remaining ||= comp.rotation != [0]
65
+ end
66
+
67
+ remaining ||= comp.x_values.any? || comp.y_values.any? || comp.dx.any? || comp.dy.any? || (rotate && rotate != 0)
68
+ rotation_remaining ||= comp.rotation.length > 1
69
+ comp = comp.parent_component
70
+ end
71
+
72
+ rotate = (-rotate if rotate && rotate != 0)
73
+
74
+ text_to_draw = remaining ? remaining_text[0..0] : remaining_text
75
+
76
+ opts = { size: component.computed_properties.numeric_font_size, kerning: true }
77
+
78
+ total_spacing = text_to_draw.length > 1 ? (component.letter_spacing_pixels || 0) * (text_to_draw.length - 1) : 0
79
+ base_width = prawn.width_of(text_to_draw, opts) + total_spacing
80
+
81
+ offset = dx ? [0, dx].max : 0
82
+
83
+ @chunks << Chunk.new(text_to_draw, x, y, dx, dy, rotate, base_width, offset, nil)
84
+
85
+ if remaining
86
+ remaining_text = remaining_text[1..]
87
+ else
88
+ # we can get to this path with rotations still pending
89
+ # solve this by shifting them out by the number of
90
+ # characters we've just drawn
91
+ shift = remaining_text.length - 1
92
+ if rotation_remaining && shift.positive?
93
+ comp = component
94
+ while comp
95
+ count = [shift, comp.rotation.length - 1].min
96
+ comp.rotation.shift(count) if count.positive?
97
+ comp = comp.parent_component
98
+ end
99
+ end
100
+
101
+ break
102
+ end
103
+ end
104
+ end
105
+
106
+ def render(prawn, size, cursor, y_offset)
107
+ chunks.each do |chunk|
108
+ cursor.x = chunk.x if chunk.x
109
+ cursor.x += chunk.dx if chunk.dx
110
+ cursor.y = chunk.y if chunk.y
111
+ cursor.y -= chunk.dy if chunk.dy
112
+
113
+ render_underline(prawn, size, cursor, y_offset, chunk.fixed_width || chunk.base_width) if component.computed_properties.text_decoration == 'underline'
114
+
115
+ opts = { size: size, at: [cursor.x, cursor.y + (y_offset || 0)] }
116
+ opts[:rotate] = chunk.rotate if chunk.rotate
117
+
118
+ scaling =
119
+ if chunk.fixed_width && component.current_length_adjust_is_scaling?
120
+ chunk.fixed_width * 100 / chunk.base_width
121
+ else
122
+ 100
123
+ end
124
+
125
+ spacing_enabled = chunk.fixed_width && !component.current_length_adjust_is_scaling? && chunk.text.length > 1
126
+
127
+ # This isn't perfect. It assumes the parent component which started the textLength context
128
+ # has a character at the end of its text nodes. If it doesn't, the last character in its
129
+ # children should not take the space. This is possible but would involve a lot more work so
130
+ # I will park it for now.
131
+ parent_spacing = spacing_enabled && !component.text_length
132
+ spacing =
133
+ if spacing_enabled
134
+ ((chunk.fixed_width - chunk.base_width) / (chunk.text.length - (parent_spacing ? 0 : 1))) + (component.letter_spacing_pixels || 0)
135
+ end
136
+
137
+ prawn.horizontal_text_scaling(scaling) do
138
+ prawn.character_spacing(spacing || component.letter_spacing_pixels || prawn.character_spacing) do
139
+ prawn.text_rendering_mode(calculate_text_rendering_mode) do
140
+ prawn.draw_text(chunk.text, **opts)
141
+ end
142
+ end
143
+ end
144
+
145
+ cursor.x += chunk.fixed_width || chunk.base_width
146
+
147
+ # If we're in a textLength context for one of our parents, we'll need to add spacing
148
+ # to the end of our string. See comment above for why this isn't quite right.
149
+ cursor.x += spacing if parent_spacing
150
+ end
151
+ end
152
+
153
+ def render_underline(prawn, size, cursor, y_offset, width)
154
+ offset, thickness = FontMetrics.underline_metrics(prawn, size)
155
+
156
+ prawn.fill_rectangle [cursor.x, cursor.y + (y_offset || 0) + offset + (thickness / 2.0)], width, thickness
157
+ end
158
+
159
+ def calculate_text_rendering_mode
160
+ fill = !component.computed_properties.fill.none? # rubocop:disable Style/InverseMethods
161
+ stroke = !component.computed_properties.stroke.none? # rubocop:disable Style/InverseMethods
162
+
163
+ if fill && stroke
164
+ :fill_stroke
165
+ elsif fill
166
+ :fill
167
+ elsif stroke
168
+ :stroke
169
+ else
170
+ :invisible
171
+ end
172
+ end
173
+ end
174
+ end
@@ -4,7 +4,7 @@ end
4
4
 
5
5
  require 'prawn/svg/elements/call_duplicator'
6
6
 
7
- %w[base depth_first_base root container clip_path viewport text text_component line polyline polygon circle ellipse
7
+ %w[base direct_render_base root container clip_path viewport text text_component text_node line polyline polygon circle ellipse
8
8
  rect path use image gradient marker ignored].each do |filename|
9
9
  require "prawn/svg/elements/#{filename}"
10
10
  end
@@ -1,65 +1,21 @@
1
- class Prawn::SVG::Font
2
- GENERIC_CSS_FONT_MAPPING = {
3
- 'serif' => 'Times-Roman',
4
- 'sans-serif' => 'Helvetica',
5
- 'cursive' => 'Times-Roman',
6
- 'fantasy' => 'Times-Roman',
7
- 'monospace' => 'Courier'
8
- }.freeze
1
+ module Prawn::SVG
2
+ class Font
3
+ attr_reader :name, :weight, :style
9
4
 
10
- attr_reader :name, :weight, :style
11
-
12
- def self.weight_for_css_font_weight(weight)
13
- case weight
14
- when '100', '200', '300' then :light
15
- when '400', '500', 'normal' then :normal
16
- when '600' then :semibold
17
- when '700', 'bold' then :bold
18
- when '800' then :extrabold
19
- when '900' then :black
20
- end
21
- end
22
-
23
- def initialize(name, weight, style, font_registry: nil)
24
- @font_registry = font_registry
25
- unless font_registry.installed_fonts.key?(name)
26
- # map generic font name to one of the built-in PDF fonts if not already mapped
27
- name = GENERIC_CSS_FONT_MAPPING[name] || name
5
+ def initialize(name, weight, style)
6
+ @name = name
7
+ @weight = weight
8
+ @style = style
28
9
  end
29
- @name = font_registry.correctly_cased_font_name(name) || name
30
- @weight = weight
31
- @style = style
32
- end
33
-
34
- def installed?
35
- subfamilies = @font_registry.installed_fonts[name]
36
- !subfamilies.nil? && subfamilies.key?(subfamily)
37
- end
38
10
 
39
- # Construct a subfamily name, ensuring that the subfamily is a valid one for the font.
40
- def subfamily
41
- if (subfamilies = @font_registry.installed_fonts[name])
42
- if subfamilies.key?(subfamily_name)
43
- subfamily_name
44
- elsif subfamilies.key?(:normal)
45
- :normal
11
+ def subfamily
12
+ if weight == :normal && style
13
+ style
14
+ elsif weight || style
15
+ [weight, style].compact.join('_').to_sym
46
16
  else
47
- subfamilies.keys.first
17
+ :normal
48
18
  end
49
19
  end
50
20
  end
51
-
52
- private
53
-
54
- # Construct a subfamily name from the weight and style information.
55
- # Note that this name might not actually exist in the font.
56
- def subfamily_name
57
- if weight == :normal && style
58
- style
59
- elsif weight || style
60
- [weight, style].compact.join('_').to_sym
61
- else
62
- :normal
63
- end
64
- end
65
21
  end
@@ -0,0 +1,97 @@
1
+ class Prawn::SVG::FontMetrics
2
+ class << self
3
+ # Default x-height as a fraction of font size (typical for most fonts)
4
+ DEFAULT_X_HEIGHT_RATIO = 0.5
5
+
6
+ def x_height_in_points(pdf, font_size)
7
+ @x_height_cache ||= {}
8
+
9
+ cache_key = cache_key_for(pdf)
10
+
11
+ @x_height_cache[cache_key] ||= calculate_x_height_ratio(pdf)
12
+ @x_height_cache[cache_key] * font_size
13
+ end
14
+
15
+ def underline_metrics(pdf, size)
16
+ @underline_metrics_cache ||= {}
17
+
18
+ cache_key = cache_key_for(pdf)
19
+
20
+ @underline_metrics_cache[cache_key] ||= fetch_underline_metrics(pdf, size)
21
+ @underline_metrics_cache[cache_key]
22
+ end
23
+
24
+ private
25
+
26
+ def cache_key_for(pdf)
27
+ return 'default' unless pdf && pdf.font.is_a?(Prawn::Fonts::TTF)
28
+
29
+ ttf = pdf.font.ttf
30
+ return 'default' unless ttf
31
+
32
+ # Use font family name from TTF metadata, which doesn't include size
33
+ ttf.name&.font_family&.first || pdf&.font&.name || 'default'
34
+ end
35
+
36
+ def calculate_x_height_ratio(pdf)
37
+ return DEFAULT_X_HEIGHT_RATIO unless pdf && pdf.font.is_a?(Prawn::Fonts::TTF)
38
+
39
+ ttf = pdf.font.ttf
40
+ return DEFAULT_X_HEIGHT_RATIO unless ttf
41
+
42
+ units_per_em = ttf.header&.units_per_em&.to_f
43
+ return DEFAULT_X_HEIGHT_RATIO unless units_per_em&.positive?
44
+
45
+ cmap = ttf.cmap&.unicode&.first
46
+ return DEFAULT_X_HEIGHT_RATIO unless cmap
47
+
48
+ xid = cmap['x'.ord]
49
+ return DEFAULT_X_HEIGHT_RATIO unless xid
50
+
51
+ bbox = ttf.glyph_outlines&.for(xid)
52
+ return DEFAULT_X_HEIGHT_RATIO unless bbox
53
+
54
+ y_max = bbox.y_max
55
+ y_min = bbox.y_min
56
+ return DEFAULT_X_HEIGHT_RATIO unless y_max && y_min
57
+
58
+ glyph_height_units = y_max - y_min
59
+ return DEFAULT_X_HEIGHT_RATIO if glyph_height_units <= 0
60
+
61
+ glyph_height_units / units_per_em
62
+ end
63
+
64
+ def fetch_underline_metrics(pdf, size)
65
+ units_per_em = nil
66
+ pos_units = thick_units = nil
67
+ if pdf.font.is_a?(Prawn::Font::TTF)
68
+ ttf = begin
69
+ pdf.font.ttf
70
+ rescue StandardError
71
+ nil
72
+ end
73
+ if ttf.respond_to?(:post) && ttf.post && ttf.respond_to?(:header) && ttf.header
74
+ units_per_em = ttf.header.units_per_em.to_f
75
+ pos_units = ttf.post.underline_position.to_f
76
+ thick_units = ttf.post.underline_thickness.to_f
77
+ end
78
+ end
79
+
80
+ offset =
81
+ if units_per_em && pos_units
82
+ (pos_units / units_per_em) * size
83
+ else
84
+ -0.12 * size
85
+ end
86
+
87
+ thick =
88
+ if units_per_em && thick_units&.positive?
89
+ [(thick_units / units_per_em) * size, 0.5].max
90
+ else
91
+ [size * 0.06, 0.5].max
92
+ end
93
+
94
+ [offset, thick]
95
+ end
96
+ end
97
+ end
@@ -1,75 +1,139 @@
1
- class Prawn::SVG::FontRegistry
2
- DEFAULT_FONT_PATHS = [
3
- '/Library/Fonts',
4
- '/System/Library/Fonts',
5
- "#{Dir.home}/Library/Fonts",
6
- '/usr/share/fonts/truetype',
7
- '/mnt/c/Windows/Fonts' # Bash on Ubuntu on Windows
8
- ].freeze
9
-
10
- @font_path = DEFAULT_FONT_PATHS.select { |path| Dir.exist?(path) }
11
-
12
- def initialize(font_families)
13
- @font_families = font_families
14
- end
1
+ module Prawn::SVG
2
+ class FontRegistry
3
+ GENERIC_CSS_FONT_MAPPING = {
4
+ 'serif' => 'Times-Roman',
5
+ 'sans-serif' => 'Helvetica',
6
+ 'cursive' => 'Times-Roman',
7
+ 'fantasy' => 'Times-Roman',
8
+ 'monospace' => 'Courier'
9
+ }.freeze
15
10
 
16
- def installed_fonts
17
- merge_external_fonts
18
- @font_families
19
- end
11
+ FONT_WEIGHT_FALLBACKS = {
12
+ light: :normal,
13
+ normal: nil,
14
+ semibold: :bold,
15
+ bold: :normal,
16
+ extrabold: :bold,
17
+ black: :extrabold
18
+ }.freeze
20
19
 
21
- def correctly_cased_font_name(name)
22
- merge_external_fonts
23
- @font_case_mapping[name.downcase]
24
- end
20
+ FONT_WEIGHTS = FONT_WEIGHT_FALLBACKS.keys.freeze
21
+
22
+ DEFAULT_FONT_PATHS = [
23
+ '/Library/Fonts',
24
+ '/System/Library/Fonts',
25
+ "#{Dir.home}/Library/Fonts",
26
+ '/usr/share/fonts/truetype',
27
+ '/mnt/c/Windows/Fonts' # Bash on Ubuntu on Windows
28
+ ].freeze
25
29
 
26
- def load(family, weight = nil, style = nil)
27
- Prawn::SVG::CSS::FontFamilyParser.parse(family).detect do |name|
28
- name = name.gsub(/\s{2,}/, ' ').downcase
30
+ @font_path = DEFAULT_FONT_PATHS.select { |path| Dir.exist?(path) }
29
31
 
30
- font = Prawn::SVG::Font.new(name, weight, style, font_registry: self)
31
- break font if font.installed?
32
+ def initialize(font_families)
33
+ @font_families = font_families
32
34
  end
33
- end
34
35
 
35
- private
36
+ def installed_fonts
37
+ merge_external_fonts
38
+ @font_families
39
+ end
36
40
 
37
- def merge_external_fonts
38
- if @font_case_mapping.nil?
39
- self.class.load_external_fonts unless self.class.external_font_families
40
- @font_families.merge!(self.class.external_font_families) do |_key, v1, _v2|
41
- v1
42
- end
43
- @font_case_mapping = @font_families.keys.each.with_object({}) do |key, result|
44
- result[key.downcase] = key
41
+ def correctly_cased_font_name(name)
42
+ merge_external_fonts
43
+ @font_case_mapping[name.downcase]
44
+ end
45
+
46
+ def load(family, weight = nil, style = nil)
47
+ weight = weight_for_css_font_weight(weight) unless FONT_WEIGHTS.include?(weight)
48
+
49
+ CSS::FontFamilyParser.parse(family).detect do |name|
50
+ name = name.gsub(/\s{2,}/, ' ')
51
+
52
+ font = find_suitable_font(name, weight, style)
53
+ break font if font
45
54
  end
46
55
  end
47
- end
48
56
 
49
- class << self
50
- attr_reader :external_font_families, :font_path
57
+ private
58
+
59
+ def find_suitable_font(name, weight, style)
60
+ name = correctly_cased_font_name(name) || name
61
+ name = GENERIC_CSS_FONT_MAPPING[name] if GENERIC_CSS_FONT_MAPPING.key?(name)
51
62
 
52
- def load_external_fonts
53
- @external_font_families = {}
63
+ return unless (subfamilies = installed_fonts[name])
64
+ return if subfamilies.empty?
54
65
 
55
- external_font_paths.each do |filename|
56
- ttf = Prawn::SVG::TTF.new(filename)
57
- next unless ttf.family
66
+ while weight
67
+ font = Font.new(name, weight, style)
68
+ return font if installed?(font)
69
+
70
+ weight = FONT_WEIGHT_FALLBACKS[weight]
71
+ end
58
72
 
59
- subfamily = (ttf.subfamily || 'normal').gsub(/\s+/, '_').downcase.to_sym
60
- subfamily = :normal if subfamily == :regular
61
- (external_font_families[ttf.family] ||= {})[subfamily] ||= filename
73
+ if style
74
+ find_suitable_font(name, weight, nil) unless style.nil?
75
+ else
76
+ Font.new(name, subfamilies.keys.first, nil)
62
77
  end
63
78
  end
64
79
 
65
- private
80
+ def installed?(font)
81
+ subfamilies = installed_fonts[font.name]
82
+ !subfamilies.nil? && subfamilies.key?(font.subfamily)
83
+ end
84
+
85
+ def weight_for_css_font_weight(weight)
86
+ case weight
87
+ when '100', '200', '300' then :light
88
+ when '400', '500', 'normal' then :normal
89
+ when '600' then :semibold
90
+ when '700', 'bold' then :bold
91
+ when '800' then :extrabold
92
+ when '900' then :black
93
+ else :normal # rubocop:disable Lint/DuplicateBranch
94
+ end
95
+ end
96
+
97
+ def merge_external_fonts
98
+ if @font_case_mapping.nil?
99
+ self.class.load_external_fonts unless self.class.external_font_families
100
+ @font_families.merge!(self.class.external_font_families) do |_key, v1, _v2|
101
+ v1
102
+ end
103
+ @font_case_mapping = @font_families.keys.each.with_object({}) do |key, result|
104
+ result[key.downcase] = key
105
+ end
106
+ GENERIC_CSS_FONT_MAPPING.each_key do |generic|
107
+ @font_case_mapping[generic] = generic
108
+ end
109
+ end
110
+ end
66
111
 
67
- def external_font_paths
68
- font_path
69
- .uniq
70
- .flat_map { |path| Dir["#{path}/**/*"] }
71
- .uniq
72
- .select { |path| File.file?(path) }
112
+ class << self
113
+ attr_reader :external_font_families, :font_path
114
+
115
+ def load_external_fonts
116
+ @external_font_families = {}
117
+
118
+ external_font_paths.each do |filename|
119
+ ttf = TTF.new(filename)
120
+ next unless ttf.family
121
+
122
+ subfamily = (ttf.subfamily || 'normal').gsub(/\s+/, '_').downcase.to_sym
123
+ subfamily = :normal if subfamily == :regular
124
+ (external_font_families[ttf.family] ||= {})[subfamily] ||= filename
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ def external_font_paths
131
+ font_path
132
+ .uniq
133
+ .flat_map { |path| Dir["#{path}/**/*"] }
134
+ .uniq
135
+ .select { |path| File.file?(path) }
136
+ end
73
137
  end
74
138
  end
75
139
  end
@@ -50,6 +50,10 @@ module Prawn
50
50
  options[:at] || [x_based_on_requested_alignment, y_based_on_requested_alignment]
51
51
  end
52
52
 
53
+ def render_calls(prawn, calls)
54
+ issue_prawn_command(prawn, calls)
55
+ end
56
+
53
57
  private
54
58
 
55
59
  def x_based_on_requested_alignment
@@ -100,80 +104,32 @@ module Prawn
100
104
  if skip
101
105
  # the call has been overridden
102
106
  elsif children.empty? && call != 'transparent' # some prawn calls complain if they aren't supplied a block
103
- if RUBY_VERSION >= '2.7' || !kwarguments.empty?
104
- prawn.send(call, *arguments, **kwarguments)
105
- else
106
- prawn.send(call, *arguments)
107
- end
108
- elsif RUBY_VERSION >= '2.7' || !kwarguments.empty?
109
- prawn.send(call, *arguments, **kwarguments, &proc_creator(prawn, children))
107
+ prawn.send(call, *arguments, **kwarguments)
110
108
  else
111
- prawn.send(call, *arguments, &proc_creator(prawn, children))
109
+ prawn.send(call, *arguments, **kwarguments, &proc_creator(prawn, children))
112
110
  end
113
111
  end
114
112
  end
115
113
 
116
114
  def rewrite_call_arguments(prawn, call, arguments, kwarguments)
117
115
  case call
118
- when 'text_group'
119
- @cursor = [0, sizing.output_height]
120
- yield
121
-
122
- when 'draw_text'
123
- text = arguments.first
124
- options = kwarguments
125
-
126
- at = options.fetch(:at)
127
-
128
- at[0] = @cursor[0] if at[0] == :relative
129
- at[1] = @cursor[1] if at[1] == :relative
130
-
131
- case options.delete(:dominant_baseline)
132
- when 'middle'
133
- height = prawn.font.height
134
- at[1] -= height / 2.0
135
- @cursor = [at[0], at[1]]
136
- end
137
-
138
- if (offset = options.delete(:offset))
139
- at[0] += offset[0]
140
- at[1] -= offset[1]
116
+ when 'svg:render'
117
+ element = arguments.first
118
+ raise ArgumentError, "Expected a Prawn::SVG::Elements::DirectRenderBase, got #{element.class}" unless element.is_a?(Prawn::SVG::Elements::DirectRenderBase)
119
+
120
+ begin
121
+ element.render(prawn, self)
122
+ rescue Prawn::SVG::Elements::Base::SkipElementQuietly
123
+ rescue Prawn::SVG::Elements::Base::SkipElementError => e
124
+ @document.warnings << e.message
141
125
  end
142
126
 
143
- width = prawn.width_of(text, options.merge(kerning: true))
144
-
145
- if (stretch_to_width = options.delete(:stretch_to_width))
146
- factor = stretch_to_width.to_f * 100 / width.to_f
147
- prawn.add_content "#{factor} Tz"
148
- width = stretch_to_width.to_f
149
- end
150
-
151
- if (pad_to_width = options.delete(:pad_to_width))
152
- padding_required = pad_to_width.to_f - width.to_f
153
- padding_per_character = padding_required / text.length.to_f
154
- prawn.add_content "#{padding_per_character} Tc"
155
- width = pad_to_width.to_f
156
- end
157
-
158
- case options.delete(:text_anchor)
159
- when 'middle'
160
- at[0] -= width / 2
161
- @cursor = [at[0] + (width / 2), at[1]]
162
- when 'end'
163
- at[0] -= width
164
- @cursor = at.dup
165
- else
166
- @cursor = [at[0] + width, at[1]]
167
- end
127
+ yield
168
128
 
169
- decoration = options.delete(:decoration)
170
- if decoration == 'underline'
171
- prawn.save_graphics_state do
172
- prawn.line_width 1
173
- prawn.line [at[0], at[1] - 1.25], [at[0] + width, at[1] - 1.25]
174
- prawn.stroke
175
- end
176
- end
129
+ when 'svg:yield'
130
+ block = arguments.first
131
+ block.call
132
+ yield
177
133
 
178
134
  when 'transformation_matrix'
179
135
  left = prawn.bounds.absolute_left
@@ -1,5 +1,5 @@
1
1
  module Prawn
2
2
  module SVG
3
- VERSION = '0.37.0'.freeze
3
+ VERSION = '0.38.0'.freeze
4
4
  end
5
5
  end
data/lib/prawn-svg.rb CHANGED
@@ -6,6 +6,7 @@ require 'prawn/svg/version'
6
6
  require 'css_parser'
7
7
 
8
8
  require 'prawn/svg/font_registry'
9
+ require 'prawn/svg/font_metrics'
9
10
  require 'prawn/svg/calculators/arc_to_bezier_curve'
10
11
  require 'prawn/svg/calculators/aspect_ratio'
11
12
  require 'prawn/svg/calculators/document_sizing'