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