prawn-html 0.6.0 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b659b809526c4c961782f613e6946613232dd836ac056ca48d3dabebdbf9a02
4
- data.tar.gz: 2549b8b8b872b44f3d6f424236249e7795f97afeadf95299bf06836fb979a13a
3
+ metadata.gz: 4593c000107eaa95af317e336d16e34a7cfcffadef95e736eedaebce1c1723f0
4
+ data.tar.gz: da05881be31d5a4058b41176336d72cd1c1e190a1a1fd59b36f671f6a7e5c98e
5
5
  SHA512:
6
- metadata.gz: b58a21e4424c89db5ff388ab2507ec502ea283ba9e6234d46f3688e477ba21944ee72f813cc9ddb4454426f5e4cc7397531ffa34c74eeb7795a0bbbeaa8d34ed
7
- data.tar.gz: 0e3cfad427968577135c8c8791f698b3c5d6e95463b9364d0630d9caf87407f378c3fb725bfe11349cad210d1d0c3bba1a90555ebb9219c620e674fff0413ff8
6
+ metadata.gz: a8061299f670a1b0f895ac521e4bb6f242125830152bd3eaccb0c33dee3d6aaa1ad2ad179197a2d1ad3cb9aad28fe38aefdc18db1fd55d4148f7870e5b68038a
7
+ data.tar.gz: ab1e8d1c552f6d105ddd9ffbd565b3d1f816857d48649b5dbc89e18495e4257175d0047a7dea1be1974b26ee81e085254c37e9aa84b0da253667a159ea772bda
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Prawn HTML
2
2
  [![gem version](https://badge.fury.io/rb/prawn-html.svg)](https://rubygems.org/gems/prawn-html)
3
+ [![gem downloads](https://badgen.net/rubygems/dt/prawn-html)](https://rubygems.org/gems/prawn-html)
4
+ [![maintainability](https://api.codeclimate.com/v1/badges/db674db00817d56ca1e9/maintainability)](https://codeclimate.com/github/blocknotes/prawn-html/maintainability)
3
5
  [![linters](https://github.com/blocknotes/prawn-html/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/prawn-html/actions/workflows/linters.yml)
4
6
  [![specs](https://github.com/blocknotes/prawn-html/actions/workflows/specs.yml/badge.svg)](https://github.com/blocknotes/prawn-html/actions/workflows/specs.yml)
5
7
 
@@ -35,34 +37,34 @@ To check some examples with the PDF output see [examples](examples/) folder.
35
37
 
36
38
  ## Supported tags & attributes
37
39
 
38
- HTML tags:
39
-
40
- - **a**: link
41
- - **b**: bold
42
- - **blockquote**: block quotation element
43
- - **br**: new line
44
- - **code**: inline code element
45
- - **del**: strike-through
46
- - **div**: block element
47
- - **em**: italic
48
- - **h1** - **h6**: headings
49
- - **hr**: horizontal line
50
- - **i**: italic
51
- - **ins**: underline
52
- - **img**: image
53
- - **li**: list item
54
- - **mark**: highlight
55
- - **ol**: ordered list
56
- - **p**: block element
57
- - **pre**: preformatted text element
58
- - **s**: strike-through
59
- - **small**: smaller text
60
- - **span**: inline element
61
- - **strong**: bold
62
- - **sub**: subscript element
63
- - **sup**: superscript element
64
- - **u**: underline
65
- - **ul**: unordered list
40
+ HTML tags (using MDN definitions):
41
+
42
+ - **a**: the Anchor element
43
+ - **b**: the Bring Attention To element
44
+ - **blockquote**: the Block Quotation element
45
+ - **br**: the Line Break element
46
+ - **code**: the Inline Code element
47
+ - **del**: the Deleted Text element
48
+ - **div**: the Content Division element
49
+ - **em**: the Emphasis element
50
+ - **h1** - **h6**: the HTML Section Heading elements
51
+ - **hr**: the Thematic Break (Horizontal Rule) element
52
+ - **i**: the Idiomatic Text element
53
+ - **ins**: the added text element
54
+ - **img**: the Image Embed element
55
+ - **li**: the list item element
56
+ - **mark**: the Mark Text element
57
+ - **ol**: the Ordered List element
58
+ - **p**: the Paragraph element
59
+ - **pre**: the Preformatted Text element
60
+ - **s**: the strike-through text element
61
+ - **small**: the side comment element
62
+ - **span**: the generic inline element
63
+ - **strong**: the Strong Importance element
64
+ - **sub**: the Subscript element
65
+ - **sup**: the Superscript element
66
+ - **u**: the Unarticulated Annotation (Underline) element
67
+ - **ul**: the Unordered List element
66
68
 
67
69
  CSS attributes (dimensional units are ignored and considered in pixel):
68
70
 
data/lib/prawn-html.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrawnHtml
4
+ ADJUST_LEADING = { nil => 0.18, 'Courier' => -0.07, 'Helvetica' => -0.17, 'Times-Roman' => 0.03 }.freeze
4
5
  PX = 0.6 # conversion constant for pixel sixes
5
6
 
6
7
  COLORS = {
@@ -154,7 +154,7 @@ module PrawnHtml
154
154
  return (@initial << rule) if value == 'initial'
155
155
 
156
156
  if rule[:set] == :append_styles
157
- val = Utils.normalize_style(value)
157
+ val = Utils.normalize_style(value, rule[:values])
158
158
  (merged_styles[rule[:key]] ||= []) << val if val
159
159
  else
160
160
  opts = rule[:options] ? options[rule[:options]] : nil
@@ -62,15 +62,28 @@ module PrawnHtml
62
62
  end
63
63
  end
64
64
 
65
+ # :nocov:
66
+ def inspect
67
+ map(&:class).map(&:to_s).join(', ')
68
+ end
69
+ # :nocov:
70
+
65
71
  # Remove the last element from the context
66
72
  def remove_last
67
73
  last.on_context_remove(self) if last.respond_to?(:on_context_remove)
68
74
  @merged_styles = nil
69
75
  @last_text_node = false
70
- @previous_tag = last.tag
76
+ @previous_tag = last
71
77
  pop
72
78
  end
73
79
 
80
+ # White space is equal to 'pre'?
81
+ #
82
+ # @return [boolean] white space property of the last element is equal to 'pre'
83
+ def white_space_pre?
84
+ last && last.styles[:white_space] == :pre
85
+ end
86
+
74
87
  private
75
88
 
76
89
  def evaluate_element_styles(element, res)
@@ -9,9 +9,12 @@ module PrawnHtml
9
9
  #
10
10
  # @param pdf [PdfWrapper] target PDF wrapper
11
11
  def initialize(pdf)
12
+ @before_content = []
12
13
  @buffer = []
13
14
  @context = Context.new
14
15
  @last_margin = 0
16
+ @last_text = ''
17
+ @last_tag_open = false
15
18
  @pdf = pdf
16
19
  end
17
20
 
@@ -22,6 +25,8 @@ module PrawnHtml
22
25
  render_if_needed(element)
23
26
  apply_tag_close_styles(element)
24
27
  context.remove_last
28
+ @last_tag_open = false
29
+ @last_text = ''
25
30
  end
26
31
 
27
32
  # On tag open callback
@@ -38,6 +43,8 @@ module PrawnHtml
38
43
  options = { width: pdf.page_width, height: pdf.page_height }
39
44
  tag_class.new(tag_name, attributes: attributes, options: options).tap do |element|
40
45
  setup_element(element, element_styles: element_styles)
46
+ @before_content.push(element.before_content) if element.respond_to?(:before_content)
47
+ @last_tag_open = true
41
48
  end
42
49
  end
43
50
 
@@ -47,9 +54,10 @@ module PrawnHtml
47
54
  #
48
55
  # @return [NilClass] nil value (=> no element)
49
56
  def on_text_node(content)
50
- return if content.match?(/\A\s*\Z/)
57
+ return if context.previous_tag&.block? && content.match?(/\A\s*\Z/)
51
58
 
52
- buffer << context.merged_styles.merge(text: prepare_text(content))
59
+ text = prepare_text(content)
60
+ buffer << context.merged_styles.merge(text: text) unless text.empty?
53
61
  context.last_text_node = true
54
62
  nil
55
63
  end
@@ -70,17 +78,13 @@ module PrawnHtml
70
78
  attr_reader :buffer, :context, :last_margin, :pdf
71
79
 
72
80
  def setup_element(element, element_styles:)
73
- add_space_if_needed unless render_if_needed(element)
81
+ render_if_needed(element)
74
82
  context.add(element)
75
83
  element.process_styles(element_styles: element_styles)
76
84
  apply_tag_open_styles(element)
77
85
  element.custom_render(pdf, context) if element.respond_to?(:custom_render)
78
86
  end
79
87
 
80
- def add_space_if_needed
81
- buffer << SPACE if buffer.any? && !context.last_text_node && ![NEW_LINE, SPACE].include?(buffer.last)
82
- end
83
-
84
88
  def render_if_needed(element)
85
89
  render_needed = element&.block? && buffer.any? && buffer.last != NEW_LINE
86
90
  return false unless render_needed
@@ -104,10 +108,14 @@ module PrawnHtml
104
108
  end
105
109
 
106
110
  def prepare_text(content)
107
- white_space_pre = context.last && context.last.styles[:white_space] == :pre
108
- text = ::Oga::HTML::Entities.decode(context.before_content)
109
- text += white_space_pre ? content : content.gsub(/\A\s*\n\s*|\s*\n\s*\Z/, '').delete("\n").squeeze(' ')
110
- text
111
+ text = @before_content.any? ? ::Oga::HTML::Entities.decode(@before_content.join) : ''
112
+ @before_content.clear
113
+
114
+ return (@last_text = text + content) if context.white_space_pre?
115
+
116
+ content = content.lstrip if @last_text[-1] == ' ' || @last_tag_open
117
+ text += content.tr("\n", ' ').squeeze(' ')
118
+ @last_text = text
111
119
  end
112
120
 
113
121
  def output_content(buffer, block_styles)
@@ -129,7 +137,10 @@ module PrawnHtml
129
137
  def adjust_leading(buffer, leading)
130
138
  return leading if leading
131
139
 
132
- (buffer.map { |item| item[:size] || Context::DEFAULT_STYLES[:size] }.max * 0.055).round(4)
140
+ leadings = buffer.map do |item|
141
+ (item[:size] || Context::DEFAULT_STYLES[:size]) * (ADJUST_LEADING[item[:font]] || ADJUST_LEADING[nil])
142
+ end
143
+ leadings.max.round(4)
133
144
  end
134
145
 
135
146
  def bounds(buffer, options, block_styles)
@@ -12,7 +12,7 @@ module PrawnHtml
12
12
  end
13
13
 
14
14
  def custom_render(pdf, context)
15
- return if context.last_text_node || context.previous_tag != :br
15
+ return if context.last_text_node || !context.previous_tag.is_a?(Br)
16
16
 
17
17
  pdf.advance_cursor(BR_SPACING)
18
18
  end
@@ -13,7 +13,9 @@ module PrawnHtml
13
13
  end
14
14
 
15
15
  def before_content
16
- @counter ? "#{@counter}. " : "#{@symbol} "
16
+ return if @before_content_once
17
+
18
+ @before_content_once = @counter ? "#{@counter}. " : "#{@symbol} "
17
19
  end
18
20
 
19
21
  def block_styles
@@ -45,7 +45,7 @@ module PrawnHtml
45
45
 
46
46
  if val.match /\A#([a-f0-9]{3})\Z/ # rubocop:disable Performance/RedundantMatch
47
47
  r, g, b = Regexp.last_match[1].chars
48
- return r * 2 + g * 2 + b * 2
48
+ return (r * 2) + (g * 2) + (b * 2)
49
49
  end
50
50
  if val.match /\Argb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\Z/ # rubocop:disable Performance/RedundantMatch
51
51
  r, g, b = Regexp.last_match[1..3].map { |v| v.to_i.to_s(16) }
@@ -103,11 +103,13 @@ module PrawnHtml
103
103
  # Normalize a style value
104
104
  #
105
105
  # @param value [String] string value
106
+ # @param accepted_values [Array] allowlist of valid values (symbols)
106
107
  #
107
108
  # @return [Symbol] style value or nil
108
- def normalize_style(value)
109
+ def normalize_style(value, accepted_values)
109
110
  val = value&.strip&.downcase
110
- NORMALIZE_STYLES[val]
111
+ ret = NORMALIZE_STYLES[val]
112
+ accepted_values.include?(ret) ? ret : nil
111
113
  end
112
114
 
113
115
  # Unquotes a string
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrawnHtml # :nodoc:
4
- VERSION = '0.6.0'
4
+ VERSION = '0.6.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prawn-html
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-14 00:00:00.000000000 Z
11
+ date: 2022-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oga
@@ -83,7 +83,10 @@ files:
83
83
  homepage: https://github.com/blocknotes/prawn-html
84
84
  licenses:
85
85
  - MIT
86
- metadata: {}
86
+ metadata:
87
+ homepage_uri: https://github.com/blocknotes/prawn-html
88
+ source_code_uri: https://github.com/blocknotes/prawn-html
89
+ rubygems_mfa_required: 'true'
87
90
  post_install_message:
88
91
  rdoc_options: []
89
92
  require_paths:
@@ -99,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
102
  - !ruby/object:Gem::Version
100
103
  version: '0'
101
104
  requirements: []
102
- rubygems_version: 3.1.4
105
+ rubygems_version: 3.1.6
103
106
  signing_key:
104
107
  specification_version: 4
105
108
  summary: Prawn PDF - HTML renderer