prawn-svg 0.36.2 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +2 -2
- data/lib/prawn/svg/attributes/transform.rb +2 -0
- data/lib/prawn/svg/css/stylesheets.rb +0 -7
- data/lib/prawn/svg/elements/base.rb +28 -9
- data/lib/prawn/svg/elements/direct_render_base.rb +27 -0
- data/lib/prawn/svg/elements/polygon.rb +2 -2
- data/lib/prawn/svg/elements/text.rb +54 -52
- data/lib/prawn/svg/elements/text_component.rb +176 -198
- data/lib/prawn/svg/elements/text_node.rb +174 -0
- data/lib/prawn/svg/elements.rb +1 -1
- data/lib/prawn/svg/font.rb +13 -57
- data/lib/prawn/svg/font_metrics.rb +97 -0
- data/lib/prawn/svg/font_registry.rb +119 -55
- data/lib/prawn/svg/properties.rb +9 -4
- data/lib/prawn/svg/renderer.rb +20 -64
- data/lib/prawn/svg/version.rb +1 -1
- data/lib/prawn-svg.rb +1 -0
- data/scripts/compare_samples +25 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +4 -0
- data/spec/prawn/svg/css/stylesheets_spec.rb +0 -2
- data/spec/prawn/svg/elements/base_spec.rb +2 -2
- data/spec/prawn/svg/elements/text_spec.rb +210 -130
- data/spec/prawn/svg/font_registry_spec.rb +121 -10
- data/spec/prawn/svg/font_spec.rb +30 -8
- data/spec/sample_svg/bytes.svg +121 -0
- data/spec/sample_svg/important.svg +14 -0
- data/spec/sample_svg/positioning.svg +26 -0
- data/spec/sample_svg/presentation_attribute_precedence.svg +12 -0
- data/spec/sample_svg/subfamilies.svg +5 -1
- data/spec/sample_svg/text_use.svg +37 -0
- metadata +11 -3
- data/lib/prawn/svg/elements/depth_first_base.rb +0 -52
data/lib/prawn/svg/font.rb
CHANGED
@@ -1,65 +1,21 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
'
|
7
|
-
'
|
8
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
+
def initialize(font_families)
|
33
|
+
@font_families = font_families
|
32
34
|
end
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
+
def installed_fonts
|
37
|
+
merge_external_fonts
|
38
|
+
@font_families
|
39
|
+
end
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
50
|
-
|
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
|
-
|
53
|
-
|
63
|
+
return unless (subfamilies = installed_fonts[name])
|
64
|
+
return if subfamilies.empty?
|
54
65
|
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
68
|
-
font_path
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
data/lib/prawn/svg/properties.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Prawn::SVG
|
2
2
|
class Properties
|
3
|
-
Config = Struct.new(:default, :inheritable?, :valid_values, :attr, :ivar)
|
3
|
+
Config = Struct.new(:default, :inheritable?, :valid_values, :attr, :ivar, :id)
|
4
4
|
|
5
5
|
EM = 16
|
6
6
|
FONT_SIZES = {
|
@@ -49,6 +49,7 @@ module Prawn::SVG
|
|
49
49
|
|
50
50
|
PROPERTIES.each do |name, value|
|
51
51
|
value.attr = name.gsub('-', '_')
|
52
|
+
value.id = value.attr.to_sym
|
52
53
|
value.ivar = "@#{value.attr}"
|
53
54
|
end
|
54
55
|
|
@@ -57,9 +58,11 @@ module Prawn::SVG
|
|
57
58
|
ATTR_NAMES = PROPERTIES.keys.map { |name| name.gsub('-', '_') }
|
58
59
|
|
59
60
|
attr_accessor(*ATTR_NAMES)
|
61
|
+
attr_reader :important_ids
|
60
62
|
|
61
63
|
def initialize
|
62
64
|
@numeric_font_size = EM
|
65
|
+
@important_ids = []
|
63
66
|
end
|
64
67
|
|
65
68
|
def load_default_stylesheet
|
@@ -70,10 +73,11 @@ module Prawn::SVG
|
|
70
73
|
self
|
71
74
|
end
|
72
75
|
|
73
|
-
def set(name, value)
|
76
|
+
def set(name, value, important: false)
|
74
77
|
name = name.to_s.downcase
|
75
78
|
if (config = PROPERTIES[name])
|
76
|
-
if (value = parse_value(config, value.strip))
|
79
|
+
if (value = parse_value(config, value.strip)) && (important || !@important_ids.include?(config.id))
|
80
|
+
@important_ids << config.id if important
|
77
81
|
instance_variable_set(config.ivar, value)
|
78
82
|
end
|
79
83
|
elsif name == 'font'
|
@@ -99,7 +103,7 @@ module Prawn::SVG
|
|
99
103
|
PROPERTY_CONFIGS.each do |config|
|
100
104
|
value = other.send(config.attr)
|
101
105
|
|
102
|
-
if value && value != 'inherit'
|
106
|
+
if value && value != 'inherit' && (!@important_ids.include?(config.id) || other.important_ids.include?(config.id))
|
103
107
|
instance_variable_set(config.ivar, value)
|
104
108
|
|
105
109
|
elsif value.nil? && !config.inheritable?
|
@@ -107,6 +111,7 @@ module Prawn::SVG
|
|
107
111
|
end
|
108
112
|
end
|
109
113
|
|
114
|
+
@important_ids += other.important_ids
|
110
115
|
@numeric_font_size = calculate_numeric_font_size
|
111
116
|
nil
|
112
117
|
end
|
data/lib/prawn/svg/renderer.rb
CHANGED
@@ -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
|
-
|
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 '
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
data/lib/prawn/svg/version.rb
CHANGED
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'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env fish
|
2
|
+
|
3
|
+
if test (count $argv) -ne 2
|
4
|
+
echo "Usage: compare_samples [file containing sample output files] [new branch name]"
|
5
|
+
echo
|
6
|
+
echo A handy script that generates a sample SVG on both the main branch and the specified branch, and displays
|
7
|
+
echo them alongside the original SVG as rendered by Arc. Used to see whether there have been regressions or
|
8
|
+
echo improvements in rendering code changes.
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
set files $argv[1]
|
13
|
+
set branch $argv[2]
|
14
|
+
|
15
|
+
set i (cat $files | string trim | fzf)
|
16
|
+
and echo $i
|
17
|
+
and git switch main
|
18
|
+
and bundle exec rspec -e (echo $i | string replace 'spec/sample_output/' '' | string replace '.pdf' '')
|
19
|
+
and cp $i original.pdf
|
20
|
+
and git switch $branch
|
21
|
+
and bundle exec rspec -e (echo $i | string replace 'spec/sample_output/' '' | string replace '.pdf' '')
|
22
|
+
and cp $i new.pdf
|
23
|
+
and open -n original.pdf
|
24
|
+
and open -n new.pdf
|
25
|
+
and open -a Arc (echo $i | string replace sample_output sample_svg | string replace .pdf '')
|
@@ -62,7 +62,6 @@ RSpec.describe Prawn::SVG::CSS::Stylesheets do
|
|
62
62
|
width_and_styles = result.map { |k, v| [k.attributes['width'].to_i, v] }.sort_by(&:first)
|
63
63
|
|
64
64
|
expected = [
|
65
|
-
[0, [['overflow', 'hidden', false]]],
|
66
65
|
[1, [['fill', '#ff0000', false]]],
|
67
66
|
[2,
|
68
67
|
[['fill', '#ff0000', false], ['fill', '#330000', false], ['fill', '#440000', false],
|
@@ -121,7 +120,6 @@ RSpec.describe Prawn::SVG::CSS::Stylesheets do
|
|
121
120
|
it 'scans the document for style tags and adds the style information to the css parser' do
|
122
121
|
css_parser = instance_double(CssParser::Parser)
|
123
122
|
|
124
|
-
expect(css_parser).to receive(:add_block!).with('svg, symbol, image, marker, pattern, foreignObject { overflow: hidden }')
|
125
123
|
expect(css_parser).to receive(:add_block!).with("a\n before>\n x y\n inside <>>\n k j\n after\nz")
|
126
124
|
expect(css_parser).to receive(:add_block!).with('hello')
|
127
125
|
allow(css_parser).to receive(:each_rule_set)
|
@@ -175,8 +175,8 @@ describe Prawn::SVG::Elements::Base do
|
|
175
175
|
</style>
|
176
176
|
<rect width="100" height="100"></rect>
|
177
177
|
<g class="special">
|
178
|
-
<rect width="100" height="100"></rect>
|
179
|
-
<rect width="100" height="100" style="fill: yellow;"></rect>
|
178
|
+
<rect width="100" fill="blue" height="100"></rect>
|
179
|
+
<rect width="100" height="100" fill="blue" style="fill: yellow;"></rect>
|
180
180
|
</g>
|
181
181
|
</svg>
|
182
182
|
SVG
|