prawn-svg 0.37.0 → 0.38.1
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 +3 -3
- data/lib/prawn/svg/attributes/transform.rb +2 -0
- data/lib/prawn/svg/css/stylesheets.rb +1 -1
- data/lib/prawn/svg/elements/base.rb +13 -1
- 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 +56 -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 +120 -55
- data/lib/prawn/svg/renderer.rb +24 -62
- data/lib/prawn/svg/version.rb +1 -1
- data/lib/prawn-svg.rb +1 -0
- data/prawn-svg.gemspec +1 -1
- data/scripts/compare_samples +25 -0
- data/spec/prawn/svg/attributes/transform_spec.rb +4 -0
- data/spec/prawn/svg/css/stylesheets_spec.rb +1 -1
- data/spec/prawn/svg/elements/text_spec.rb +210 -130
- data/spec/prawn/svg/font_registry_spec.rb +130 -10
- data/spec/prawn/svg/font_spec.rb +30 -8
- data/spec/sample_svg/bytes.svg +121 -0
- data/spec/sample_svg/positioning.svg +26 -0
- data/spec/sample_svg/subfamilies.svg +5 -1
- data/spec/sample_svg/text_use.svg +37 -0
- metadata +11 -5
- data/lib/prawn/svg/elements/depth_first_base.rb +0 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48e3c463c4c8684671c20e26e701c51d07fd58322f2a7557d03b1ccccd1621c3
|
|
4
|
+
data.tar.gz: 12e77f6409d625d9e2682f1efdd9519f983a8dc24e8dcc7371ff79febe2a43ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f212b78477d9211a1ca009f7667d05f27f0138e467939d19d1c252a017a416e2a0267e613426142c50b77e83a613cc12e2e65af8d38e3e7c71bcf0816cc062a
|
|
7
|
+
data.tar.gz: fb5f095597da89d3d6127429b51f4fe8cc125ef14c01f3046bd0a7fced4379abb9ad2bf89a35ec2f38c96e4d06775a2c8dd7edec85bca947e385c90e00b6435a
|
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
prawn-svg (0.
|
|
4
|
+
prawn-svg (0.38.1)
|
|
5
5
|
css_parser (~> 1.6)
|
|
6
6
|
matrix (~> 0.4.2)
|
|
7
7
|
prawn (>= 0.11.1, < 3)
|
|
8
|
-
rexml (>= 3.
|
|
8
|
+
rexml (>= 3.4.2, < 4)
|
|
9
9
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: http://rubygems.org/
|
|
@@ -34,7 +34,7 @@ GEM
|
|
|
34
34
|
rainbow (3.1.1)
|
|
35
35
|
rake (13.2.1)
|
|
36
36
|
regexp_parser (2.9.2)
|
|
37
|
-
rexml (3.4.
|
|
37
|
+
rexml (3.4.4)
|
|
38
38
|
rspec (3.13.0)
|
|
39
39
|
rspec-core (~> 3.13.0)
|
|
40
40
|
rspec-expectations (~> 3.13.0)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Prawn::SVG::Attributes::Transform
|
|
2
2
|
def parse_transform_attribute_and_call
|
|
3
|
+
# Some elements do not support transforms
|
|
4
|
+
return unless transformable?
|
|
3
5
|
return unless (transform = attributes['transform'])
|
|
4
6
|
|
|
5
7
|
matrix = matrix_for_pdf(parse_transform_attribute(transform))
|
|
@@ -119,7 +119,7 @@ module Prawn::SVG::CSS
|
|
|
119
119
|
when '^='
|
|
120
120
|
result << "[starts-with(@#{key}, #{xpath_quote value})]"
|
|
121
121
|
when '$='
|
|
122
|
-
result << "[substring(@#{key}, string-length(@#{key}) - #{value.length - 1}) = #{xpath_quote value}
|
|
122
|
+
result << "[substring(@#{key}, string-length(@#{key}) - #{value.length - 1}) = #{xpath_quote value}]"
|
|
123
123
|
when '*='
|
|
124
124
|
result << "[contains(@#{key}, #{xpath_quote value})]"
|
|
125
125
|
when '~='
|
|
@@ -36,7 +36,7 @@ class Prawn::SVG::Elements::Base
|
|
|
36
36
|
@attributes = {}
|
|
37
37
|
@properties = Prawn::SVG::Properties.new
|
|
38
38
|
|
|
39
|
-
if source && !state.inside_use
|
|
39
|
+
if source && add_to_elements_by_id? && !state.inside_use
|
|
40
40
|
id = source.attributes['id']
|
|
41
41
|
id = id.strip if id
|
|
42
42
|
|
|
@@ -114,6 +114,10 @@ class Prawn::SVG::Elements::Base
|
|
|
114
114
|
@calls.concat duplicate_calls(other.base_calls)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
def add_yield_call(&block)
|
|
118
|
+
add_call('svg:yield', block)
|
|
119
|
+
end
|
|
120
|
+
|
|
117
121
|
def new_call_context_from_base
|
|
118
122
|
old_calls = @calls
|
|
119
123
|
@calls = @base_calls
|
|
@@ -269,6 +273,14 @@ class Prawn::SVG::Elements::Base
|
|
|
269
273
|
['hidden', 'scroll'].include?(computed_properties.overflow)
|
|
270
274
|
end
|
|
271
275
|
|
|
276
|
+
def transformable?
|
|
277
|
+
true
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def add_to_elements_by_id?
|
|
281
|
+
true
|
|
282
|
+
end
|
|
283
|
+
|
|
272
284
|
def stroke_width
|
|
273
285
|
if computed_properties.stroke.none?
|
|
274
286
|
0
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This element base class is used for elements that render directly to the Prawn canvas.
|
|
3
|
+
#
|
|
4
|
+
# Initially when I wrote prawn-svg, I was expecting to have multiple renderers and thought separating the codebase
|
|
5
|
+
# from the Prawn renderer would be a good idea. However, it turns out that the Prawn renderer was the only one
|
|
6
|
+
# that was targeted, and it ended up being tightly coupled with the Prawn library.
|
|
7
|
+
#
|
|
8
|
+
# This class is probably how I should have written it. Direct render is required to do text rendering properly
|
|
9
|
+
# because we need to know the width of all the things we print. As of the time of this comment it's the only
|
|
10
|
+
# system that uses DirectRenderBase in prawn-svg.
|
|
11
|
+
#
|
|
12
|
+
class Prawn::SVG::Elements::DirectRenderBase < Prawn::SVG::Elements::Base
|
|
13
|
+
# Called by Renderer when it finds the svg:render call added below.
|
|
14
|
+
def render(prawn, renderer); end
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
|
|
18
|
+
def parse_and_apply
|
|
19
|
+
parse_standard_attributes
|
|
20
|
+
parse
|
|
21
|
+
apply_calls_from_standard_attributes
|
|
22
|
+
@parent_calls << ['svg:render', [self], [], []] unless computed_properties.display == 'none'
|
|
23
|
+
rescue SkipElementQuietly
|
|
24
|
+
rescue SkipElementError => e
|
|
25
|
+
@document.warnings << e.message
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -16,9 +16,9 @@ class Prawn::SVG::Elements::Polygon < Prawn::SVG::Elements::Base
|
|
|
16
16
|
def commands
|
|
17
17
|
@commands ||= [
|
|
18
18
|
Prawn::SVG::Pathable::Move.new(@points[0])
|
|
19
|
-
] + @points[1..].map
|
|
19
|
+
] + @points[1..].map do |point|
|
|
20
20
|
Prawn::SVG::Pathable::Line.new(point)
|
|
21
|
-
|
|
21
|
+
end + [
|
|
22
22
|
Prawn::SVG::Pathable::Close.new(@points[0])
|
|
23
23
|
]
|
|
24
24
|
end
|
|
@@ -1,70 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
module Prawn::SVG
|
|
2
|
+
class Elements::Text < Elements::DirectRenderBase
|
|
3
|
+
Cursor = Struct.new(:x, :y)
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
# of the element, delegating it to our root text component.
|
|
5
|
+
def parse
|
|
6
|
+
@root_component = Elements::TextComponent.new(document, source, [], state.dup)
|
|
7
|
+
@root_component.process
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
reintroduce_trailing_and_leading_whitespace if @root_component.children
|
|
10
|
+
end
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
def render(prawn, renderer)
|
|
13
|
+
return unless @root_component.children
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
end
|
|
15
|
+
@root_component.lay_out(prawn)
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
translate_x =
|
|
18
|
+
case @root_component.computed_properties.text_anchor
|
|
19
|
+
when 'middle'
|
|
20
|
+
-@root_component.calculated_width / 2.0
|
|
21
|
+
when 'end'
|
|
22
|
+
-@root_component.calculated_width
|
|
23
|
+
end
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
cursor = Cursor.new(0, document.sizing.output_height)
|
|
26
|
+
@root_component.render_component(prawn, renderer, cursor, translate_x)
|
|
27
|
+
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
printables = []
|
|
30
|
-
built_printable_queue(printables, @text_root)
|
|
29
|
+
private
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
31
|
+
def reintroduce_trailing_and_leading_whitespace
|
|
32
|
+
text_nodes = []
|
|
33
|
+
build_text_node_queue(text_nodes, @root_component)
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
remove_whitespace_only_text_nodes_and_start_and_end(text_nodes)
|
|
36
|
+
remove_text_nodes_that_are_completely_empty(text_nodes)
|
|
37
|
+
apportion_leading_and_trailing_spaces(text_nodes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def build_text_node_queue(queue, component)
|
|
41
|
+
component.children.each do |element|
|
|
42
|
+
case element
|
|
43
|
+
when Elements::TextNode
|
|
44
|
+
queue << element
|
|
45
|
+
else
|
|
46
|
+
build_text_node_queue(queue, element)
|
|
47
|
+
end
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
|
-
end
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
def remove_whitespace_only_text_nodes_and_start_and_end(text_nodes)
|
|
52
|
+
text_nodes.pop while text_nodes.last && text_nodes.last.text.empty?
|
|
53
|
+
text_nodes.shift while text_nodes.first && text_nodes.first.text.empty?
|
|
54
|
+
end
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
def remove_text_nodes_that_are_completely_empty(text_nodes)
|
|
57
|
+
text_nodes.reject! do |text_node|
|
|
58
|
+
text_node.text.empty? && !text_node.trailing_space? && !text_node.leading_space?
|
|
59
|
+
end
|
|
56
60
|
end
|
|
57
|
-
end
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
def apportion_leading_and_trailing_spaces(text_nodes)
|
|
63
|
+
text_nodes.each_cons(2) do |a, b|
|
|
64
|
+
if a.text.empty?
|
|
65
|
+
# Empty strings can only get a leading space from the previous non-empty text,
|
|
66
|
+
# and never get a trailing space
|
|
67
|
+
elsif a.trailing_space?
|
|
68
|
+
a.text += ' '
|
|
69
|
+
elsif b.leading_space?
|
|
70
|
+
b.text = " #{b.text}"
|
|
71
|
+
end
|
|
68
72
|
end
|
|
69
73
|
end
|
|
70
74
|
end
|