prawn-table-html 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|