prawn-html 0.1.4 → 0.4.0

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: 2a2fb462a91991cadb770f8bbee7d07c99f670d46eeb62d2a3b467b1839e5364
4
- data.tar.gz: 1710135c8ca94e2de836e913ce8e177b9bc47b4ee37725a23729912b086aff25
3
+ metadata.gz: 4dec0bd9a3746705bdaca34d49c8a8eff5700fd8a5feebc3b3452001cd239b0b
4
+ data.tar.gz: 74b5e31d3056560435ea939f50f713f11c44ad4c1f584873203b910754e1f782
5
5
  SHA512:
6
- metadata.gz: ceec4ef90b155ea23f659771ab4e6275f12c9c5d4dd29a5da11b4d7f23512320ec71246623ff3c0eb82e0bed9a5e837292c40ac5a8d82585eaa4cb64cdc0653b
7
- data.tar.gz: efea6fe48069d63b56c8c2e7a0142ed959af8cb200e8bc6ff03f8b252614379130feed7bd54942d59a97689c43f3b8a407e739db2b3c9fd1087f10c75fe0ca35
6
+ metadata.gz: 814c481a5fce1da43dcb04254ed97f76f234fbdcaa6826070b00594f80cd3b4396c190a2972c0f6f72e00d35e5fd1b534cf22c9ef932b8ee308562de7c6bc854
7
+ data.tar.gz: f99ce1543d1b28689b19ceddd8e86a73fae8e204bc98270e6a422dd93e44d89ff50366969b67e0fb2a9a4e91d03367981ab5c0f9481b91378df57e2304358f40
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Prawn HTML
2
- [![gem version](https://badge.fury.io/rb/prawn-html.svg)](https://badge.fury.io/rb/prawn-html)
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,9 +8,10 @@ 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
- - no extra settings: it just parses an input HTML and output to a Prawn PDF document.
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
- **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*.
14
15
 
15
16
  > [prawn-styled-text](https://github.com/blocknotes/prawn-styled-text) rewritten from scratch, finally!
16
17
 
@@ -38,6 +39,7 @@ HTML tags:
38
39
 
39
40
  - **a**: link
40
41
  - **b**: bold
42
+ - **blockquote**: block quotation element
41
43
  - **br**: new line
42
44
  - **del**: strike-through
43
45
  - **div**: block element
@@ -49,38 +51,57 @@ HTML tags:
49
51
  - **img**: image
50
52
  - **li**: list item
51
53
  - **mark**: highlight
54
+ - **ol**: ordered list
52
55
  - **p**: block element
53
56
  - **s**: strike-through
54
57
  - **small**: smaller text
55
58
  - **span**: inline element
56
59
  - **strong**: bold
57
60
  - **u**: underline
58
- - **ul**: list
61
+ - **ul**: unordered list
59
62
 
60
63
  CSS attributes (dimensional units are ignored and considered in pixel):
61
64
 
62
- - **background**: for *mark* tag, only 3 or 6 hex digits format, ex. `style="background: #FECD08"`
63
- - **color**: only 3 or 6 hex digits format - ex. `style="color: #FB1"`
65
+ - **background**: for *mark* tag (3/6 hex digits or RGB or color name), ex. `style="background: #FECD08"`
66
+ - **break-after**: go to a new page after some elements, ex. `style="break-after: auto"`
67
+ - **break-before**: go to a new page before some elements, ex. `style="break-before: auto"`
68
+ - **color**: (3/6 hex digits or RGB or color name) ex. `style="color: #FB1"`
64
69
  - **font-family**: font must be registered, quotes are optional, ex. `style="font-family: Courier"`
65
70
  - **font-size**: ex. `style="font-size: 20px"`
66
71
  - **font-style**: values: *:italic*, ex. `style="font-style: italic"`
67
72
  - **font-weight**: values: *:bold*, ex. `style="font-weight: bold"`
68
73
  - **height**: for *img* tag, ex. `<img src="image.jpg" style="height: 200px"/>`
69
74
  - **href**: for *a* tag, ex. `<a href="http://www.google.com/">Google</a>`
75
+ - **left**: see *position (absolute)*
70
76
  - **letter-spacing**: ex. `style="letter-spacing: 1.5"`
71
77
  - **line-height**: ex. `style="line-height: 10px"`
78
+ - **list-style-type**: for *ul*, a string, ex. `style="list-style-type: '- '"`
72
79
  - **margin-bottom**: ex. `style="margin-bottom: 10px"`
73
80
  - **margin-left**: ex. `style="margin-left: 15px"`
74
81
  - **margin-top**: ex. `style="margin-top: 20px"`
82
+ - **position**: `absolute`, ex. `style="position: absolute; left: 20px; top: 100px"`
75
83
  - **src**: for *img* tag, ex. `<img src="image.jpg"/>`
76
84
  - **text-align**: `left` | `center` | `right` | `justify`, ex. `style="text-align: center"`
77
85
  - **text-decoration**: `underline`, ex. `style="text-decoration: underline"`
86
+ - **top**: see *position (absolute)*
78
87
  - **width**: for *img* tag, support also percentage, ex. `<img src="image.jpg" style="width: 50%; height: 200px"/>`
79
88
 
89
+ For colors, the supported formats are:
90
+ - 3 hex digits, ex. `color: #FB1`;
91
+ - 6 hex digits, ex. `color: #abcdef`;
92
+ - RGB, ex. `color: RGB(64, 0, 128)`;
93
+ - color name, ex. `color: yellow`.
94
+
95
+ ## Data attributes
96
+
97
+ Some custom data attributes are used to pass options:
98
+
99
+ - **dash**: for *hr* tag, accepts an integer or a list of integers), ex. `data-data="2, 4, 3"`
100
+ - **mode**: allow to specify the text mode (stroke|fill||fill_stroke), ex. `data-mode="stroke"`
101
+
80
102
  ## Document styles
81
103
 
82
- [Experimental feature] You can define document CSS rules inside an _head_ tag, but with a limited support for now.
83
- Only single CSS selectors and basic ones are supported. Example:
104
+ You can define document CSS rules inside an _head_ tag. Example:
84
105
 
85
106
  ```html
86
107
  <!DOCTYPE html>
@@ -105,6 +126,28 @@ Only single CSS selectors and basic ones are supported. Example:
105
126
  </html>
106
127
  ```
107
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
+
108
151
  ## Do you like it? Star it!
109
152
 
110
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,24 +1,180 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'prawn'
4
-
5
- require 'prawn_html/tags/base'
6
- Dir["#{__dir__}/prawn_html/tags/*.rb"].sort.each { |f| require f }
7
-
8
- Dir["#{__dir__}/prawn_html/callbacks/*.rb"].sort.each { |f| require f }
9
-
10
- require 'prawn_html/attributes'
11
- require 'prawn_html/context'
12
- require 'prawn_html/document_renderer'
13
- require 'prawn_html/html_handler'
14
-
15
3
  module PrawnHtml
16
4
  PX = 0.66 # conversion constant for pixel sixes
17
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
+
18
157
  def append_html(pdf, html)
19
- handler = PrawnHtml::HtmlHandler.new(pdf)
20
- handler.process(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)
21
162
  end
22
163
 
23
164
  module_function :append_html
24
165
  end
166
+
167
+ require 'prawn'
168
+
169
+ require 'prawn_html/utils'
170
+
171
+ Dir["#{__dir__}/prawn_html/callbacks/*.rb"].sort.each { |f| require f }
172
+
173
+ require 'prawn_html/tag'
174
+ Dir["#{__dir__}/prawn_html/tags/*.rb"].sort.each { |f| require f }
175
+
176
+ require 'prawn_html/attributes'
177
+ require 'prawn_html/context'
178
+ require 'prawn_html/pdf_wrapper'
179
+ require 'prawn_html/document_renderer'
180
+ require 'prawn_html/html_parser'
@@ -3,124 +3,87 @@
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_styles },
24
+ 'font-weight' => { key: :styles, set: :append_styles },
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_styles },
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)
45
53
  end
