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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5c28b2e706923d04785979a68f0e457f14df7c71b519d16db6e0bb45e3458bc
4
- data.tar.gz: 3ade59453c7005f624b4dcd36d9930f27285291c410b00fe57b3d3d278673e4c
3
+ metadata.gz: 48e3c463c4c8684671c20e26e701c51d07fd58322f2a7557d03b1ccccd1621c3
4
+ data.tar.gz: 12e77f6409d625d9e2682f1efdd9519f983a8dc24e8dcc7371ff79febe2a43ea
5
5
  SHA512:
6
- metadata.gz: 250c187430ff8042898b9ee55a595162824613e13a9760a8e32dd21e5a026dfabe012a80f3c19442b3a97635ab020aa1eb113cd70dcc86ffef888f6bc2d39fa1
7
- data.tar.gz: 1d79258d401547d57dd694878e08d9a50d0166cce4fefd9351cfda0ef6a7c3096910a88a29650993f0eeadd44d7f2fba21d25f438e31660750cc4b847cacc760
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.37.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.3.9, < 4)
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.0)
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 { |point|
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
- class Prawn::SVG::Elements::Text < Prawn::SVG::Elements::DepthFirstBase
2
- private
1
+ module Prawn::SVG
2
+ class Elements::Text < Elements::DirectRenderBase
3
+ Cursor = Struct.new(:x, :y)
3
4
 
4
- # This class doesn't represent an element in the SVG document; it's here
5
- # to hold together text information. Because of this, we overload the
6
- # parse_step and apply_step methods, and bypass all the normal processing
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
- def parse_step
10
- state.text = Prawn::SVG::Elements::TextComponent::TextState.new
9
+ reintroduce_trailing_and_leading_whitespace if @root_component.children
10
+ end
11
11
 
12
- @text_root = Prawn::SVG::Elements::TextComponent.new(document, source, nil, state.dup)
13
- @text_root.parse_step
12
+ def render(prawn, renderer)
13
+ return unless @root_component.children
14
14
 
15
- reintroduce_trailing_and_leading_whitespace
16
- end
15
+ @root_component.lay_out(prawn)
17
16
 
18
- def apply_step(calls)
19
- @base_calls = @calls = calls
20
- add_call_and_enter 'text_group'
21
- @text_root.apply_step(@calls)
22
- end
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
- def drawable?
25
- false
26
- end
25
+ cursor = Cursor.new(0, document.sizing.output_height)
26
+ @root_component.render_component(prawn, renderer, cursor, translate_x)
27
+ end
27
28
 
28
- def reintroduce_trailing_and_leading_whitespace
29
- printables = []
30
- built_printable_queue(printables, @text_root)
29
+ private
31
30
 
32
- remove_whitespace_only_printables_and_start_and_end(printables)
33
- remove_printables_that_are_completely_empty(printables)
34
- apportion_leading_and_trailing_spaces(printables)
35
- end
31
+ def reintroduce_trailing_and_leading_whitespace
32
+ text_nodes = []
33
+ build_text_node_queue(text_nodes, @root_component)
36
34
 
37
- def built_printable_queue(queue, component)
38
- component.commands.each do |command|
39
- case command
40
- when Prawn::SVG::Elements::TextComponent::Printable
41
- queue << command
42
- else
43
- built_printable_queue(queue, command)
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
- def remove_whitespace_only_printables_and_start_and_end(printables)
49
- printables.pop while printables.last && printables.last.text.empty?
50
- printables.shift while printables.first && printables.first.text.empty?
51
- end
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
- def remove_printables_that_are_completely_empty(printables)
54
- printables.reject! do |printable|
55
- printable.text.empty? && !printable.trailing_space? && !printable.leading_space?
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
- def apportion_leading_and_trailing_spaces(printables)
60
- printables.each_cons(2) do |a, b|
61
- if a.text.empty?
62
- # Empty strings can only get a leading space from the previous non-empty text,
63
- # and never get a trailing space
64
- elsif a.trailing_space?
65
- a.text += ' '
66
- elsif b.leading_space?
67
- b.text = " #{b.text}"
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