prawn-html 0.1.4 → 0.2.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 +10 -2
- data/lib/prawn-html.rb +2 -1
- data/lib/prawn_html/attributes.rb +78 -47
- data/lib/prawn_html/context.rb +15 -11
- data/lib/prawn_html/document_renderer.rb +28 -40
- data/lib/prawn_html/html_handler.rb +0 -2
- data/lib/prawn_html/tag.rb +95 -0
- data/lib/prawn_html/tags/a.rb +3 -5
- data/lib/prawn_html/tags/b.rb +2 -4
- data/lib/prawn_html/tags/body.rb +1 -3
- data/lib/prawn_html/tags/br.rb +1 -3
- data/lib/prawn_html/tags/del.rb +5 -7
- data/lib/prawn_html/tags/div.rb +1 -3
- data/lib/prawn_html/tags/h.rb +3 -5
- data/lib/prawn_html/tags/hr.rb +17 -5
- data/lib/prawn_html/tags/i.rb +2 -4
- data/lib/prawn_html/tags/img.rb +10 -12
- data/lib/prawn_html/tags/li.rb +4 -6
- data/lib/prawn_html/tags/mark.rb +5 -7
- data/lib/prawn_html/tags/p.rb +3 -5
- data/lib/prawn_html/tags/small.rb +1 -3
- data/lib/prawn_html/tags/span.rb +1 -3
- data/lib/prawn_html/tags/u.rb +2 -2
- data/lib/prawn_html/tags/ul.rb +3 -5
- data/lib/prawn_html/version.rb +1 -1
- metadata +3 -3
- data/lib/prawn_html/tags/base.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 319cd3772829ddae0d52b3228b8544cb3906c32c58725082ce95da675bdb841f
|
4
|
+
data.tar.gz: e4cc3ec1cdbbe8ad655ef236d44a2c179178cf913d83f7bb307b2fe529927ae1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58b388050c3f8c94075bab09353ca3974956b9eebb3fa1a7fdfff488ced919dc8182d744c620d2c2268b4fd26b76bbeaacfcb58717fdeee7c16862665176e66c
|
7
|
+
data.tar.gz: 386d529584f5cc8b9b6c326500270761c3a53f859243714980cb188d9496f820158ed83e1d399dc361e368b0a63b2eeaa585843719d83ca2fa75729b6ebffef4
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Prawn HTML
|
2
|
-
[](https://
|
2
|
+
[](https://rubygems.org/gems/prawn-html)
|
3
3
|
[](https://github.com/blocknotes/prawn-html/actions/workflows/linters.yml)
|
4
4
|
[](https://github.com/blocknotes/prawn-html/actions/workflows/specs.yml)
|
5
5
|
|
@@ -8,7 +8,8 @@ HTML to PDF renderer using [Prawn PDF](https://github.com/prawnpdf/prawn).
|
|
8
8
|
Features:
|
9
9
|
- support a [good set](#supported-tags--attributes) of HTML tags and CSS properties;
|
10
10
|
- handle [document styles](#document-styles);
|
11
|
-
-
|
11
|
+
- custom [data attributes](#data-attributes) for Prawn PDF features;
|
12
|
+
- no extra settings: it just parses an input HTML and outputs to a Prawn PDF document.
|
12
13
|
|
13
14
|
**Notice**: render HTML documents properly is not an easy task, this gem support only some HTML tags and a small set of CSS attributes. If you need more rendering accuracy take a look at other projects like WickedPDF.
|
14
15
|
|
@@ -77,6 +78,13 @@ CSS attributes (dimensional units are ignored and considered in pixel):
|
|
77
78
|
- **text-decoration**: `underline`, ex. `style="text-decoration: underline"`
|
78
79
|
- **width**: for *img* tag, support also percentage, ex. `<img src="image.jpg" style="width: 50%; height: 200px"/>`
|
79
80
|
|
81
|
+
## Data attributes
|
82
|
+
|
83
|
+
Some custom data attributes are used to pass options:
|
84
|
+
|
85
|
+
- **dash**: for *hr* tag, accepts an integer or a list of integers), ex. `data-data="2, 4, 3"`
|
86
|
+
- **mode**: allow to specify the text mode (stroke|fill||fill_stroke), ex. `data-mode="stroke"`
|
87
|
+
|
80
88
|
## Document styles
|
81
89
|
|
82
90
|
[Experimental feature] You can define document CSS rules inside an _head_ tag, but with a limited support for now.
|
data/lib/prawn-html.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'oga'
|
3
4
|
require 'prawn'
|
4
5
|
|
5
|
-
require 'prawn_html/
|
6
|
+
require 'prawn_html/tag'
|
6
7
|
Dir["#{__dir__}/prawn_html/tags/*.rb"].sort.each { |f| require f }
|
7
8
|
|
8
9
|
Dir["#{__dir__}/prawn_html/callbacks/*.rb"].sort.each { |f| require f }
|
@@ -3,57 +3,78 @@
|
|
3
3
|
require 'ostruct'
|
4
4
|
|
5
5
|
module PrawnHtml
|
6
|
-
class Attributes
|
7
|
-
attr_reader :
|
6
|
+
class Attributes < OpenStruct
|
7
|
+
attr_reader :styles
|
8
|
+
|
9
|
+
STYLES_APPLY = {
|
10
|
+
block: %i[align leading margin_left padding_left],
|
11
|
+
tag_close: %i[margin_bottom padding_bottom],
|
12
|
+
tag_open: %i[margin_top padding_top],
|
13
|
+
text_node: %i[background callback character_spacing color font link size styles]
|
14
|
+
}.freeze
|
8
15
|
|
9
16
|
STYLES_LIST = {
|
10
|
-
# styles
|
11
|
-
'background' => { key: :background, set: :convert_color
|
12
|
-
'
|
13
|
-
'
|
14
|
-
'font-
|
15
|
-
'font-
|
16
|
-
'font-
|
17
|
-
'
|
18
|
-
|
19
|
-
'
|
20
|
-
|
21
|
-
|
22
|
-
'
|
23
|
-
|
24
|
-
|
25
|
-
'margin-
|
26
|
-
'padding-
|
27
|
-
|
28
|
-
'
|
29
|
-
'
|
17
|
+
# text node styles
|
18
|
+
'background' => { key: :background, set: :convert_color },
|
19
|
+
'callback' => { key: :callback, set: :copy },
|
20
|
+
'color' => { key: :color, set: :convert_color },
|
21
|
+
'font-family' => { key: :font, set: :unquote },
|
22
|
+
'font-size' => { key: :size, set: :convert_size },
|
23
|
+
'font-style' => { key: :styles, set: :append_symbol },
|
24
|
+
'font-weight' => { key: :styles, set: :append_symbol },
|
25
|
+
'href' => { key: :link, set: :copy },
|
26
|
+
'letter-spacing' => { key: :character_spacing, set: :convert_float },
|
27
|
+
'text-decoration' => { key: :styles, set: :append_symbol },
|
28
|
+
# tag opening styles
|
29
|
+
'margin-top' => { key: :margin_top, set: :convert_size },
|
30
|
+
'padding-top' => { key: :padding_top, set: :convert_size },
|
31
|
+
# tag closing styles
|
32
|
+
'margin-bottom' => { key: :margin_bottom, set: :convert_size },
|
33
|
+
'padding-bottom' => { key: :padding_bottom, set: :convert_size },
|
34
|
+
# block styles
|
35
|
+
'line-height' => { key: :leading, set: :convert_size },
|
36
|
+
'margin-left' => { key: :margin_left, set: :convert_size },
|
37
|
+
'padding-left' => { key: :padding_left, set: :convert_size },
|
38
|
+
'text-align' => { key: :align, set: :convert_symbol }
|
30
39
|
}.freeze
|
31
40
|
|
32
41
|
STYLES_MERGE = %i[margin_left padding_left].freeze
|
33
42
|
|
34
43
|
# Init the Attributes
|
35
|
-
|
36
|
-
|
37
|
-
def initialize(attributes)
|
38
|
-
@hash = ::OpenStruct.new(attributes)
|
39
|
-
@options = {}
|
40
|
-
@post_styles = {}
|
41
|
-
@pre_styles = {}
|
44
|
+
def initialize(attributes = {})
|
45
|
+
super
|
42
46
|
@styles = {} # result styles
|
43
|
-
|
44
|
-
|
47
|
+
return unless style
|
48
|
+
|
49
|
+
styles_hash = Attributes.parse_styles(style)
|
50
|
+
process_styles(styles_hash)
|
45
51
|
end
|
46
52
|
|
47
|
-
# Processes the
|
53
|
+
# Processes the data attributes
|
48
54
|
#
|
49
|
-
# @
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
55
|
+
# @return [Hash] hash of data attributes with 'data-' prefix removed and stripped values
|
56
|
+
def data
|
57
|
+
to_h.each_with_object({}) do |(key, value), res|
|
58
|
+
data_key = key.match /\Adata-(.+)/
|
59
|
+
res[data_key[1]] = value.strip if data_key
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Merge already parsed styles
|
64
|
+
#
|
65
|
+
# @param parsed_styles [Hash] hash of parsed styles
|
66
|
+
def merge_styles!(parsed_styles)
|
67
|
+
@styles.merge!(parsed_styles)
|
68
|
+
end
|
54
69
|
|
55
|
-
|
70
|
+
# Processes the styles attributes
|
71
|
+
#
|
72
|
+
# @param styles_hash [Hash] hash of styles attributes
|
73
|
+
def process_styles(styles_hash)
|
74
|
+
styles_hash.each do |key, value|
|
75
|
+
apply_rule!(@styles, STYLES_LIST[key], value)
|
56
76
|
end
|
77
|
+
@styles
|
57
78
|
end
|
58
79
|
|
59
80
|
class << self
|
@@ -95,7 +116,6 @@ module PrawnHtml
|
|
95
116
|
else
|
96
117
|
val.to_f * PX
|
97
118
|
end
|
98
|
-
# pdf.bounds.height
|
99
119
|
val.round(4)
|
100
120
|
end
|
101
121
|
|
@@ -108,19 +128,28 @@ module PrawnHtml
|
|
108
128
|
value.to_sym if value && !value.match?(/\A\s*\Z/)
|
109
129
|
end
|
110
130
|
|
131
|
+
# Copy a value without conversion
|
132
|
+
#
|
133
|
+
# @param value
|
134
|
+
#
|
135
|
+
# @return value
|
136
|
+
def copy(value)
|
137
|
+
value
|
138
|
+
end
|
139
|
+
|
111
140
|
# Merges attributes
|
112
141
|
#
|
113
|
-
# @param
|
142
|
+
# @param attributes [Hash] target attributes hash
|
114
143
|
# @param key [Symbol] key
|
115
144
|
# @param value
|
116
145
|
#
|
117
146
|
# @return [Hash] the updated hash of attributes
|
118
|
-
def merge_attr!(
|
147
|
+
def merge_attr!(attributes, key, value)
|
119
148
|
return unless key
|
120
|
-
return (
|
149
|
+
return (attributes[key] = value) unless Attributes::STYLES_MERGE.include?(key)
|
121
150
|
|
122
|
-
|
123
|
-
|
151
|
+
attributes[key] ||= 0
|
152
|
+
attributes[key] += value
|
124
153
|
end
|
125
154
|
|
126
155
|
# Parses a string of styles
|
@@ -146,11 +175,13 @@ module PrawnHtml
|
|
146
175
|
|
147
176
|
private
|
148
177
|
|
149
|
-
def apply_rule(rule, value)
|
178
|
+
def apply_rule!(result, rule, value)
|
179
|
+
return unless rule
|
180
|
+
|
150
181
|
if rule[:set] == :append_symbol
|
151
|
-
(
|
182
|
+
(result[rule[:key]] ||= []) << Attributes.convert_symbol(value)
|
152
183
|
else
|
153
|
-
|
184
|
+
result[rule[:key]] = Attributes.send(rule[:set], value)
|
154
185
|
end
|
155
186
|
end
|
156
187
|
end
|
data/lib/prawn_html/context.rb
CHANGED
@@ -10,34 +10,37 @@ module PrawnHtml
|
|
10
10
|
def initialize(*_args)
|
11
11
|
super
|
12
12
|
@last_margin = 0
|
13
|
+
@last_text_node = false
|
13
14
|
end
|
14
15
|
|
16
|
+
# Evaluate before content
|
17
|
+
#
|
18
|
+
# @return [String] before content string
|
15
19
|
def before_content
|
16
|
-
return '' if empty?
|
20
|
+
return '' if empty? || !last.respond_to?(:tag_styles)
|
17
21
|
|
18
|
-
last.
|
22
|
+
last.tag_styles[:before_content].to_s
|
19
23
|
end
|
20
24
|
|
21
|
-
# Merges the context
|
25
|
+
# Merges the context block styles
|
22
26
|
#
|
23
|
-
# @return [Hash] the hash of merged
|
24
|
-
def
|
27
|
+
# @return [Hash] the hash of merged styles
|
28
|
+
def block_styles
|
25
29
|
each_with_object({}) do |element, res|
|
26
|
-
element.
|
30
|
+
element.block_styles.each do |key, value|
|
27
31
|
Attributes.merge_attr!(res, key, value)
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
|
-
# Merge the context styles
|
36
|
+
# Merge the context styles for text nodes
|
33
37
|
#
|
34
38
|
# @return [Hash] the hash of merged styles
|
35
|
-
def
|
36
|
-
|
39
|
+
def text_node_styles
|
40
|
+
each_with_object(base_styles) do |element, res|
|
37
41
|
evaluate_element_styles(element, res)
|
38
42
|
element.update_styles(res) if element.respond_to?(:update_styles)
|
39
43
|
end
|
40
|
-
base_styles.merge(context_styles)
|
41
44
|
end
|
42
45
|
|
43
46
|
private
|
@@ -49,7 +52,8 @@ module PrawnHtml
|
|
49
52
|
end
|
50
53
|
|
51
54
|
def evaluate_element_styles(element, res)
|
52
|
-
element.styles.
|
55
|
+
styles = element.styles.slice(*Attributes::STYLES_APPLY[:text_node])
|
56
|
+
styles.each do |key, val|
|
53
57
|
if res.include?(key) && res[key].is_a?(Array)
|
54
58
|
res[key] += val
|
55
59
|
else
|
@@ -4,7 +4,6 @@ module PrawnHtml
|
|
4
4
|
class DocumentRenderer
|
5
5
|
NEW_LINE = { text: "\n" }.freeze
|
6
6
|
SPACE = { text: ' ' }.freeze
|
7
|
-
TAG_CLASSES = [Tags::A, Tags::B, Tags::Body, Tags::Br, Tags::Del, Tags::Div, Tags::H, Tags::Hr, Tags::I, Tags::Img, Tags::Li, Tags::Mark, Tags::P, Tags::Small, Tags::Span, Tags::U, Tags::Ul].freeze
|
8
7
|
|
9
8
|
# Init the DocumentRenderer
|
10
9
|
#
|
@@ -12,40 +11,40 @@ module PrawnHtml
|
|
12
11
|
def initialize(pdf)
|
13
12
|
@buffer = []
|
14
13
|
@context = Context.new
|
15
|
-
@
|
14
|
+
@document_styles = {}
|
16
15
|
@pdf = pdf
|
17
16
|
end
|
18
17
|
|
19
|
-
#
|
18
|
+
# Evaluate the document styles and store the internally
|
20
19
|
#
|
21
20
|
# @param styles [Hash] styles hash with CSS selectors as keys and rules as values
|
22
21
|
def assign_document_styles(styles)
|
23
|
-
@
|
22
|
+
@document_styles = styles.transform_values do |style_rules|
|
24
23
|
Attributes.new(style: style_rules).styles
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
28
27
|
# On tag close callback
|
29
28
|
#
|
30
|
-
# @param element [
|
29
|
+
# @param element [Tag] closing element wrapper
|
31
30
|
def on_tag_close(element)
|
32
31
|
render_if_needed(element)
|
33
|
-
|
32
|
+
apply_tag_close_styles(element)
|
34
33
|
context.last_text_node = false
|
35
34
|
context.pop
|
36
35
|
end
|
37
36
|
|
38
37
|
# On tag open callback
|
39
38
|
#
|
40
|
-
# @param
|
39
|
+
# @param tag_name [String] the tag name of the opening element
|
41
40
|
# @param attributes [Hash] an hash of the element attributes
|
42
41
|
#
|
43
|
-
# @return [
|
44
|
-
def on_tag_open(
|
45
|
-
tag_class =
|
42
|
+
# @return [Tag] the opening element wrapper
|
43
|
+
def on_tag_open(tag_name, attributes)
|
44
|
+
tag_class = Tag.class_for(tag_name)
|
46
45
|
return unless tag_class
|
47
46
|
|
48
|
-
tag_class.new(
|
47
|
+
tag_class.new(tag_name, attributes, document_styles).tap do |element|
|
49
48
|
setup_element(element)
|
50
49
|
end
|
51
50
|
end
|
@@ -58,8 +57,9 @@ module PrawnHtml
|
|
58
57
|
def on_text_node(content)
|
59
58
|
return if content.match?(/\A\s*\Z/)
|
60
59
|
|
61
|
-
text =
|
62
|
-
|
60
|
+
text = ::Oga::HTML::Entities.decode(context.before_content)
|
61
|
+
text += content.gsub(/\A\s*\n\s*|\s*\n\s*\Z/, '').delete("\n").squeeze(' ')
|
62
|
+
buffer << context.text_node_styles.merge(text: text)
|
63
63
|
context.last_text_node = true
|
64
64
|
nil
|
65
65
|
end
|
@@ -68,7 +68,7 @@ module PrawnHtml
|
|
68
68
|
def render
|
69
69
|
return if buffer.empty?
|
70
70
|
|
71
|
-
options = context.
|
71
|
+
options = context.block_styles.slice(:align, :leading, :margin_left, :mode, :padding_left)
|
72
72
|
output_content(buffer.dup, options)
|
73
73
|
buffer.clear
|
74
74
|
context.last_margin = 0
|
@@ -78,18 +78,11 @@ module PrawnHtml
|
|
78
78
|
|
79
79
|
private
|
80
80
|
|
81
|
-
attr_reader :buffer, :context, :
|
82
|
-
|
83
|
-
def tag_classes
|
84
|
-
@tag_classes ||= TAG_CLASSES.each_with_object({}) do |klass, res|
|
85
|
-
res.merge!(klass.elements)
|
86
|
-
end
|
87
|
-
end
|
81
|
+
attr_reader :buffer, :context, :document_styles, :pdf
|
88
82
|
|
89
83
|
def setup_element(element)
|
90
84
|
add_space_if_needed unless render_if_needed(element)
|
91
|
-
|
92
|
-
element.apply_doc_styles(doc_styles)
|
85
|
+
apply_tag_open_styles(element)
|
93
86
|
context.push(element)
|
94
87
|
element.custom_render(pdf, context) if element.respond_to?(:custom_render)
|
95
88
|
end
|
@@ -106,29 +99,24 @@ module PrawnHtml
|
|
106
99
|
true
|
107
100
|
end
|
108
101
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
pdf.move_down(
|
114
|
-
pdf.move_down(styles[:padding_bottom].round(4)) if styles[:padding_bottom].to_f > 0
|
102
|
+
def apply_tag_close_styles(element)
|
103
|
+
tag_styles = element.tag_close_styles
|
104
|
+
context.last_margin = tag_styles[:margin_bottom].to_f
|
105
|
+
move_down = context.last_margin + tag_styles[:padding_bottom].to_f
|
106
|
+
pdf.move_down(move_down) if move_down > 0
|
115
107
|
end
|
116
108
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
margin = (element.pre_styles[:margin_top] - context.last_margin).round(4)
|
122
|
-
pdf.move_down(margin) if margin > 0
|
109
|
+
def apply_tag_open_styles(element)
|
110
|
+
tag_styles = element.tag_open_styles
|
111
|
+
move_down = (tag_styles[:margin_top].to_f - context.last_margin) + tag_styles[:padding_top].to_f
|
112
|
+
pdf.move_down(move_down) if move_down > 0
|
123
113
|
end
|
124
114
|
|
125
115
|
def output_content(buffer, options)
|
126
116
|
buffer.each { |item| item[:callback] = item[:callback].new(pdf, item) if item[:callback] }
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
pdf.formatted_text(buffer, options)
|
131
|
-
end
|
117
|
+
left_indent = options.delete(:margin_left).to_f + options.delete(:padding_left).to_f
|
118
|
+
options[:indent_paragraphs] = left_indent if left_indent > 0
|
119
|
+
pdf.formatted_text(buffer, options)
|
132
120
|
end
|
133
121
|
end
|
134
122
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrawnHtml
|
4
|
+
class Tag
|
5
|
+
TAG_CLASSES = %w[A B Body Br Del Div H Hr I Img Li Mark P Small Span U Ul].freeze
|
6
|
+
|
7
|
+
attr_reader :attrs, :tag
|
8
|
+
|
9
|
+
# Init the Tag
|
10
|
+
#
|
11
|
+
# @param tag [Symbol] tag name
|
12
|
+
# @param attributes [Hash] hash of element attributes
|
13
|
+
# @param document_styles [Hash] hash of document styles
|
14
|
+
def initialize(tag, attributes = {}, document_styles = {})
|
15
|
+
@tag = tag
|
16
|
+
element_styles = attributes.delete(:style)
|
17
|
+
@attrs = Attributes.new(attributes)
|
18
|
+
process_styles(document_styles, element_styles)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Is a block tag?
|
22
|
+
#
|
23
|
+
# @return [Boolean] true if the type of the tag is block, false otherwise
|
24
|
+
def block?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Styles to apply to the block
|
29
|
+
#
|
30
|
+
# @return [Hash] hash of styles to apply
|
31
|
+
def block_styles
|
32
|
+
block_styles = styles.slice(*Attributes::STYLES_APPLY[:block])
|
33
|
+
block_styles[:mode] = attrs.data['mode'].to_sym if attrs.data.include?('mode')
|
34
|
+
block_styles
|
35
|
+
end
|
36
|
+
|
37
|
+
# Styles to apply on tag closing
|
38
|
+
#
|
39
|
+
# @return [Hash] hash of styles to apply
|
40
|
+
def tag_close_styles
|
41
|
+
styles.slice(*Attributes::STYLES_APPLY[:tag_close])
|
42
|
+
end
|
43
|
+
|
44
|
+
# Styles hash
|
45
|
+
#
|
46
|
+
# @return [Hash] hash of styles
|
47
|
+
def styles
|
48
|
+
attrs.styles
|
49
|
+
end
|
50
|
+
|
51
|
+
# Styles to apply on tag opening
|
52
|
+
#
|
53
|
+
# @return [Hash] hash of styles to apply
|
54
|
+
def tag_open_styles
|
55
|
+
styles.slice(*Attributes::STYLES_APPLY[:tag_open])
|
56
|
+
end
|
57
|
+
|
58
|
+
class << self
|
59
|
+
# Evaluate the Tag class from a tag name
|
60
|
+
#
|
61
|
+
# @params tag_name [Symbol] the tag name
|
62
|
+
#
|
63
|
+
# @return [Tag] the class for the tag if available or nil
|
64
|
+
def class_for(tag_name)
|
65
|
+
@tag_classes ||= TAG_CLASSES.each_with_object({}) do |tag_class, res|
|
66
|
+
klass = const_get("PrawnHtml::Tags::#{tag_class}")
|
67
|
+
k = [klass] * klass::ELEMENTS.size
|
68
|
+
res.merge!(klass::ELEMENTS.zip(k).to_h)
|
69
|
+
end
|
70
|
+
@tag_classes[tag_name]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def evaluate_document_styles(document_styles)
|
77
|
+
selectors = [
|
78
|
+
tag.to_s,
|
79
|
+
attrs['class'] ? ".#{attrs['class']}" : nil,
|
80
|
+
attrs['id'] ? "##{attrs['id']}" : nil
|
81
|
+
].compact!
|
82
|
+
document_styles.each_with_object({}) do |(sel, attributes), res|
|
83
|
+
res.merge!(attributes) if selectors.include?(sel)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def process_styles(document_styles, element_styles)
|
88
|
+
attrs.merge_styles!(attrs.process_styles(tag_styles)) if respond_to?(:tag_styles)
|
89
|
+
doc_styles = evaluate_document_styles(document_styles)
|
90
|
+
attrs.merge_styles!(doc_styles)
|
91
|
+
el_styles = Attributes.parse_styles(element_styles)
|
92
|
+
attrs.merge_styles!(attrs.process_styles(el_styles)) if el_styles
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/prawn_html/tags/a.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class A <
|
5
|
+
class A < Tag
|
8
6
|
ELEMENTS = [:a].freeze
|
9
7
|
|
10
|
-
def
|
11
|
-
attrs.
|
8
|
+
def tag_styles
|
9
|
+
attrs.href ? { 'href' => attrs.href } : {}
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
data/lib/prawn_html/tags/b.rb
CHANGED
data/lib/prawn_html/tags/body.rb
CHANGED
data/lib/prawn_html/tags/br.rb
CHANGED
data/lib/prawn_html/tags/del.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class Del <
|
5
|
+
class Del < Tag
|
8
6
|
ELEMENTS = [:del, :s].freeze
|
9
7
|
|
10
|
-
def
|
11
|
-
|
12
|
-
callback
|
13
|
-
|
8
|
+
def tag_styles
|
9
|
+
{
|
10
|
+
'callback' => Callbacks::StrikeThrough
|
11
|
+
}
|
14
12
|
end
|
15
13
|
end
|
16
14
|
end
|
data/lib/prawn_html/tags/div.rb
CHANGED
data/lib/prawn_html/tags/h.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class H <
|
5
|
+
class H < Tag
|
8
6
|
ELEMENTS = [:h1, :h2, :h3, :h4, :h5, :h6].freeze
|
9
7
|
|
10
8
|
MARGINS_TOP = {
|
@@ -38,8 +36,8 @@ module PrawnHtml
|
|
38
36
|
true
|
39
37
|
end
|
40
38
|
|
41
|
-
def
|
42
|
-
@
|
39
|
+
def tag_styles
|
40
|
+
@tag_styles ||= {
|
43
41
|
'font-size' => SIZES[tag].to_s,
|
44
42
|
'font-weight' => 'bold',
|
45
43
|
'margin-bottom' => MARGINS_BOTTOM[tag].to_s,
|
data/lib/prawn_html/tags/hr.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class Hr <
|
5
|
+
class Hr < Tag
|
8
6
|
ELEMENTS = [:hr].freeze
|
9
7
|
|
10
8
|
MARGIN_BOTTOM = 12
|
@@ -15,15 +13,29 @@ module PrawnHtml
|
|
15
13
|
end
|
16
14
|
|
17
15
|
def custom_render(pdf, _context)
|
16
|
+
dash = parse_dash_value(attrs.data['dash']) if attrs.data.include?('dash')
|
17
|
+
pdf.dash(dash) if dash
|
18
18
|
pdf.stroke_horizontal_rule
|
19
|
+
pdf.undash if dash
|
19
20
|
end
|
20
21
|
|
21
|
-
def
|
22
|
-
@
|
22
|
+
def tag_styles
|
23
|
+
@tag_styles ||= {
|
23
24
|
'margin-bottom' => MARGIN_BOTTOM.to_s,
|
24
25
|
'margin-top' => MARGIN_TOP.to_s,
|
25
26
|
}
|
26
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse_dash_value(dash_string)
|
32
|
+
if dash_string.match? /\A\d+\Z/
|
33
|
+
dash_string.to_i
|
34
|
+
else
|
35
|
+
dash_array = dash_string.split(',')
|
36
|
+
dash_array.map(&:to_i) if dash_array.any?
|
37
|
+
end
|
38
|
+
end
|
27
39
|
end
|
28
40
|
end
|
29
41
|
end
|
data/lib/prawn_html/tags/i.rb
CHANGED
data/lib/prawn_html/tags/img.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class Img <
|
5
|
+
class Img < Tag
|
8
6
|
ELEMENTS = [:img].freeze
|
9
7
|
|
10
8
|
def block?
|
@@ -12,20 +10,20 @@ module PrawnHtml
|
|
12
10
|
end
|
13
11
|
|
14
12
|
def custom_render(pdf, context)
|
15
|
-
styles = Attributes.parse_styles(attrs.
|
16
|
-
|
17
|
-
|
18
|
-
pdf.image(@attrs.
|
13
|
+
styles = Attributes.parse_styles(attrs.style)
|
14
|
+
block_styles = context.block_styles
|
15
|
+
evaluated_styles = evaluate_styles(pdf, block_styles.merge(styles))
|
16
|
+
pdf.image(@attrs.src, evaluated_styles) if @attrs.src
|
19
17
|
end
|
20
18
|
|
21
19
|
private
|
22
20
|
|
23
21
|
def evaluate_styles(pdf, styles)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
{}.tap do |result|
|
23
|
+
result[:width] = Attributes.convert_size(styles['width'], pdf.bounds.width) if styles.include?('width')
|
24
|
+
result[:height] = Attributes.convert_size(styles['height'], pdf.bounds.height) if styles.include?('height')
|
25
|
+
result[:position] = styles[:align] if %i[left center right].include?(styles[:align])
|
26
|
+
end
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
data/lib/prawn_html/tags/li.rb
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class Li <
|
5
|
+
class Li < Tag
|
8
6
|
ELEMENTS = [:li].freeze
|
9
7
|
|
10
8
|
def block?
|
11
9
|
true
|
12
10
|
end
|
13
11
|
|
14
|
-
def
|
15
|
-
|
12
|
+
def tag_styles
|
13
|
+
{
|
16
14
|
before_content: '• '
|
17
|
-
|
15
|
+
}
|
18
16
|
end
|
19
17
|
end
|
20
18
|
end
|
data/lib/prawn_html/tags/mark.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class Mark <
|
5
|
+
class Mark < Tag
|
8
6
|
ELEMENTS = [:mark].freeze
|
9
7
|
|
10
|
-
def
|
11
|
-
|
12
|
-
callback
|
13
|
-
|
8
|
+
def tag_styles
|
9
|
+
{
|
10
|
+
'callback' => Callbacks::Highlight
|
11
|
+
}
|
14
12
|
end
|
15
13
|
end
|
16
14
|
end
|
data/lib/prawn_html/tags/p.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class P <
|
5
|
+
class P < Tag
|
8
6
|
ELEMENTS = [:p].freeze
|
9
7
|
|
10
8
|
MARGIN_BOTTOM = 6
|
@@ -14,8 +12,8 @@ module PrawnHtml
|
|
14
12
|
true
|
15
13
|
end
|
16
14
|
|
17
|
-
def
|
18
|
-
@
|
15
|
+
def tag_styles
|
16
|
+
@tag_styles ||= {
|
19
17
|
'margin-bottom' => MARGIN_BOTTOM.to_s,
|
20
18
|
'margin-top' => MARGIN_TOP.to_s
|
21
19
|
}
|
data/lib/prawn_html/tags/span.rb
CHANGED
data/lib/prawn_html/tags/u.rb
CHANGED
data/lib/prawn_html/tags/ul.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
3
|
module PrawnHtml
|
6
4
|
module Tags
|
7
|
-
class Ul <
|
5
|
+
class Ul < Tag
|
8
6
|
ELEMENTS = [:ul].freeze
|
9
7
|
|
10
8
|
MARGIN_LEFT = 25
|
@@ -13,8 +11,8 @@ module PrawnHtml
|
|
13
11
|
true
|
14
12
|
end
|
15
13
|
|
16
|
-
def
|
17
|
-
@
|
14
|
+
def tag_styles
|
15
|
+
@tag_styles ||= {
|
18
16
|
'margin-left' => MARGIN_LEFT.to_s,
|
19
17
|
}
|
20
18
|
end
|
data/lib/prawn_html/version.rb
CHANGED
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.
|
4
|
+
version: 0.2.0
|
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-08-
|
11
|
+
date: 2021-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oga
|
@@ -53,9 +53,9 @@ files:
|
|
53
53
|
- lib/prawn_html/context.rb
|
54
54
|
- lib/prawn_html/document_renderer.rb
|
55
55
|
- lib/prawn_html/html_handler.rb
|
56
|
+
- lib/prawn_html/tag.rb
|
56
57
|
- lib/prawn_html/tags/a.rb
|
57
58
|
- lib/prawn_html/tags/b.rb
|
58
|
-
- lib/prawn_html/tags/base.rb
|
59
59
|
- lib/prawn_html/tags/body.rb
|
60
60
|
- lib/prawn_html/tags/br.rb
|
61
61
|
- lib/prawn_html/tags/del.rb
|
data/lib/prawn_html/tags/base.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PrawnHtml
|
4
|
-
module Tags
|
5
|
-
class Base
|
6
|
-
attr_reader :attrs, :styles, :tag
|
7
|
-
|
8
|
-
def initialize(tag, attributes = {})
|
9
|
-
@attrs = Attributes.new(attributes)
|
10
|
-
@styles = attrs.styles
|
11
|
-
@tag = tag
|
12
|
-
attrs.process_styles(extra_attrs) unless extra_attrs.empty?
|
13
|
-
end
|
14
|
-
|
15
|
-
def apply_doc_styles(document_styles)
|
16
|
-
selectors = [
|
17
|
-
tag.to_s,
|
18
|
-
attrs.hash['class'] ? ".#{attrs.hash['class']}" : nil,
|
19
|
-
attrs.hash['id'] ? "##{attrs.hash['id']}" : nil
|
20
|
-
].compact!
|
21
|
-
merged_styles = document_styles.each_with_object({}) do |(sel, attributes), res|
|
22
|
-
res.merge!(attributes) if selectors.include?(sel)
|
23
|
-
end
|
24
|
-
@styles = merged_styles.merge(styles)
|
25
|
-
end
|
26
|
-
|
27
|
-
def block?
|
28
|
-
false
|
29
|
-
end
|
30
|
-
|
31
|
-
def extra_attrs
|
32
|
-
{}
|
33
|
-
end
|
34
|
-
|
35
|
-
def options
|
36
|
-
attrs.options
|
37
|
-
end
|
38
|
-
|
39
|
-
def post_styles
|
40
|
-
attrs.post_styles
|
41
|
-
end
|
42
|
-
|
43
|
-
def pre_styles
|
44
|
-
attrs.pre_styles
|
45
|
-
end
|
46
|
-
|
47
|
-
class << self
|
48
|
-
def elements
|
49
|
-
self::ELEMENTS.each_with_object({}) { |el, list| list[el] = self }
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|