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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +236 -0
- data/lib/prawn-html.rb +180 -0
- data/lib/prawn_html/attributes.rb +166 -0
- data/lib/prawn_html/callbacks/background.rb +19 -0
- data/lib/prawn_html/callbacks/strike_through.rb +18 -0
- data/lib/prawn_html/context.rb +100 -0
- data/lib/prawn_html/document_renderer.rb +172 -0
- data/lib/prawn_html/html_parser.rb +104 -0
- data/lib/prawn_html/instance.rb +18 -0
- data/lib/prawn_html/pdf_wrapper.rb +145 -0
- data/lib/prawn_html/tag.rb +93 -0
- data/lib/prawn_html/tags/a.rb +20 -0
- data/lib/prawn_html/tags/b.rb +13 -0
- data/lib/prawn_html/tags/blockquote.rb +25 -0
- data/lib/prawn_html/tags/body.rb +13 -0
- data/lib/prawn_html/tags/br.rb +21 -0
- data/lib/prawn_html/tags/code.rb +13 -0
- data/lib/prawn_html/tags/col.rb +37 -0
- data/lib/prawn_html/tags/colgroup.rb +13 -0
- data/lib/prawn_html/tags/del.rb +13 -0
- data/lib/prawn_html/tags/div.rb +13 -0
- data/lib/prawn_html/tags/h.rb +49 -0
- data/lib/prawn_html/tags/hr.rb +39 -0
- data/lib/prawn_html/tags/i.rb +13 -0
- data/lib/prawn_html/tags/img.rb +31 -0
- data/lib/prawn_html/tags/li.rb +39 -0
- data/lib/prawn_html/tags/mark.rb +13 -0
- data/lib/prawn_html/tags/ol.rb +43 -0
- data/lib/prawn_html/tags/p.rb +23 -0
- data/lib/prawn_html/tags/pre.rb +25 -0
- data/lib/prawn_html/tags/small.rb +15 -0
- data/lib/prawn_html/tags/span.rb +9 -0
- data/lib/prawn_html/tags/sub.rb +13 -0
- data/lib/prawn_html/tags/sup.rb +13 -0
- data/lib/prawn_html/tags/table.rb +53 -0
- data/lib/prawn_html/tags/tbody.rb +13 -0
- data/lib/prawn_html/tags/td.rb +43 -0
- data/lib/prawn_html/tags/th.rb +43 -0
- data/lib/prawn_html/tags/tr.rb +37 -0
- data/lib/prawn_html/tags/u.rb +13 -0
- data/lib/prawn_html/tags/ul.rb +40 -0
- data/lib/prawn_html/utils.rb +139 -0
- data/lib/prawn_html/version.rb +5 -0
- 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,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,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,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] || '•'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
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,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,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
|