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 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