prawn-html 0.1.0 → 0.3.2
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 +87 -8
- data/lib/prawn-html.rb +169 -7
- data/lib/prawn_html/attributes.rb +74 -107
- data/lib/prawn_html/callbacks/highlight.rb +2 -4
- data/lib/prawn_html/callbacks/strike_through.rb +4 -4
- data/lib/prawn_html/context.rb +30 -11
- data/lib/prawn_html/document_renderer.rb +45 -46
- data/lib/prawn_html/{html_handler.rb → html_parser.rb} +9 -7
- data/lib/prawn_html/pdf_wrapper.rb +94 -0
- data/lib/prawn_html/tag.rb +96 -0
- data/lib/prawn_html/tags/a.rb +3 -7
- 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 +3 -6
- 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 +16 -6
- data/lib/prawn_html/tags/i.rb +2 -4
- data/lib/prawn_html/tags/img.rb +11 -13
- data/lib/prawn_html/tags/li.rb +10 -7
- data/lib/prawn_html/tags/mark.rb +5 -7
- data/lib/prawn_html/tags/ol.rb +28 -0
- data/lib/prawn_html/tags/p.rb +3 -5
- data/lib/prawn_html/tags/small.rb +2 -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/utils.rb +90 -0
- data/lib/prawn_html/version.rb +1 -1
- metadata +7 -4
- 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: 998ac3f3429812cc60dfe2bdf0184a86c116b1b80c7522376f99e044f185207c
|
4
|
+
data.tar.gz: 1a4ce4f9055fa8dd8ed121665102698b6077d69986974c366098d69759555d80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d443116a4be710f698f02da8092b19b0d83f9b319c0e7b431ae9cfe67d11c208ddea48ff5c995057bd3b28056dafcb2ff8461c1ad3be8134f9dcaa926b96ef73
|
7
|
+
data.tar.gz: 530a30ff700f75312a5f72b38ef433f9f03a3eab9ac9d83297a3eacc20d680df4ab1f8a4bc3c5134146b3da71be6ffa23d151bc71824dc21022fe7c5aa592758
|
data/README.md
CHANGED
@@ -1,29 +1,38 @@
|
|
1
1
|
# Prawn HTML
|
2
|
+
[](https://rubygems.org/gems/prawn-html)
|
2
3
|
[](https://github.com/blocknotes/prawn-html/actions/workflows/linters.yml)
|
3
4
|
[](https://github.com/blocknotes/prawn-html/actions/workflows/specs.yml)
|
4
5
|
|
5
6
|
HTML to PDF renderer using [Prawn PDF](https://github.com/prawnpdf/prawn).
|
6
7
|
|
7
|
-
|
8
|
+
Features:
|
9
|
+
- support a [good set](#supported-tags--attributes) of HTML tags and CSS properties;
|
10
|
+
- handle [document styles](#document-styles);
|
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.
|
8
13
|
|
9
|
-
**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
|
+
**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* or *PDFKit*.
|
15
|
+
|
16
|
+
> [prawn-styled-text](https://github.com/blocknotes/prawn-styled-text) rewritten from scratch, finally!
|
10
17
|
|
11
18
|
Please :star: if you like it.
|
12
19
|
|
13
20
|
## Install
|
14
21
|
|
15
|
-
- Add to your Gemfile: `gem 'prawn-html'
|
16
|
-
-
|
22
|
+
- Add to your Gemfile: `gem 'prawn-html'` (and execute `bundle`)
|
23
|
+
- Just call `PrawnHtml.append_html` on a `Prawn::Document` instance (see the examples)
|
17
24
|
|
18
25
|
## Examples
|
19
26
|
|
20
27
|
```rb
|
21
28
|
require 'prawn-html'
|
22
29
|
pdf = Prawn::Document.new(page_size: 'A4')
|
23
|
-
PrawnHtml
|
30
|
+
PrawnHtml.append_html(pdf, '<h1 style="text-align: center">Just a test</h1>')
|
24
31
|
pdf.render_file('test.pdf')
|
25
32
|
```
|
26
33
|
|
34
|
+
To check some examples with the PDF output see [examples](examples/) folder.
|
35
|
+
|
27
36
|
## Supported tags & attributes
|
28
37
|
|
29
38
|
HTML tags:
|
@@ -41,34 +50,104 @@ HTML tags:
|
|
41
50
|
- **img**: image
|
42
51
|
- **li**: list item
|
43
52
|
- **mark**: highlight
|
53
|
+
- **ol**: ordered list
|
44
54
|
- **p**: block element
|
45
55
|
- **s**: strike-through
|
46
56
|
- **small**: smaller text
|
47
57
|
- **span**: inline element
|
48
58
|
- **strong**: bold
|
49
59
|
- **u**: underline
|
50
|
-
- **ul**: list
|
60
|
+
- **ul**: unordered list
|
51
61
|
|
52
62
|
CSS attributes (dimensional units are ignored and considered in pixel):
|
53
63
|
|
54
|
-
- **background**: for *mark* tag
|
55
|
-
- **
|
64
|
+
- **background**: for *mark* tag (3/6 hex digits or RGB or color name), ex. `style="background: #FECD08"`
|
65
|
+
- **break-after**: go to a new page after some elements, ex. `style="break-after: auto"`
|
66
|
+
- **break-before**: go to a new page before some elements, ex. `style="break-before: auto"`
|
67
|
+
- **color**: (3/6 hex digits or RGB or color name) ex. `style="color: #FB1"`
|
56
68
|
- **font-family**: font must be registered, quotes are optional, ex. `style="font-family: Courier"`
|
57
69
|
- **font-size**: ex. `style="font-size: 20px"`
|
58
70
|
- **font-style**: values: *:italic*, ex. `style="font-style: italic"`
|
59
71
|
- **font-weight**: values: *:bold*, ex. `style="font-weight: bold"`
|
60
72
|
- **height**: for *img* tag, ex. `<img src="image.jpg" style="height: 200px"/>`
|
61
73
|
- **href**: for *a* tag, ex. `<a href="http://www.google.com/">Google</a>`
|
74
|
+
- **left**: see *position (absolute)*
|
62
75
|
- **letter-spacing**: ex. `style="letter-spacing: 1.5"`
|
63
76
|
- **line-height**: ex. `style="line-height: 10px"`
|
77
|
+
- **list-style-type**: for *ul*, a string, ex. `style="list-style-type: '- '"`
|
64
78
|
- **margin-bottom**: ex. `style="margin-bottom: 10px"`
|
65
79
|
- **margin-left**: ex. `style="margin-left: 15px"`
|
66
80
|
- **margin-top**: ex. `style="margin-top: 20px"`
|
81
|
+
- **position**: `absolute`, ex. `style="position: absolute; left: 20px; top: 100px"`
|
67
82
|
- **src**: for *img* tag, ex. `<img src="image.jpg"/>`
|
68
83
|
- **text-align**: `left` | `center` | `right` | `justify`, ex. `style="text-align: center"`
|
69
84
|
- **text-decoration**: `underline`, ex. `style="text-decoration: underline"`
|
85
|
+
- **top**: see *position (absolute)*
|
70
86
|
- **width**: for *img* tag, support also percentage, ex. `<img src="image.jpg" style="width: 50%; height: 200px"/>`
|
71
87
|
|
88
|
+
For colors, the supported formats are:
|
89
|
+
- 3 hex digits, ex. `color: #FB1`;
|
90
|
+
- 6 hex digits, ex. `color: #abcdef`;
|
91
|
+
- RGB, ex. `color: RGB(64, 0, 128)`;
|
92
|
+
- color name, ex. `color: yellow`.
|
93
|
+
|
94
|
+
## Data attributes
|
95
|
+
|
96
|
+
Some custom data attributes are used to pass options:
|
97
|
+
|
98
|
+
- **dash**: for *hr* tag, accepts an integer or a list of integers), ex. `data-data="2, 4, 3"`
|
99
|
+
- **mode**: allow to specify the text mode (stroke|fill||fill_stroke), ex. `data-mode="stroke"`
|
100
|
+
|
101
|
+
## Document styles
|
102
|
+
|
103
|
+
[Experimental feature] You can define document CSS rules inside an _head_ tag, but with a limited support for now.
|
104
|
+
Only single CSS selectors and basic ones are supported. Example:
|
105
|
+
|
106
|
+
```html
|
107
|
+
<!DOCTYPE html>
|
108
|
+
<html>
|
109
|
+
<head>
|
110
|
+
<title>A test</title>
|
111
|
+
<style>
|
112
|
+
body { color: #abbccc }
|
113
|
+
.green {
|
114
|
+
color: #0f0;
|
115
|
+
font-family: Courier;
|
116
|
+
}
|
117
|
+
#test-1 { font-weight: bold }
|
118
|
+
</style>
|
119
|
+
</head>
|
120
|
+
<body>
|
121
|
+
<div class="green">
|
122
|
+
Div content
|
123
|
+
<span id="test-1">Span content</span>
|
124
|
+
</div>
|
125
|
+
</body>
|
126
|
+
</html>
|
127
|
+
```
|
128
|
+
|
129
|
+
## Additional notes
|
130
|
+
|
131
|
+
### Rails: generate PDF on the fly
|
132
|
+
|
133
|
+
Sample controller's action to create a PDF from Rails:
|
134
|
+
|
135
|
+
```rb
|
136
|
+
class SomeController < ApplicationController
|
137
|
+
def sample_action
|
138
|
+
respond_to do |format|
|
139
|
+
format.pdf do
|
140
|
+
pdf = Prawn::Document.new
|
141
|
+
PrawnHtml.append_html(pdf, '<h1 style="text-align: center">Just a test</h1>')
|
142
|
+
send_data(pdf.render, filename: 'sample.pdf', type: 'application/pdf')
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
More details in this blogpost: [generate PDF from HTML](https://www.blocknot.es/2021-08-20-rails-generate-pdf-from-html/)
|
150
|
+
|
72
151
|
## Do you like it? Star it!
|
73
152
|
|
74
153
|
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
data/lib/prawn-html.rb
CHANGED
@@ -1,17 +1,179 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module PrawnHtml
|
4
|
+
PX = 0.66 # conversion constant for pixel sixes
|
5
|
+
|
6
|
+
COLORS = {
|
7
|
+
'aliceblue' => 'f0f8ff',
|
8
|
+
'antiquewhite' => 'faebd7',
|
9
|
+
'aqua' => '00ffff',
|
10
|
+
'aquamarine' => '7fffd4',
|
11
|
+
'azure' => 'f0ffff',
|
12
|
+
'beige' => 'f5f5dc',
|
13
|
+
'bisque' => 'ffe4c4',
|
14
|
+
'black' => '000000',
|
15
|
+
'blanchedalmond' => 'ffebcd',
|
16
|
+
'blue' => '0000ff',
|
17
|
+
'blueviolet' => '8a2be2',
|
18
|
+
'brown' => 'a52a2a',
|
19
|
+
'burlywood' => 'deb887',
|
20
|
+
'cadetblue' => '5f9ea0',
|
21
|
+
'chartreuse' => '7fff00',
|
22
|
+
'chocolate' => 'd2691e',
|
23
|
+
'coral' => 'ff7f50',
|
24
|
+
'cornflowerblue' => '6495ed',
|
25
|
+
'cornsilk' => 'fff8dc',
|
26
|
+
'crimson' => 'dc143c',
|
27
|
+
'cyan' => '00ffff',
|
28
|
+
'darkblue' => '00008b',
|
29
|
+
'darkcyan' => '008b8b',
|
30
|
+
'darkgoldenrod' => 'b8860b',
|
31
|
+
'darkgray' => 'a9a9a9',
|
32
|
+
'darkgreen' => '006400',
|
33
|
+
'darkgrey' => 'a9a9a9',
|
34
|
+
'darkkhaki' => 'bdb76b',
|
35
|
+
'darkmagenta' => '8b008b',
|
36
|
+
'darkolivegreen' => '556b2f',
|
37
|
+
'darkorange' => 'ff8c00',
|
38
|
+
'darkorchid' => '9932cc',
|
39
|
+
'darkred' => '8b0000',
|
40
|
+
'darksalmon' => 'e9967a',
|
41
|
+
'darkseagreen' => '8fbc8f',
|
42
|
+
'darkslateblue' => '483d8b',
|
43
|
+
'darkslategray' => '2f4f4f',
|
44
|
+
'darkslategrey' => '2f4f4f',
|
45
|
+
'darkturquoise' => '00ced1',
|
46
|
+
'darkviolet' => '9400d3',
|
47
|
+
'deeppink' => 'ff1493',
|
48
|
+
'deepskyblue' => '00bfff',
|
49
|
+
'dimgray' => '696969',
|
50
|
+
'dimgrey' => '696969',
|
51
|
+
'dodgerblue' => '1e90ff',
|
52
|
+
'firebrick' => 'b22222',
|
53
|
+
'floralwhite' => 'fffaf0',
|
54
|
+
'forestgreen' => '228b22',
|
55
|
+
'fuchsia' => 'ff00ff',
|
56
|
+
'gainsboro' => 'dcdcdc',
|
57
|
+
'ghostwhite' => 'f8f8ff',
|
58
|
+
'gold' => 'ffd700',
|
59
|
+
'goldenrod' => 'daa520',
|
60
|
+
'gray' => '808080',
|
61
|
+
'green' => '008000',
|
62
|
+
'greenyellow' => 'adff2f',
|
63
|
+
'grey' => '808080',
|
64
|
+
'honeydew' => 'f0fff0',
|
65
|
+
'hotpink' => 'ff69b4',
|
66
|
+
'indianred' => 'cd5c5c',
|
67
|
+
'indigo' => '4b0082',
|
68
|
+
'ivory' => 'fffff0',
|
69
|
+
'khaki' => 'f0e68c',
|
70
|
+
'lavender' => 'e6e6fa',
|
71
|
+
'lavenderblush' => 'fff0f5',
|
72
|
+
'lawngreen' => '7cfc00',
|
73
|
+
'lemonchiffon' => 'fffacd',
|
74
|
+
'lightblue' => 'add8e6',
|
75
|
+
'lightcoral' => 'f08080',
|
76
|
+
'lightcyan' => 'e0ffff',
|
77
|
+
'lightgoldenrodyellow' => 'fafad2',
|
78
|
+
'lightgray' => 'd3d3d3',
|
79
|
+
'lightgreen' => '90ee90',
|
80
|
+
'lightgrey' => 'd3d3d3',
|
81
|
+
'lightpink' => 'ffb6c1',
|
82
|
+
'lightsalmon' => 'ffa07a',
|
83
|
+
'lightseagreen' => '20b2aa',
|
84
|
+
'lightskyblue' => '87cefa',
|
85
|
+
'lightslategray' => '778899',
|
86
|
+
'lightslategrey' => '778899',
|
87
|
+
'lightsteelblue' => 'b0c4de',
|
88
|
+
'lightyellow' => 'ffffe0',
|
89
|
+
'lime' => '00ff00',
|
90
|
+
'limegreen' => '32cd32',
|
91
|
+
'linen' => 'faf0e6',
|
92
|
+
'magenta' => 'ff00ff',
|
93
|
+
'maroon' => '800000',
|
94
|
+
'mediumaquamarine' => '66cdaa',
|
95
|
+
'mediumblue' => '0000cd',
|
96
|
+
'mediumorchid' => 'ba55d3',
|
97
|
+
'mediumpurple' => '9370db',
|
98
|
+
'mediumseagreen' => '3cb371',
|
99
|
+
'mediumslateblue' => '7b68ee',
|
100
|
+
'mediumspringgreen' => '00fa9a',
|
101
|
+
'mediumturquoise' => '48d1cc',
|
102
|
+
'mediumvioletred' => 'c71585',
|
103
|
+
'midnightblue' => '191970',
|
104
|
+
'mintcream' => 'f5fffa',
|
105
|
+
'mistyrose' => 'ffe4e1',
|
106
|
+
'moccasin' => 'ffe4b5',
|
107
|
+
'navajowhite' => 'ffdead',
|
108
|
+
'navy' => '000080',
|
109
|
+
'oldlace' => 'fdf5e6',
|
110
|
+
'olive' => '808000',
|
111
|
+
'olivedrab' => '6b8e23',
|
112
|
+
'orange' => 'ffa500',
|
113
|
+
'orangered' => 'ff4500',
|
114
|
+
'orchid' => 'da70d6',
|
115
|
+
'palegoldenrod' => 'eee8aa',
|
116
|
+
'palegreen' => '98fb98',
|
117
|
+
'paleturquoise' => 'afeeee',
|
118
|
+
'palevioletred' => 'db7093',
|
119
|
+
'papayawhip' => 'ffefd5',
|
120
|
+
'peachpuff' => 'ffdab9',
|
121
|
+
'peru' => 'cd853f',
|
122
|
+
'pink' => 'ffc0cb',
|
123
|
+
'plum' => 'dda0dd',
|
124
|
+
'powderblue' => 'b0e0e6',
|
125
|
+
'purple' => '800080',
|
126
|
+
'rebeccapurple' => '663399',
|
127
|
+
'red' => 'ff0000',
|
128
|
+
'rosybrown' => 'bc8f8f',
|
129
|
+
'royalblue' => '4169e1',
|
130
|
+
'saddlebrown' => '8b4513',
|
131
|
+
'salmon' => 'fa8072',
|
132
|
+
'sandybrown' => 'f4a460',
|
133
|
+
'seagreen' => '2e8b57',
|
134
|
+
'seashell' => 'fff5ee',
|
135
|
+
'sienna' => 'a0522d',
|
136
|
+
'silver' => 'c0c0c0',
|
137
|
+
'skyblue' => '87ceeb',
|
138
|
+
'slateblue' => '6a5acd',
|
139
|
+
'slategray' => '708090',
|
140
|
+
'slategrey' => '708090',
|
141
|
+
'snow' => 'fffafa',
|
142
|
+
'springgreen' => '00ff7f',
|
143
|
+
'steelblue' => '4682b4',
|
144
|
+
'tan' => 'd2b48c',
|
145
|
+
'teal' => '008080',
|
146
|
+
'thistle' => 'd8bfd8',
|
147
|
+
'tomato' => 'ff6347',
|
148
|
+
'turquoise' => '40e0d0',
|
149
|
+
'violet' => 'ee82ee',
|
150
|
+
'wheat' => 'f5deb3',
|
151
|
+
'white' => 'ffffff',
|
152
|
+
'whitesmoke' => 'f5f5f5',
|
153
|
+
'yellow' => 'ffff00',
|
154
|
+
'yellowgreen' => '9acd32'
|
155
|
+
}.freeze
|
156
|
+
|
157
|
+
def append_html(pdf, html)
|
158
|
+
pdf_wrapper = PdfWrapper.new(pdf)
|
159
|
+
renderer = DocumentRenderer.new(pdf_wrapper)
|
160
|
+
html_parser = PrawnHtml::HtmlParser.new(renderer)
|
161
|
+
html_parser.process(html)
|
162
|
+
end
|
163
|
+
|
164
|
+
module_function :append_html
|
165
|
+
end
|
166
|
+
|
3
167
|
require 'prawn'
|
4
168
|
|
5
|
-
require 'prawn_html/
|
6
|
-
Dir["#{__dir__}/prawn_html/tags/*.rb"].sort.each { |f| require f }
|
169
|
+
require 'prawn_html/utils'
|
7
170
|
|
171
|
+
require 'prawn_html/tag'
|
172
|
+
Dir["#{__dir__}/prawn_html/tags/*.rb"].sort.each { |f| require f }
|
8
173
|
Dir["#{__dir__}/prawn_html/callbacks/*.rb"].sort.each { |f| require f }
|
9
174
|
|
10
175
|
require 'prawn_html/attributes'
|
11
176
|
require 'prawn_html/context'
|
177
|
+
require 'prawn_html/pdf_wrapper'
|
12
178
|
require 'prawn_html/document_renderer'
|
13
|
-
require 'prawn_html/
|
14
|
-
|
15
|
-
module PrawnHtml
|
16
|
-
PX = 0.66 # conversion costant for pixel sixes
|
17
|
-
end
|
179
|
+
require 'prawn_html/html_parser'
|
@@ -3,124 +3,100 @@
|
|
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 left margin_left padding_left position top],
|
11
|
+
tag_close: %i[margin_bottom padding_bottom break_after],
|
12
|
+
tag_open: %i[margin_top padding_top break_before],
|
13
|
+
text_node: %i[background callback character_spacing color font link list_style_type 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
|
-
'
|
26
|
-
|
27
|
-
'
|
28
|
-
'
|
29
|
-
'
|
17
|
+
# text node styles
|
18
|
+
'background' => { key: :background, set: :convert_color },
|
19
|
+
'callback' => { key: :callback, set: :copy_value },
|
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_value },
|
26
|
+
'letter-spacing' => { key: :character_spacing, set: :convert_float },
|
27
|
+
'list-style-type' => { key: :list_style_type, set: :unquote },
|
28
|
+
'text-decoration' => { key: :styles, set: :append_symbol },
|
29
|
+
# tag opening styles
|
30
|
+
'break-before' => { key: :break_before, set: :convert_symbol },
|
31
|
+
'margin-top' => { key: :margin_top, set: :convert_size },
|
32
|
+
'padding-top' => { key: :padding_top, set: :convert_size },
|
33
|
+
# tag closing styles
|
34
|
+
'break-after' => { key: :break_after, set: :convert_symbol },
|
35
|
+
'margin-bottom' => { key: :margin_bottom, set: :convert_size },
|
36
|
+
'padding-bottom' => { key: :padding_bottom, set: :convert_size },
|
37
|
+
# block styles
|
38
|
+
'left' => { key: :left, set: :convert_size },
|
39
|
+
'line-height' => { key: :leading, set: :convert_size },
|
40
|
+
'margin-left' => { key: :margin_left, set: :convert_size },
|
41
|
+
'padding-left' => { key: :padding_left, set: :convert_size },
|
42
|
+
'position' => { key: :position, set: :convert_symbol },
|
43
|
+
'text-align' => { key: :align, set: :convert_symbol },
|
44
|
+
'top' => { key: :top, set: :convert_size }
|
30
45
|
}.freeze
|
31
46
|
|
32
47
|
STYLES_MERGE = %i[margin_left padding_left].freeze
|
33
48
|
|
34
49
|
# Init the Attributes
|
35
|
-
|
36
|
-
|
37
|
-
def initialize(attributes)
|
38
|
-
@hash = ::OpenStruct.new(attributes)
|
39
|
-
@options = {}
|
40
|
-
@post_styles = {}
|
41
|
-
@pre_styles = {}
|
50
|
+
def initialize(attributes = {})
|
51
|
+
super
|
42
52
|
@styles = {} # result styles
|
43
|
-
|
44
|
-
|
53
|
+
return unless style
|
54
|
+
|
55
|
+
styles_hash = Attributes.parse_styles(style)
|
56
|
+
process_styles(styles_hash)
|
45
57
|
end
|
46
58
|
|
47
|
-
# Processes the
|
59
|
+
# Processes the data attributes
|
48
60
|
#
|
49
|
-
# @
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
apply_rule(rule, value)
|
61
|
+
# @return [Hash] hash of data attributes with 'data-' prefix removed and stripped values
|
62
|
+
def data
|
63
|
+
to_h.each_with_object({}) do |(key, value), res|
|
64
|
+
data_key = key.match /\Adata-(.+)/
|
65
|
+
res[data_key[1]] = value.strip if data_key
|
56
66
|
end
|
57
67
|
end
|
58
68
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def convert_color(value)
|
66
|
-
val = value&.downcase || +''
|
67
|
-
val.gsub!(/[^a-f0-9]/, '')
|
68
|
-
return val unless val.size == 3
|
69
|
-
|
70
|
-
a, b, c = val.chars
|
71
|
-
a * 2 + b * 2 + c * 2
|
72
|
-
end
|
73
|
-
|
74
|
-
# Converts a decimal number string
|
75
|
-
#
|
76
|
-
# @param value [String] string decimal
|
77
|
-
#
|
78
|
-
# @return [Float] converted and rounded float number
|
79
|
-
def convert_float(value)
|
80
|
-
val = value&.gsub(/[^0-9.]/, '') || ''
|
81
|
-
val.to_f.round(4)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Converts a size string
|
85
|
-
#
|
86
|
-
# @param value [String] size string
|
87
|
-
# @param container_size [Numeric] container size
|
88
|
-
#
|
89
|
-
# @return [Float] converted and rounded size
|
90
|
-
def convert_size(value, container_size = nil)
|
91
|
-
val = value&.gsub(/[^0-9.]/, '') || ''
|
92
|
-
val =
|
93
|
-
if container_size && value.include?('%')
|
94
|
-
val.to_f * container_size * 0.01
|
95
|
-
else
|
96
|
-
val.to_f * PX
|
97
|
-
end
|
98
|
-
# pdf.bounds.height
|
99
|
-
val.round(4)
|
100
|
-
end
|
69
|
+
# Merge already parsed styles
|
70
|
+
#
|
71
|
+
# @param parsed_styles [Hash] hash of parsed styles
|
72
|
+
def merge_styles!(parsed_styles)
|
73
|
+
@styles.merge!(parsed_styles)
|
74
|
+
end
|
101
75
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
value.to_sym if value && !value.match?(/\A\s*\Z/)
|
76
|
+
# Processes the styles attributes
|
77
|
+
#
|
78
|
+
# @param styles_hash [Hash] hash of styles attributes
|
79
|
+
def process_styles(styles_hash)
|
80
|
+
styles_hash.each do |key, value|
|
81
|
+
apply_rule!(@styles, STYLES_LIST[key], value)
|
109
82
|
end
|
83
|
+
@styles
|
84
|
+
end
|
110
85
|
|
86
|
+
class << self
|
111
87
|
# Merges attributes
|
112
88
|
#
|
113
|
-
# @param
|
89
|
+
# @param attributes [Hash] target attributes hash
|
114
90
|
# @param key [Symbol] key
|
115
91
|
# @param value
|
116
92
|
#
|
117
93
|
# @return [Hash] the updated hash of attributes
|
118
|
-
def merge_attr!(
|
94
|
+
def merge_attr!(attributes, key, value)
|
119
95
|
return unless key
|
120
|
-
return (
|
96
|
+
return (attributes[key] = value) unless Attributes::STYLES_MERGE.include?(key)
|
121
97
|
|
122
|
-
|
123
|
-
|
98
|
+
attributes[key] ||= 0
|
99
|
+
attributes[key] += value
|
124
100
|
end
|
125
101
|
|
126
102
|
# Parses a string of styles
|
@@ -131,26 +107,17 @@ module PrawnHtml
|
|
131
107
|
def parse_styles(styles)
|
132
108
|
(styles || '').scan(/\s*([^:;]+)\s*:\s*([^;]+)\s*/).to_h
|
133
109
|
end
|
134
|
-
|
135
|
-
# Unquotes a string
|
136
|
-
#
|
137
|
-
# @param value [String] string
|
138
|
-
#
|
139
|
-
# @return [String] string without quotes at the beginning/ending
|
140
|
-
def unquote(value)
|
141
|
-
(value&.strip || +'').tap do |val|
|
142
|
-
val.gsub!(/\A['"]|["']\Z/, '')
|
143
|
-
end
|
144
|
-
end
|
145
110
|
end
|
146
111
|
|
147
112
|
private
|
148
113
|
|
149
|
-
def apply_rule(rule, value)
|
114
|
+
def apply_rule!(result, rule, value)
|
115
|
+
return unless rule
|
116
|
+
|
150
117
|
if rule[:set] == :append_symbol
|
151
|
-
(
|
118
|
+
(result[rule[:key]] ||= []) << Utils.convert_symbol(value)
|
152
119
|
else
|
153
|
-
|
120
|
+
result[rule[:key]] = Utils.send(rule[:set], value)
|
154
121
|
end
|
155
122
|
end
|
156
123
|
end
|