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
         |