prawn-html 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![gem version](https://badge.fury.io/rb/prawn-html.svg)](https://
|
2
|
+
[![gem version](https://badge.fury.io/rb/prawn-html.svg)](https://rubygems.org/gems/prawn-html)
|
3
3
|
[![linters](https://github.com/blocknotes/prawn-html/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/prawn-html/actions/workflows/linters.yml)
|
4
4
|
[![specs](https://github.com/blocknotes/prawn-html/actions/workflows/specs.yml/badge.svg)](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
|