46
54
 
47
- # Processes the styles attributes
55
+ # Processes the data attributes
48
56
  #
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)
57
+ # @return [Hash] hash of data attributes with 'data-' prefix removed and stripped values
58
+ def data
59
+ to_h.each_with_object({}) do |(key, value), res|
60
+ data_key = key.match /\Adata-(.+)/
61
+ res[data_key[1]] = value.strip if data_key
56
62
  end
57
63
  end
58
64
 
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
101
-
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/)
109
- end
65
+ # Merge text styles
66
+ #
67
+ # @param text_styles [String] styles to parse and process
68
+ def merge_text_styles!(text_styles)
69
+ hash_styles = Attributes.parse_styles(text_styles)
70
+ process_styles(hash_styles) unless hash_styles.empty?
71
+ end
110
72
 
73
+ class << self
111
74
  # Merges attributes
112
75
  #
113
- # @param hash [Hash] target attributes hash
76
+ # @param attributes [Hash] target attributes hash
114
77
  # @param key [Symbol] key
115
78
  # @param value
116
79
  #
117
80
  # @return [Hash] the updated hash of attributes
118
- def merge_attr!(hash, key, value)
81
+ def merge_attr!(attributes, key, value)
119
82
  return unless key
120
- return (hash[key] = value) unless Attributes::STYLES_MERGE.include?(key)
83
+ return (attributes[key] = value) unless Attributes::STYLES_MERGE.include?(key)
121
84
 
122
- hash[key] ||= 0
123
- hash[key] += value
85
+ attributes[key] ||= 0
86
+ attributes[key] += value
124
87
  end
125
88
 
126
89
  # Parses a string of styles
@@ -131,27 +94,25 @@ module PrawnHtml
131
94
  def parse_styles(styles)
132
95
  (styles || '').scan(/\s*([^:;]+)\s*:\s*([^;]+)\s*/).to_h
133
96
  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
97
  end
146
98
 
147
99
  private
148
100
 
149
- def apply_rule(rule, value)
150
- if rule[:set] == :append_symbol
151
- (send(rule[:dest])[rule[:key]] ||= []) << Attributes.convert_symbol(value)
101
+ def apply_rule!(result, rule, value)
102
+ return unless rule
103
+
104
+ if rule[:set] == :append_styles
105
+ (result[rule[:key]] ||= []) << Utils.normalize_style(value)
152
106
  else
153
- send(rule[:dest])[rule[:key]] = Attributes.send(rule[:set], value)
107
+ result[rule[:key]] = Utils.send(rule[:set], value)
108
+ end
109
+ end
110
+
111
+ def process_styles(hash_styles)
112
+ hash_styles.each do |key, value|
113
+ apply_rule!(@styles, STYLES_LIST[key], value)
154
114
  end
115
+ @styles
155
116
  end
156
117
  end
157
118
  end