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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5a671ae12f6a66358c10f739404ab709f3fdcaacb059b9943433a7535a539a5
4
- data.tar.gz: 274afa7a2d6cbb2a75e1ca03e2b82ee38985bf279f41ecd3878166ce9eabf15a
3
+ metadata.gz: 998ac3f3429812cc60dfe2bdf0184a86c116b1b80c7522376f99e044f185207c
4
+ data.tar.gz: 1a4ce4f9055fa8dd8ed121665102698b6077d69986974c366098d69759555d80
5
5
  SHA512:
6
- metadata.gz: a67e743e2c77c1b0e9caecefd9b6f1fc2d33243d0b86b37548f5bbed7e8e569ea11562d849c82fbdfd8ca7eeb5bdf2921cf789464868f0e90de4b631dcf89a10
7
- data.tar.gz: dd06b9b0410cceb28eaf68ab6eaeb6007086047478f3ec1ba2ffd37a0a38de38264f56fae3232b4025a5d6f45b776fe2d79bb23eb76926a8a87139e82294e14d
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
- > Still in beta. [prawn-styled-text](https://github.com/blocknotes/prawn-styled-text) rewritten from scratch
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', git: 'https://github.com/blocknotes/prawn-html.git'` (and execute `bundle`)
16
- - Use the class `HtmlHandler` on a `Prawn::Document` instance
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::HtmlHandler.new(pdf).process('<h1 style="text-align: center">Just a test</h1>')
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, only 3 or 6 hex digits format, ex. `style="background: #FECD08"`
55
- - **color**: only 3 or 6 hex digits format - ex. `style="color: #FB1"`
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/tags/base'
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/html_handler'
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 :hash, :options, :post_styles, :pre_styles, :styles
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, dest: :styles },
12
- 'color' => { key: :color, set: :convert_color, dest: :styles },
13
- 'font-family' => { key: :font, set: :unquote, dest: :styles },
14
- 'font-size' => { key: :size, set: :convert_size, dest: :styles },
15
- 'font-style' => { key: :styles, set: :append_symbol, dest: :styles },
16
- 'font-weight' => { key: :styles, set: :append_symbol, dest: :styles },
17
- 'letter-spacing' => { key: :character_spacing, set: :convert_float, dest: :styles },
18
- # pre styles
19
- 'margin-top' => { key: :margin_top, set: :convert_size, dest: :pre_styles },
20
- # post styles
21
- 'margin-bottom' => { key: :margin_bottom, set: :convert_size, dest: :post_styles },
22
- 'padding-bottom' => { key: :padding_bottom, set: :convert_size, dest: :post_styles },
23
- # options
24
- 'line-height' => { key: :leading, set: :convert_size, dest: :options },
25
- 'margin-left' => { key: :margin_left, set: :convert_size, dest: :options },
26
- 'padding-left' => { key: :padding_left, set: :convert_size, dest: :options },
27
- 'padding-top' => { key: :padding_top, set: :convert_size, dest: :options },
28
- 'text-align' => { key: :align, set: :convert_symbol, dest: :options },
29
- 'text-decoration' => { key: :styles, set: :append_symbol, dest: :styles }
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
- # @param attributes [Hash] hash of attributes to parse
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
- parsed_styles = Attributes.parse_styles(hash.style)
44
- process_styles(parsed_styles)
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 styles attributes
59
+ # Processes the data attributes
48
60
  #
49
- # @param attributes [Hash] hash of styles attributes
50
- def process_styles(styles)
51
- styles.each do |key, value|
52
- rule = STYLES_LIST[key]
53
- next unless rule
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
- class << self
60
- # Converts a color string
61
- #
62
- # @param value [String] HTML string color
63
- #
64
- # @return [String] adjusted string color
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
- # Converts a string to symbol
103
- #
104
- # @param value [String] string
105
- #
106
- # @return [Symbol] symbol
107
- def convert_symbol(value)
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 hash [Hash] target attributes hash
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!(hash, key, value)
94
+ def merge_attr!(attributes, key, value)
119
95
  return unless key
120
- return (hash[key] = value) unless Attributes::STYLES_MERGE.include?(key)
96
+ return (attributes[key] = value) unless Attributes::STYLES_MERGE.include?(key)
121
97
 
122
- hash[key] ||= 0
123
- hash[key] += value
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
- (send(rule[:dest])[rule[:key]] ||= []) << Attributes.convert_symbol(value)
118
+ (result[rule[:key]] ||= []) << Utils.convert_symbol(value)
152
119
  else
153
- send(rule[:dest])[rule[:key]] = Attributes.send(rule[:set], value)
120
+ result[rule[:key]] = Utils.send(rule[:set], value)
154
121
  end
155
122
  end
156
123
  end