prawn-html 0.3.2 → 0.6.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/README.md +8 -2
- data/lib/prawn-html.rb +3 -2
- data/lib/prawn_html/attributes.rb +71 -29
- data/lib/prawn_html/callbacks/{highlight.rb → background.rb} +3 -3
- data/lib/prawn_html/context.rb +24 -17
- data/lib/prawn_html/document_renderer.rb +55 -35
- data/lib/prawn_html/html_parser.rb +35 -10
- data/lib/prawn_html/pdf_wrapper.rb +51 -4
- data/lib/prawn_html/tag.rb +24 -30
- data/lib/prawn_html/tags/a.rb +8 -1
- data/lib/prawn_html/tags/b.rb +1 -3
- data/lib/prawn_html/tags/blockquote.rb +25 -0
- data/lib/prawn_html/tags/body.rb +4 -0
- data/lib/prawn_html/tags/br.rb +2 -2
- data/lib/prawn_html/tags/code.rb +13 -0
- data/lib/prawn_html/tags/del.rb +1 -3
- data/lib/prawn_html/tags/h.rb +21 -21
- data/lib/prawn_html/tags/hr.rb +4 -4
- data/lib/prawn_html/tags/i.rb +1 -3
- data/lib/prawn_html/tags/img.rb +5 -4
- data/lib/prawn_html/tags/li.rb +20 -7
- data/lib/prawn_html/tags/mark.rb +1 -3
- data/lib/prawn_html/tags/ol.rb +20 -5
- data/lib/prawn_html/tags/p.rb +6 -6
- data/lib/prawn_html/tags/pre.rb +25 -0
- data/lib/prawn_html/tags/small.rb +4 -4
- data/lib/prawn_html/tags/sub.rb +13 -0
- data/lib/prawn_html/tags/sup.rb +13 -0
- data/lib/prawn_html/tags/u.rb +1 -3
- data/lib/prawn_html/tags/ul.rb +23 -4
- data/lib/prawn_html/utils.rb +47 -10
- data/lib/prawn_html/version.rb +1 -1
- metadata +8 -3
@@ -4,12 +4,18 @@ require 'oga'
|
|
4
4
|
|
5
5
|
module PrawnHtml
|
6
6
|
class HtmlParser
|
7
|
+
REGEXP_STYLES = /\s*([^{\s]+)\s*{\s*([^}]*?)\s*}/m.freeze
|
8
|
+
|
7
9
|
# Init the HtmlParser
|
8
10
|
#
|
9
11
|
# @param renderer [DocumentRenderer] document renderer
|
10
|
-
|
12
|
+
# @param ignore_content_tags [Array] array of tags (symbols) to skip their contents while preparing the PDF document
|
13
|
+
def initialize(renderer, ignore_content_tags: %i[script style])
|
11
14
|
@processing = false
|
15
|
+
@ignore = false
|
16
|
+
@ignore_content_tags = ignore_content_tags
|
12
17
|
@renderer = renderer
|
18
|
+
@styles = {}
|
13
19
|
end
|
14
20
|
|
15
21
|
# Processes HTML and renders it
|
@@ -17,17 +23,19 @@ module PrawnHtml
|
|
17
23
|
# @param html [String] The HTML content to process
|
18
24
|
def process(html)
|
19
25
|
@processing = !html.include?('<body')
|
20
|
-
|
21
|
-
traverse_nodes(
|
26
|
+
@document = Oga.parse_html(html)
|
27
|
+
traverse_nodes(document.children)
|
22
28
|
renderer.flush
|
23
29
|
end
|
24
30
|
|
25
31
|
private
|
26
32
|
|
27
|
-
attr_reader :processing, :renderer
|
33
|
+
attr_reader :document, :ignore, :processing, :renderer, :styles
|
28
34
|
|
29
35
|
def traverse_nodes(nodes)
|
30
36
|
nodes.each do |node|
|
37
|
+
next if node.is_a?(Oga::XML::Comment)
|
38
|
+
|
31
39
|
element = node_open(node)
|
32
40
|
traverse_nodes(node.children) if node.children.any?
|
33
41
|
node_close(element) if element
|
@@ -37,21 +45,27 @@ module PrawnHtml
|
|
37
45
|
def node_open(node)
|
38
46
|
tag = node.is_a?(Oga::XML::Element) && init_element(node)
|
39
47
|
return unless processing
|
48
|
+
return IgnoredTag.new(tag) if ignore
|
40
49
|
return renderer.on_text_node(node.text) unless tag
|
41
50
|
|
42
|
-
attributes
|
43
|
-
renderer.on_tag_open(tag, attributes)
|
51
|
+
renderer.on_tag_open(tag, attributes: prepare_attributes(node), element_styles: styles[node])
|
44
52
|
end
|
45
53
|
|
46
54
|
def init_element(node)
|
47
55
|
node.name.downcase.to_sym.tap do |tag_name|
|
48
56
|
@processing = true if tag_name == :body
|
49
|
-
|
57
|
+
@ignore = true if @processing && @ignore_content_tags.include?(tag_name)
|
58
|
+
process_styles(node.text) if tag_name == :style
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
53
|
-
def
|
54
|
-
|
62
|
+
def process_styles(text_styles)
|
63
|
+
hash_styles = text_styles.scan(REGEXP_STYLES).to_h
|
64
|
+
hash_styles.each do |selector, rule|
|
65
|
+
document.css(selector).each do |node|
|
66
|
+
styles[node] = rule
|
67
|
+
end
|
68
|
+
end
|
55
69
|
end
|
56
70
|
|
57
71
|
def prepare_attributes(node)
|
@@ -61,10 +75,21 @@ module PrawnHtml
|
|
61
75
|
end
|
62
76
|
|
63
77
|
def node_close(element)
|
64
|
-
|
78
|
+
if processing
|
79
|
+
renderer.on_tag_close(element) unless ignore
|
80
|
+
@ignore = false if ignore && @ignore_content_tags.include?(element.tag)
|
81
|
+
end
|
65
82
|
@processing = false if element.tag == :body
|
66
83
|
end
|
67
84
|
end
|
68
85
|
|
86
|
+
class IgnoredTag
|
87
|
+
attr_accessor :tag
|
88
|
+
|
89
|
+
def initialize(tag_name)
|
90
|
+
@tag = tag_name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
69
94
|
HtmlHandler = HtmlParser
|
70
95
|
end
|
@@ -6,7 +6,7 @@ module PrawnHtml
|
|
6
6
|
class PdfWrapper
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
def_delegators :@pdf, :
|
9
|
+
def_delegators :@pdf, :start_new_page
|
10
10
|
|
11
11
|
# Wrapper for Prawn PDF Document
|
12
12
|
#
|
@@ -24,6 +24,46 @@ module PrawnHtml
|
|
24
24
|
pdf.move_down(move_down)
|
25
25
|
end
|
26
26
|
|
27
|
+
# Calculate the height of a buffer of items
|
28
|
+
#
|
29
|
+
# @param buffer [Array] Buffer of items
|
30
|
+
# @param options [Hash] Output options
|
31
|
+
#
|
32
|
+
# @return [Float] calculated height
|
33
|
+
def calc_buffer_height(buffer, options)
|
34
|
+
pdf.height_of_formatted(buffer, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Calculate the width of a buffer of items
|
38
|
+
#
|
39
|
+
# @param buffer [Array] Buffer of items
|
40
|
+
#
|
41
|
+
# @return [Float] calculated width
|
42
|
+
def calc_buffer_width(buffer)
|
43
|
+
width = 0
|
44
|
+
buffer.each do |item|
|
45
|
+
font_family = item[:font] || pdf.font.name
|
46
|
+
pdf.font(font_family, size: item[:size] || pdf.font_size) do
|
47
|
+
width += pdf.width_of(item[:text], inline_format: true)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
width
|
51
|
+
end
|
52
|
+
|
53
|
+
# Height of the page
|
54
|
+
#
|
55
|
+
# @return [Float] height
|
56
|
+
def page_height
|
57
|
+
pdf.bounds.height
|
58
|
+
end
|
59
|
+
|
60
|
+
# Width of the page
|
61
|
+
#
|
62
|
+
# @return [Float] width
|
63
|
+
def page_width
|
64
|
+
pdf.bounds.width
|
65
|
+
end
|
66
|
+
|
27
67
|
# Draw a rectangle
|
28
68
|
#
|
29
69
|
# @param x [Float] left position of the rectangle
|
@@ -66,12 +106,12 @@ module PrawnHtml
|
|
66
106
|
# @param buffer [Array] array of text items
|
67
107
|
# @param options [Hash] hash of options
|
68
108
|
# @param bounding_box [Array] bounding box arguments, if bounded
|
69
|
-
def puts(buffer, options, bounding_box: nil)
|
70
|
-
return
|
109
|
+
def puts(buffer, options, bounding_box: nil, left_indent: 0)
|
110
|
+
return output_buffer(buffer, options, left_indent: left_indent) unless bounding_box
|
71
111
|
|
72
112
|
current_y = pdf.cursor
|
73
113
|
pdf.bounding_box(*bounding_box) do
|
74
|
-
|
114
|
+
output_buffer(buffer, options, left_indent: left_indent)
|
75
115
|
end
|
76
116
|
pdf.move_cursor_to(current_y)
|
77
117
|
end
|
@@ -90,5 +130,12 @@ module PrawnHtml
|
|
90
130
|
private
|
91
131
|
|
92
132
|
attr_reader :pdf
|
133
|
+
|
134
|
+
def output_buffer(buffer, options, left_indent:)
|
135
|
+
formatted_text = proc { pdf.formatted_text(buffer, options) }
|
136
|
+
return formatted_text.call if left_indent == 0
|
137
|
+
|
138
|
+
pdf.indent(left_indent, 0, &formatted_text)
|
139
|
+
end
|
93
140
|
end
|
94
141
|
end
|
data/lib/prawn_html/tag.rb
CHANGED
@@ -2,7 +2,16 @@
|
|
2
2
|
|
3
3
|
module PrawnHtml
|
4
4
|
class Tag
|
5
|
-
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
CALLBACKS = {
|
8
|
+
'Background' => Callbacks::Background,
|
9
|
+
'StrikeThrough' => Callbacks::StrikeThrough
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
TAG_CLASSES = %w[A B Blockquote Body Br Code Del Div H Hr I Img Li Mark Ol P Pre Small Span Sub Sup U Ul].freeze
|
13
|
+
|
14
|
+
def_delegators :@attrs, :styles, :update_styles
|
6
15
|
|
7
16
|
attr_accessor :parent
|
8
17
|
attr_reader :attrs, :tag
|
@@ -11,12 +20,11 @@ module PrawnHtml
|
|
11
20
|
#
|
12
21
|
# @param tag [Symbol] tag name
|
13
22
|
# @param attributes [Hash] hash of element attributes
|
14
|
-
# @param
|
15
|
-
def initialize(tag, attributes
|
23
|
+
# @param options [Hash] options (container width/height/etc.)
|
24
|
+
def initialize(tag, attributes: {}, options: {})
|
16
25
|
@tag = tag
|
17
|
-
|
26
|
+
@options = options
|
18
27
|
@attrs = Attributes.new(attributes)
|
19
|
-
process_styles(document_styles, element_styles)
|
20
28
|
end
|
21
29
|
|
22
30
|
# Is a block tag?
|
@@ -35,6 +43,16 @@ module PrawnHtml
|
|
35
43
|
block_styles
|
36
44
|
end
|
37
45
|
|
46
|
+
# Process tag styles
|
47
|
+
#
|
48
|
+
# @param element_styles [String] extra styles to apply to the element
|
49
|
+
def process_styles(element_styles: nil)
|
50
|
+
attrs.merge_text_styles!(tag_styles, options: options) if respond_to?(:tag_styles)
|
51
|
+
attrs.merge_text_styles!(element_styles, options: options) if element_styles
|
52
|
+
attrs.merge_text_styles!(attrs.style, options: options)
|
53
|
+
attrs.merge_text_styles!(extra_styles, options: options) if respond_to?(:extra_styles)
|
54
|
+
end
|
55
|
+
|
38
56
|
# Styles to apply on tag closing
|
39
57
|
#
|
40
58
|
# @return [Hash] hash of styles to apply
|
@@ -42,13 +60,6 @@ module PrawnHtml
|
|
42
60
|
styles.slice(*Attributes::STYLES_APPLY[:tag_close])
|
43
61
|
end
|
44
62
|
|
45
|
-
# Styles hash
|
46
|
-
#
|
47
|
-
# @return [Hash] hash of styles
|
48
|
-
def styles
|
49
|
-
attrs.styles
|
50
|
-
end
|
51
|
-
|
52
63
|
# Styles to apply on tag opening
|
53
64
|
#
|
54
65
|
# @return [Hash] hash of styles to apply
|
@@ -74,23 +85,6 @@ module PrawnHtml
|
|
74
85
|
|
75
86
|
private
|
76
87
|
|
77
|
-
|
78
|
-
selectors = [
|
79
|
-
tag.to_s,
|
80
|
-
attrs['class'] ? ".#{attrs['class']}" : nil,
|
81
|
-
attrs['id'] ? "##{attrs['id']}" : nil
|
82
|
-
].compact!
|
83
|
-
document_styles.each_with_object({}) do |(sel, attributes), res|
|
84
|
-
res.merge!(attributes) if selectors.include?(sel)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def process_styles(document_styles, element_styles)
|
89
|
-
attrs.merge_styles!(attrs.process_styles(tag_styles)) if respond_to?(:tag_styles)
|
90
|
-
doc_styles = evaluate_document_styles(document_styles)
|
91
|
-
attrs.merge_styles!(doc_styles)
|
92
|
-
el_styles = Attributes.parse_styles(element_styles)
|
93
|
-
attrs.merge_styles!(attrs.process_styles(el_styles)) if el_styles
|
94
|
-
end
|
88
|
+
attr_reader :options
|
95
89
|
end
|
96
90
|
end
|
data/lib/prawn_html/tags/a.rb
CHANGED
@@ -5,8 +5,15 @@ module PrawnHtml
|
|
5
5
|
class A < Tag
|
6
6
|
ELEMENTS = [:a].freeze
|
7
7
|
|
8
|
+
def extra_styles
|
9
|
+
attrs.href ? "href: #{attrs.href}" : nil
|
10
|
+
end
|
11
|
+
|
8
12
|
def tag_styles
|
9
|
-
|
13
|
+
<<~STYLES
|
14
|
+
color: #00E;
|
15
|
+
text-decoration: underline;
|
16
|
+
STYLES
|
10
17
|
end
|
11
18
|
end
|
12
19
|
end
|
data/lib/prawn_html/tags/b.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrawnHtml
|
4
|
+
module Tags
|
5
|
+
class Blockquote < Tag
|
6
|
+
ELEMENTS = [:blockquote].freeze
|
7
|
+
|
8
|
+
MARGIN_BOTTOM = 12.7
|
9
|
+
MARGIN_LEFT = 40.4
|
10
|
+
MARGIN_TOP = 12.7
|
11
|
+
|
12
|
+
def block?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def tag_styles
|
17
|
+
<<~STYLES
|
18
|
+
margin-bottom: #{MARGIN_BOTTOM}px;
|
19
|
+
margin-left: #{MARGIN_LEFT}px;
|
20
|
+
margin-top: #{MARGIN_TOP}px;
|
21
|
+
STYLES
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/prawn_html/tags/body.rb
CHANGED
data/lib/prawn_html/tags/br.rb
CHANGED
@@ -5,14 +5,14 @@ module PrawnHtml
|
|
5
5
|
class Br < Tag
|
6
6
|
ELEMENTS = [:br].freeze
|
7
7
|
|
8
|
-
BR_SPACING = Utils.convert_size('
|
8
|
+
BR_SPACING = Utils.convert_size('17')
|
9
9
|
|
10
10
|
def block?
|
11
11
|
true
|
12
12
|
end
|
13
13
|
|
14
14
|
def custom_render(pdf, context)
|
15
|
-
return if context.last_text_node
|
15
|
+
return if context.last_text_node || context.previous_tag != :br
|
16
16
|
|
17
17
|
pdf.advance_cursor(BR_SPACING)
|
18
18
|
end
|
data/lib/prawn_html/tags/del.rb
CHANGED
data/lib/prawn_html/tags/h.rb
CHANGED
@@ -6,30 +6,30 @@ module PrawnHtml
|
|
6
6
|
ELEMENTS = [:h1, :h2, :h3, :h4, :h5, :h6].freeze
|
7
7
|
|
8
8
|
MARGINS_TOP = {
|
9
|
-
h1: 25
|
9
|
+
h1: 25,
|
10
10
|
h2: 20.5,
|
11
|
-
h3:
|
12
|
-
h4:
|
11
|
+
h3: 18,
|
12
|
+
h4: 21.2,
|
13
13
|
h5: 21.2,
|
14
|
-
h6:
|
14
|
+
h6: 22.8
|
15
15
|
}.freeze
|
16
16
|
|
17
17
|
MARGINS_BOTTOM = {
|
18
|
-
h1:
|
19
|
-
h2:
|
20
|
-
h3:
|
21
|
-
h4:
|
22
|
-
h5:
|
23
|
-
h6:
|
18
|
+
h1: 15.8,
|
19
|
+
h2: 15.8,
|
20
|
+
h3: 15.8,
|
21
|
+
h4: 20,
|
22
|
+
h5: 21.4,
|
23
|
+
h6: 24.8
|
24
24
|
}.freeze
|
25
25
|
|
26
26
|
SIZES = {
|
27
|
-
h1: 31,
|
28
|
-
h2:
|
29
|
-
h3: 18.
|
30
|
-
h4:
|
27
|
+
h1: 31.5,
|
28
|
+
h2: 24,
|
29
|
+
h3: 18.7,
|
30
|
+
h4: 15.7,
|
31
31
|
h5: 13,
|
32
|
-
h6: 10.
|
32
|
+
h6: 10.8
|
33
33
|
}.freeze
|
34
34
|
|
35
35
|
def block?
|
@@ -37,12 +37,12 @@ module PrawnHtml
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def tag_styles
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
<<~STYLES
|
41
|
+
font-size: #{SIZES[tag]}px;
|
42
|
+
font-weight: bold;
|
43
|
+
margin-bottom: #{MARGINS_BOTTOM[tag]}px;
|
44
|
+
margin-top: #{MARGINS_TOP[tag]}px;
|
45
|
+
STYLES
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|