prawn-table-html 0.0.1

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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +236 -0
  4. data/lib/prawn-html.rb +180 -0
  5. data/lib/prawn_html/attributes.rb +166 -0
  6. data/lib/prawn_html/callbacks/background.rb +19 -0
  7. data/lib/prawn_html/callbacks/strike_through.rb +18 -0
  8. data/lib/prawn_html/context.rb +100 -0
  9. data/lib/prawn_html/document_renderer.rb +172 -0
  10. data/lib/prawn_html/html_parser.rb +104 -0
  11. data/lib/prawn_html/instance.rb +18 -0
  12. data/lib/prawn_html/pdf_wrapper.rb +145 -0
  13. data/lib/prawn_html/tag.rb +93 -0
  14. data/lib/prawn_html/tags/a.rb +20 -0
  15. data/lib/prawn_html/tags/b.rb +13 -0
  16. data/lib/prawn_html/tags/blockquote.rb +25 -0
  17. data/lib/prawn_html/tags/body.rb +13 -0
  18. data/lib/prawn_html/tags/br.rb +21 -0
  19. data/lib/prawn_html/tags/code.rb +13 -0
  20. data/lib/prawn_html/tags/col.rb +37 -0
  21. data/lib/prawn_html/tags/colgroup.rb +13 -0
  22. data/lib/prawn_html/tags/del.rb +13 -0
  23. data/lib/prawn_html/tags/div.rb +13 -0
  24. data/lib/prawn_html/tags/h.rb +49 -0
  25. data/lib/prawn_html/tags/hr.rb +39 -0
  26. data/lib/prawn_html/tags/i.rb +13 -0
  27. data/lib/prawn_html/tags/img.rb +31 -0
  28. data/lib/prawn_html/tags/li.rb +39 -0
  29. data/lib/prawn_html/tags/mark.rb +13 -0
  30. data/lib/prawn_html/tags/ol.rb +43 -0
  31. data/lib/prawn_html/tags/p.rb +23 -0
  32. data/lib/prawn_html/tags/pre.rb +25 -0
  33. data/lib/prawn_html/tags/small.rb +15 -0
  34. data/lib/prawn_html/tags/span.rb +9 -0
  35. data/lib/prawn_html/tags/sub.rb +13 -0
  36. data/lib/prawn_html/tags/sup.rb +13 -0
  37. data/lib/prawn_html/tags/table.rb +53 -0
  38. data/lib/prawn_html/tags/tbody.rb +13 -0
  39. data/lib/prawn_html/tags/td.rb +43 -0
  40. data/lib/prawn_html/tags/th.rb +43 -0
  41. data/lib/prawn_html/tags/tr.rb +37 -0
  42. data/lib/prawn_html/tags/u.rb +13 -0
  43. data/lib/prawn_html/tags/ul.rb +40 -0
  44. data/lib/prawn_html/utils.rb +139 -0
  45. data/lib/prawn_html/version.rb +5 -0
  46. metadata +131 -0
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Br < Tag
6
+ ELEMENTS = [:br].freeze
7
+
8
+ BR_SPACING = Utils.convert_size('17')
9
+
10
+ def block?
11
+ true
12
+ end
13
+
14
+ def custom_render(pdf, context)
15
+ return if context.last_text_node || !context.previous_tag.is_a?(Br)
16
+
17
+ pdf.advance_cursor(BR_SPACING)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Code < Tag
6
+ ELEMENTS = [:code].freeze
7
+
8
+ def tag_styles
9
+ 'font-family: Courier'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Col < Tag
6
+ ELEMENTS = [:col].freeze
7
+
8
+ def on_context_add(_context)
9
+ table_parent = find_parent_of_type(PrawnHtml::Tags::Table)
10
+ return unless table_parent
11
+
12
+ width = parse_width
13
+ table_parent.add_col_width(width) if width
14
+ end
15
+
16
+ private
17
+
18
+ def find_parent_of_type(klass)
19
+ p = parent
20
+ p = p.parent while p && !p.is_a?(klass)
21
+ p
22
+ end
23
+
24
+ def parse_width
25
+ return attrs[:colwidth].to_f if attrs[:colwidth]
26
+
27
+ return nil if attrs[:style].include?('min-width:')
28
+
29
+ if attrs[:style] && attrs[:style] =~ /width:\s*(\d+)px/i
30
+ return Regexp.last_match(1).to_f
31
+ end
32
+
33
+ nil
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Colgroup < Tag
6
+ ELEMENTS = [:colgroup].freeze
7
+
8
+ def block?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Del < Tag
6
+ ELEMENTS = [:del, :s].freeze
7
+
8
+ def tag_styles
9
+ 'text-decoration: line-through'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Div < Tag
6
+ ELEMENTS = [:div].freeze
7
+
8
+ def block?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class H < Tag
6
+ ELEMENTS = [:h1, :h2, :h3, :h4, :h5, :h6].freeze
7
+
8
+ MARGINS_TOP = {
9
+ h1: 25,
10
+ h2: 20.5,
11
+ h3: 18,
12
+ h4: 21.2,
13
+ h5: 21.2,
14
+ h6: 22.8
15
+ }.freeze
16
+
17
+ MARGINS_BOTTOM = {
18
+ h1: 15.8,
19
+ h2: 15.8,
20
+ h3: 15.8,
21
+ h4: 20,
22
+ h5: 21.4,
23
+ h6: 24.8
24
+ }.freeze
25
+
26
+ SIZES = {
27
+ h1: 31.5,
28
+ h2: 24,
29
+ h3: 18.7,
30
+ h4: 15.7,
31
+ h5: 13,
32
+ h6: 10.8
33
+ }.freeze
34
+
35
+ def block?
36
+ true
37
+ end
38
+
39
+ def tag_styles
40
+ <<~STYLES
41
+ font-size: #{SIZES[tag]}px;
42
+ font-weight: bold;
43
+ margin-bottom: #{MARGINS_BOTTOM[tag]}px;
44
+ margin-top: #{MARGINS_TOP[tag]}px;
45
+ STYLES
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Hr < Tag
6
+ ELEMENTS = [:hr].freeze
7
+
8
+ MARGIN_BOTTOM = 12
9
+ MARGIN_TOP = 6
10
+
11
+ def block?
12
+ true
13
+ end
14
+
15
+ def custom_render(pdf, _context)
16
+ dash = attrs.data.include?('dash') ? parse_dash_value(attrs.data['dash']) : nil
17
+ pdf.horizontal_rule(color: attrs.styles[:color], dash: dash)
18
+ end
19
+
20
+ def tag_styles
21
+ <<~STYLES
22
+ margin-bottom: #{MARGIN_BOTTOM}px;
23
+ margin-top: #{MARGIN_TOP}px;
24
+ STYLES
25
+ end
26
+
27
+ private
28
+
29
+ def parse_dash_value(dash_string)
30
+ if dash_string.match? /\A\d+\Z/
31
+ dash_string.to_i
32
+ else
33
+ dash_array = dash_string.split(',')
34
+ dash_array.map(&:to_i) if dash_array.any?
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class I < Tag
6
+ ELEMENTS = [:i, :em].freeze
7
+
8
+ def tag_styles
9
+ 'font-style: italic'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Img < Tag
6
+ ELEMENTS = [:img].freeze
7
+
8
+ def block?
9
+ true
10
+ end
11
+
12
+ def custom_render(pdf, context)
13
+ parsed_styles = Attributes.parse_styles(attrs.style)
14
+ block_styles = context.block_styles
15
+ evaluated_styles = adjust_styles(pdf, block_styles.merge(parsed_styles))
16
+ pdf.image(@attrs.src, evaluated_styles)
17
+ end
18
+
19
+ private
20
+
21
+ def adjust_styles(pdf, img_styles)
22
+ {}.tap do |result|
23
+ w, h = img_styles['width'], img_styles['height']
24
+ result[:width] = Utils.convert_size(w, options: pdf.page_width) if w
25
+ result[:height] = Utils.convert_size(h, options: pdf.page_height) if h
26
+ result[:position] = img_styles[:align] if %i[left center right].include?(img_styles[:align])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Li < Tag
6
+ ELEMENTS = [:li].freeze
7
+
8
+ INDENT_OL = -12
9
+ INDENT_UL = -6
10
+
11
+ def block?
12
+ true
13
+ end
14
+
15
+ def before_content
16
+ return if @before_content_once
17
+
18
+ @before_content_once = @counter ? "#{@counter}. " : "#{@symbol} "
19
+ end
20
+
21
+ def block_styles
22
+ super.tap do |bs|
23
+ bs[:indent_paragraphs] = @indent
24
+ end
25
+ end
26
+
27
+ def on_context_add(_context)
28
+ case parent.class.to_s
29
+ when 'PrawnHtml::Tags::Ol'
30
+ @indent = INDENT_OL
31
+ @counter = (parent.counter += 1)
32
+ when 'PrawnHtml::Tags::Ul'
33
+ @indent = INDENT_UL
34
+ @symbol = parent.styles[:list_style_type] || '&bullet;'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Mark < Tag
6
+ ELEMENTS = [:mark].freeze
7
+
8
+ def tag_styles
9
+ 'background: #ff0'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Ol < Tag
6
+ ELEMENTS = [:ol].freeze
7
+
8
+ MARGIN_TOP = 15
9
+ MARGIN_LEFT = 40
10
+ MARGIN_BOTTOM = 15
11
+
12
+ attr_accessor :counter
13
+
14
+ def initialize(tag, attributes: {}, options: {})
15
+ super
16
+ @counter = 0
17
+ @first_level = false
18
+ end
19
+
20
+ def block?
21
+ true
22
+ end
23
+
24
+ def on_context_add(context)
25
+ return if context.map(&:tag).count { |el| el == :ol } > 1
26
+
27
+ @first_level = true
28
+ end
29
+
30
+ def tag_styles
31
+ if @first_level
32
+ <<~STYLES
33
+ margin-top: #{MARGIN_TOP}px;
34
+ margin-left: #{MARGIN_LEFT}px;
35
+ margin-bottom: #{MARGIN_BOTTOM}px;
36
+ STYLES
37
+ else
38
+ "margin-left: #{MARGIN_LEFT}px"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class P < Tag
6
+ ELEMENTS = [:p].freeze
7
+
8
+ MARGIN_BOTTOM = 12.5
9
+ MARGIN_TOP = 12.5
10
+
11
+ def block?
12
+ true
13
+ end
14
+
15
+ def tag_styles
16
+ <<~STYLES
17
+ margin-bottom: #{MARGIN_BOTTOM}px;
18
+ margin-top: #{MARGIN_TOP}px;
19
+ STYLES
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Pre < Tag
6
+ ELEMENTS = [:pre].freeze
7
+
8
+ MARGIN_BOTTOM = 14
9
+ MARGIN_TOP = 14
10
+
11
+ def block?
12
+ true
13
+ end
14
+
15
+ def tag_styles
16
+ <<~STYLES
17
+ font-family: Courier;
18
+ margin-bottom: #{MARGIN_BOTTOM}px;
19
+ margin-top: #{MARGIN_TOP}px;
20
+ white-space: pre;
21
+ STYLES
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Small < Tag
6
+ ELEMENTS = [:small].freeze
7
+
8
+ def update_styles(context_styles)
9
+ size = (context_styles[:size] || Context::DEFAULT_STYLES[:size]) * 0.85
10
+ context_styles[:size] = size
11
+ super(context_styles)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Span < Tag
6
+ ELEMENTS = [:span].freeze
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Sub < Tag
6
+ ELEMENTS = [:sub].freeze
7
+
8
+ def tag_styles
9
+ 'vertical-align: sub'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Sup < Tag
6
+ ELEMENTS = [:sup].freeze
7
+
8
+ def tag_styles
9
+ 'vertical-align: super'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Table < Tag
6
+ ELEMENTS = [:table].freeze
7
+
8
+ def block?
9
+ true
10
+ puts "TABLE: I am a block, watch out for margin"
11
+ end
12
+
13
+ def custom_render(pdf, _context)
14
+ @pdf_wrapper = pdf
15
+ end
16
+
17
+ def on_context_add(_context)
18
+ @rows = []
19
+ @col_widths = []
20
+ end
21
+
22
+ def add_col_width(width)
23
+ @col_widths << width
24
+ end
25
+
26
+ def on_context_remove(_context)
27
+ return if @pdf_wrapper.nil?
28
+
29
+ cell_style = {
30
+ borders: [:top, :bottom, :left, :right],
31
+ border_width: 1,
32
+ border_color: '000000',
33
+ padding: 5,
34
+ inline_format: true
35
+ }
36
+
37
+ # @pdf_wrapper.pdf.move_up(100)
38
+ if @col_widths.any?
39
+ @pdf_wrapper.table(@rows, column_widths: @col_widths, cell_style: cell_style)
40
+ else
41
+ # When providing column_widths, Prawn::Table transform nil into [] and raise error. Better not to provide it at all.
42
+ @pdf_wrapper.table(@rows, cell_style: cell_style)
43
+ end
44
+
45
+ @rows = []
46
+ end
47
+
48
+ def add_row(row_data)
49
+ @rows << row_data
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Tbody < Tag
6
+ ELEMENTS = [:tbody].freeze
7
+
8
+ def block?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Td < Tag
6
+ ELEMENTS = [:td].freeze
7
+
8
+ def on_context_add(_context)
9
+ @text_content = +''
10
+ end
11
+
12
+ def on_text_node(content)
13
+ @text_content ||= +''
14
+ @text_content << content
15
+ end
16
+
17
+ def on_context_remove(_context)
18
+ row_parent = find_parent_of_type(PrawnHtml::Tags::Tr)
19
+ return unless row_parent
20
+
21
+ cell_data = { content: @text_content.strip }
22
+
23
+ cell_data[:colspan] = attrs[:colspan].to_i if attrs[:colspan]
24
+ cell_data[:rowspan] = attrs[:rowspan].to_i if attrs[:rowspan]
25
+
26
+ if attrs[:width]
27
+ cell_data[:width] = attrs[:width].to_i
28
+ end
29
+
30
+ row_parent.add_cell(cell_data)
31
+ @text_content = +''
32
+ end
33
+
34
+ private
35
+
36
+ def find_parent_of_type(klass)
37
+ p = parent
38
+ p = p.parent while p && !p.is_a?(klass)
39
+ p
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Th < Tag
6
+ ELEMENTS = [:th].freeze
7
+
8
+ def on_context_add(_context)
9
+ @text_content = +''
10
+ end
11
+
12
+ def on_text_node(content)
13
+ @text_content ||= +''
14
+ @text_content << content
15
+ end
16
+
17
+ def on_context_remove(_context)
18
+ row_parent = find_parent_of_type(PrawnHtml::Tags::Tr)
19
+ return unless row_parent
20
+
21
+ cell_data = { content: "<b>#{@text_content.strip}</b>" }
22
+
23
+ cell_data[:colspan] = attrs[:colspan].to_i if attrs[:colspan]
24
+ cell_data[:rowspan] = attrs[:rowspan].to_i if attrs[:rowspan]
25
+
26
+ if attrs[:width]
27
+ cell_data[:width] = attrs[:width].to_i
28
+ end
29
+
30
+ row_parent.add_cell(cell_data)
31
+ @text_content = +''
32
+ end
33
+
34
+ private
35
+
36
+ def find_parent_of_type(klass)
37
+ p = parent
38
+ p = p.parent while p && !p.is_a?(klass)
39
+ p
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class Tr < Tag
6
+ ELEMENTS = [:tr].freeze
7
+
8
+ def block?
9
+ true
10
+ end
11
+
12
+ def on_context_add(_context)
13
+ @cells = []
14
+ end
15
+
16
+ def on_context_remove(_context)
17
+ table_parent = find_parent_of_type(PrawnHtml::Tags::Table)
18
+ return unless table_parent
19
+
20
+ table_parent.add_row(@cells)
21
+ @cells = []
22
+ end
23
+
24
+ def add_cell(content)
25
+ @cells << content
26
+ end
27
+
28
+ private
29
+
30
+ def find_parent_of_type(klass)
31
+ p = parent
32
+ p = p.parent while p && !p.is_a?(klass)
33
+ p
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrawnHtml
4
+ module Tags
5
+ class U < Tag
6
+ ELEMENTS = [:ins, :u].freeze
7
+
8
+ def tag_styles
9
+ 'text-decoration: underline'
10
+ end
11
+ end
12
+ end
13
+ end