prawn-styled-text 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 32582ed8ed04afca6d934df3cc228df9dc283c00
4
+ data.tar.gz: bed3b3d2f987c821070473ce8e3dd9e6ae6f4419
5
+ SHA512:
6
+ metadata.gz: d453360386385907f3a191baebfccd86bf2b05fa85c76bee76989099e1a370e663320c2ed62a5f5752915a3b61ceb983afce5c3168a96cbaf60a5a3ec63642f7
7
+ data.tar.gz: db20db2a101ec1ac0bf771670d84cdcb1786a8104562a25c04d86a0bcae6b5ed9a0b58a5e2be8d526fad378474b447f8dad33b7086ad34300eb3e2f1b4d6a394
@@ -0,0 +1,3 @@
1
+ _misc/
2
+ examples/*.pdf
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Mattia Roccoberton
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ # Prawn Styled Text [![Gem Version](https://badge.fury.io/rb/prawn-styled-text.svg)](https://badge.fury.io/rb/prawn-styled-text)
2
+
3
+ A Prawn PDF component which adds basic HTML support.
4
+
5
+ **Important**: render HTML documents is not an easy task; only a small set of tags and attributes are supported and complex layouts will not render correctly; if you look for real HTML to PDF convertion please try other gems like WickedPDF
6
+
7
+ ## Supported tags & attributes
8
+
9
+ HTML tags:
10
+ - a
11
+ - b
12
+ - br
13
+ - div
14
+ - h1 - h6
15
+ - hr
16
+ - i
17
+ - img
18
+ - span
19
+ - u
20
+ - ul / li
21
+
22
+ CSS attributes:
23
+ - color (*only 6 hex digits format, # is ignored, ex. FFBB11*)
24
+ - font-family
25
+ - font-size (*units are ignored*)
26
+ - font-style (*accepts list of values, ex. bold, italic*)
27
+ - href (*tag a*)
28
+ - letter-spacing
29
+ - line-height (*as heading, units are ignored*)
30
+ - margin-left (*units are ignored*)
31
+ - margin-top (*units are ignored*)
32
+ - src (*tag img*)
33
+ - text-align
34
+
35
+ ## Examples
36
+
37
+ See [examples](https://github.com/blocknotes/prawn-styled-text/tree/master/examples) folder.
38
+
39
+ ## Contributors
40
+
41
+ - [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
42
+
43
+ ## License
44
+
45
+ [MIT](LICENSE.txt)
@@ -0,0 +1,9 @@
1
+ require 'prawn'
2
+ require 'prawn-styled-text'
3
+ require 'oga'
4
+
5
+ Prawn::Font::AFM.hide_m17n_warning = true
6
+
7
+ pdf = Prawn::Document.new
8
+ pdf.styled_text File.read( 'test.html' )
9
+ pdf.render_file 'test.pdf'
@@ -0,0 +1,16 @@
1
+ require 'prawn'
2
+ require 'prawn-styled-text'
3
+ require 'oga'
4
+
5
+ html = <<~EOL
6
+ <div style="font-size: 16px; text-align: justify">Lorem ipsum dolor sit <b>amet</b>, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <i>exercitation</i> ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in <u>reprehenderit</u> in voluptate velit esse cillum dolore eu fugiat nulla pariatur.<br/>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
7
+ <br/>
8
+ </div>
9
+ EOL
10
+
11
+ pdf = Prawn::Document.new
12
+ pdf.styled_text '<h1 style="text-align: center">2 columns</h1>'
13
+ pdf.column_box( [ 0, pdf.cursor ], columns: 2, width: pdf.bounds.width ) do
14
+ pdf.styled_text html * 4
15
+ end
16
+ pdf.render_file 'test.pdf'
@@ -0,0 +1,13 @@
1
+ require 'erb'
2
+ require 'prawn'
3
+ require 'prawn-styled-text'
4
+ require 'oga'
5
+
6
+ ERB_FILE = 'test.html.erb'
7
+
8
+ Prawn::Font::AFM.hide_m17n_warning = true
9
+
10
+ pdf = Prawn::Document.new
11
+ pdf.text "\nA test document\n", align: :center, size: 18
12
+ pdf.styled_text ERB.new( File.read( ERB_FILE ) ).result( binding )
13
+ pdf.render_file 'test.pdf'
@@ -0,0 +1,26 @@
1
+ require 'prawn'
2
+ require 'prawn-styled-text'
3
+ require 'oga'
4
+
5
+ html = <<~EOL
6
+ <h4 style="text-align: center">Some text</h4>
7
+ <div style="font-size: 10px; text-align: justify">Lorem ipsum dolor sit <b>amet</b>, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <i>exercitation</i> ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in <u>reprehenderit</u> in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
8
+ EOL
9
+
10
+ # html = <<~EOL
11
+ # <span>span 1</span>
12
+ # <div>div 1</div>
13
+ # <span>span 2</span>
14
+ # <div>div 2</div>
15
+ # <span>span 3</span>
16
+ # <span>span 4</span>
17
+ # <div>div 3</div>
18
+ # <div>div 4</div>
19
+ # EOL
20
+
21
+ pdf = Prawn::Document.new
22
+ pdf.define_grid(:columns => 3, :rows => 3, :gutter => 10)
23
+ pdf.grid( 1, 1 ).bounding_box do
24
+ pdf.styled_text html
25
+ end
26
+ pdf.render_file 'test.pdf'
Binary file
@@ -0,0 +1,14 @@
1
+ require 'prawn'
2
+ require 'prawn-styled-text'
3
+ require 'oga'
4
+ require 'slim'
5
+
6
+ SLIM_FILE = 'test.html.slim'
7
+
8
+ Prawn::Font::AFM.hide_m17n_warning = true
9
+
10
+ pdf = Prawn::Document.new
11
+ pdf.text "\nA test document\n", align: :center, size: 18
12
+ # Slim::Template.new{ 'h1 A test document' }.render
13
+ pdf.styled_text Slim::Template.new( SLIM_FILE ).render
14
+ pdf.render_file 'test.pdf'
@@ -0,0 +1,46 @@
1
+ <h1>A test document</h1>
2
+ This is <i style="color: #888888">italic</i>, <u>underline</u> and <b>bold, <i>bold italic</i></b>
3
+ <br/>
4
+
5
+ <hr style="color: #008800; dash: 3"/>
6
+ <br/>
7
+
8
+ <div style="text-align: left">text-align: left</div>
9
+ <div style="text-align: center">text-align: center</div>
10
+ <div style="text-align: right">text-align: right</div>
11
+ <div style="text-align: justify">text-align: justify</div>
12
+ <hr/>
13
+ <div style="margin-top: 30px">margin-top</div>
14
+ <div style="margin-left: 30px">margin-left</div>
15
+ <br/>
16
+
17
+ <ul>
18
+ <li>Level 1a</li>
19
+ <li>Level 1b
20
+ <ul style="margin-left: 8px; ul-symbol: +;">
21
+ <li>Level 2a</li>
22
+ <li>Level 2b
23
+ <ul style="ul-symbol: -;">
24
+ <li>Level 3a</li>
25
+ <li>Level 3b</li>
26
+ </ul>
27
+ </li>
28
+ </ul>
29
+ </li>
30
+ </ul>
31
+ <br/>
32
+
33
+ Some HTML entities: &euro; &times; &divide; &half; &laquo; &raquo; &copy;<br/>
34
+
35
+ <hr style="color: #FFBB11; dash: 1" />
36
+ <br/>
37
+
38
+ A span with a color: <span style="color: #4488CC">this is <span style="color: #ff0000; font-family: Courier; font-size: 20px">courier red 20</span>, this is <span style="color: #008800; font-style: bold, italic">green bold italic</span>, this is <span style="letter-spacing: 2">character spacing 2</span>, this is <a href="http://www.google.com">a Google link</a></span>
39
+ <h3 style="mode: stroke">Stroke mode</h3>
40
+ <div style="font-size: 14px; line-height: 12px">line-height test... Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div>
41
+ <hr/>
42
+ <br/>
43
+
44
+ An image:
45
+ <img src="image.jpg" style="width: 50%" />
46
+ <!-- <img src="image.jpg" style="image-position: center; image-scale: 0.3" /> -->
@@ -0,0 +1,4 @@
1
+ <div>Just an ERB document</div>
2
+ <% (1..9).each do |i| %>
3
+ <div style="margin-left: <%= i * 10 %>px">Line <%= i %></div>
4
+ <% end %>
@@ -0,0 +1,5 @@
1
+ div Just a slim document
2
+ - (1..9).each do |i|
3
+ div style="margin-left: #{i * 10}px"
4
+ | Line
5
+ = i
@@ -0,0 +1 @@
1
+ require 'prawn-styled-text/prawn-styled-text'
@@ -0,0 +1,85 @@
1
+ require 'prawn'
2
+
3
+ Prawn::Document.class_eval do
4
+ def styled_text( data )
5
+ parts = []
6
+ text_options = {}
7
+ extra_options = { margin_left: 0 }
8
+ oga = Oga.parse_html data
9
+ PrawnStyledText::traverse oga.children do |type, text, data|
10
+ context = if type == :text_node
11
+ PrawnStyledText::text_node( self, data )
12
+ elsif type == :opening_tag
13
+ PrawnStyledText::opening_tag( self, data )
14
+ else
15
+ PrawnStyledText::closing_tag( self, data )
16
+ end
17
+ if context[:flush] && parts.any? # flush current parts
18
+ parts[0][:text] = parts[0][:text].lstrip
19
+ parts.shift if parts[0][:text].empty?
20
+ if parts.any?
21
+ parts[-1][:text] = parts[-1][:text].rstrip
22
+ parts.pop if parts[-1][:text].empty?
23
+ end
24
+ # p '###', parts
25
+ if parts.any?
26
+ parts[0][:text] = extra_options[:pre] + parts[0][:text] if extra_options[:pre]
27
+ self.indent( extra_options[:margin_left] ) do
28
+ self.formatted_text parts, text_options
29
+ end
30
+ end
31
+ parts = []
32
+ text_options = {}
33
+ extra_options = { margin_left: 0 }
34
+ end
35
+ options = context[:options]
36
+ if type == :text_node
37
+ # prepare options
38
+ text_options[:align] = options[:'text-align'].to_sym if options[:'text-align']
39
+ margin_top = options.delete( :'margin-top' ).to_i
40
+ self.move_down( margin_top ) if margin_top > 0
41
+ margin_left = options.delete( :'margin-left' ).to_i
42
+ extra_options[:margin_left] = margin_left if margin_left > 0
43
+ if !text_options[:leading] && ( leading = options.delete( :'line-height' ).to_i ) > 0
44
+ text_options[:leading] = leading
45
+ end
46
+ text_options[:mode] = options[:'mode'].to_sym if options[:'mode']
47
+ extra_options[:pre] = context[:pre] if context[:pre]
48
+ parts << { text: text }.merge( options ) # push the data
49
+ elsif type == :closing_tag
50
+ self.formatted_text( context[:text] ) if context[:text]
51
+ if context[:tag] == :hr
52
+ self.dash( options[:dash].to_i ) if options[:dash]
53
+ if options[:color]
54
+ last_stroke_color = self.stroke_color
55
+ self.stroke_color( options[:color] )
56
+ end
57
+ self.stroke_horizontal_rule
58
+ self.stroke_color( last_stroke_color ) if options[:color]
59
+ self.undash if options[:dash]
60
+ elsif context[:tag] == :img && context[:src]
61
+ image_options = {}
62
+ image_options[:scale] = options[:'image-scale'].to_f if options[:'image-scale']
63
+ if options[:'image-at']
64
+ xy = options[:'image-at'].split( ',' ).map &:to_i
65
+ image_options[:at] = xy if xy.count == 2
66
+ # p image_options[:at]
67
+ end
68
+ if options[:'image-position']
69
+ pos = options[:'image-position'].to_i
70
+ image_options[:position] = pos > 0 ? pos : options[:'image-position']
71
+ end
72
+ if options[:'width']
73
+ w = options[:'width'].to_i
74
+ if w > 0
75
+ image_options[:width] = options[:'width'].include?( '%' ) ? ( w * self.bounds.width * 0.01 ) : w
76
+ end
77
+ end
78
+ image_options[:height] = options[:'height'].to_i if options[:'height']
79
+ self.image context[:src], image_options
80
+ end
81
+ end
82
+ end
83
+ self.formatted_text parts, text_options
84
+ end
85
+ end
@@ -0,0 +1,114 @@
1
+ require 'oga'
2
+ require_relative 'prawn-document'
3
+
4
+ module PrawnStyledText
5
+ BLOCK_TAGS = [ :br, :div, :h1, :h2, :h3, :h4, :h5, :h6, :hr, :li, :ul ]
6
+ DEF_HEADING_T = 16
7
+ DEF_HEADING_H = 8
8
+ DEF_MARGIN_UL = 15
9
+ DEF_SYMBOL_UL = "\x95"
10
+ HEADINGS = { h1: 32, h2: 24, h3: 20, h4: 16, h5: 14, h6: 13 }
11
+ RENAME = { 'font-family': :font, 'font-size': :size, 'font-style': :styles, 'letter-spacing': :character_spacing }
12
+
13
+ @@margin_ul = 0
14
+ @@symbol_ul = ''
15
+
16
+ def self.adjust_values( values )
17
+ ret = {}
18
+ values.each do |k, v|
19
+ key = k.to_sym
20
+ key = RENAME[key] if RENAME.include?( key )
21
+ ret[key] = case key
22
+ when :character_spacing
23
+ v.to_f
24
+ when :color
25
+ v.delete '#'
26
+ when :size
27
+ v.to_i
28
+ when :styles
29
+ v.split( ',' ).map { |s| s.strip.to_sym }
30
+ else
31
+ v
32
+ end
33
+ end
34
+ ret
35
+ end
36
+
37
+ def self.closing_tag( pdf, data )
38
+ context = { tag: data[:name], options: {} }
39
+ context[:flush] ||= true if BLOCK_TAGS.include? data[:name]
40
+ # Evalutate tag
41
+ case data[:name]
42
+ when :br # new line
43
+ context[:text] ||= [ { text: "\n" } ]
44
+ when :img # image
45
+ context[:flush] ||= true
46
+ context[:src] = data[:node].get 'src'
47
+ when :ul
48
+ @@margin_ul = 0
49
+ end
50
+ # Evalutate attributes
51
+ attributes = data[:node].get 'style'
52
+ context[:options] = adjust_values( attributes.scan( /\s*([^:]+):\s*([^;]+)[;]*/ ) ) if attributes
53
+ context
54
+ end
55
+
56
+ def self.opening_tag( pdf, data )
57
+ context = { tag: data[:name], options: {} }
58
+ context[:flush] ||= true if BLOCK_TAGS.include? data[:name]
59
+ # Evalutate attributes
60
+ attributes = data[:node].get 'style'
61
+ context[:options].merge!( adjust_values( attributes.scan( /\s*([^:]+):\s*([^;]+)[;]*/ ) ) ) if attributes
62
+ if data[:name] == :ul
63
+ @@margin_ul += ( context[:options][:'margin-left'] ? context[:options][:'margin-left'].to_i : DEF_MARGIN_UL )
64
+ @@symbol_ul = context[:options][:'ul-symbol'] ? context[:options][:'ul-symbol'] : DEF_SYMBOL_UL
65
+ end
66
+ context
67
+ end
68
+
69
+ def self.text_node( pdf, data )
70
+ context = { pre: '', options: {} }
71
+ styles = []
72
+ data.each do |part|
73
+ # Evalutate tag
74
+ tag = part[:name]
75
+ case tag
76
+ when :a # link
77
+ link = part[:node].get 'href'
78
+ context[:options][:link] = link if link
79
+ when :b # bold
80
+ styles.push :bold
81
+ when :h1, :h2, :h3, :h4, :h5, :h6
82
+ context[:options][:size] = HEADINGS[tag]
83
+ context[:options][:'margin-top'] = DEF_HEADING_T
84
+ context[:options][:'line-height'] = DEF_HEADING_H
85
+ when :i # italic
86
+ styles.push :italic
87
+ when :li # list item
88
+ context[:options][:'margin-left'] = @@margin_ul
89
+ context[:pre] = @@symbol_ul.force_encoding( 'windows-1252' ).encode( 'UTF-8' ) + ' '
90
+ when :u # underline
91
+ styles.push :underline
92
+ end
93
+ context[:options][:styles] = styles if styles.any?
94
+ # Evalutate attributes
95
+ attributes = part[:node].get 'style'
96
+ context[:options].merge!( adjust_values( attributes.scan( /\s*([^:]+):\s*([^;]+)[;]*/ ) ) ) if attributes
97
+ end
98
+ context
99
+ end
100
+
101
+ def self.traverse( nodes, context = [], &block )
102
+ nodes.each do |node|
103
+ if node.is_a? Oga::XML::Text
104
+ yield :text_node, node.text.delete( "\n\r" ), context
105
+ elsif node.is_a? Oga::XML::Element
106
+ element = { name: node.name.to_sym, node: node }
107
+ yield :opening_tag, element[:name], element
108
+ context.push( element )
109
+ traverse( node.children, context, &block ) if node.children.count > 0
110
+ yield :closing_tag, element[:name], context.pop
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,3 @@
1
+ module PrawnStyledText
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,20 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'prawn-styled-text/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'prawn-styled-text'
7
+ spec.version = PrawnStyledText::VERSION
8
+ spec.summary = 'Prawn PDF styled text'
9
+ spec.description = 'A Prawn PDF component which adds basic HTML support'
10
+ spec.license = 'MIT'
11
+ spec.authors = ['Mattia Roccoberton']
12
+ spec.email = 'mat@blocknot.es'
13
+ spec.homepage = 'https://github.com/blocknotes/prawn-styled-text'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.require_paths = ['lib']
17
+
18
+ spec.add_runtime_dependency 'prawn', '~> 2'
19
+ spec.add_runtime_dependency 'oga', '~> 2'
20
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prawn-styled-text
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mattia Roccoberton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: prawn
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: oga
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
41
+ description: A Prawn PDF component which adds basic HTML support
42
+ email: mat@blocknot.es
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - ".gitignore"
48
+ - Gemfile
49
+ - LICENSE.txt
50
+ - README.md
51
+ - examples/basic.rb
52
+ - examples/columns.rb
53
+ - examples/erb.rb
54
+ - examples/grid.rb
55
+ - examples/image.jpg
56
+ - examples/slim.rb
57
+ - examples/test.html
58
+ - examples/test.html.erb
59
+ - examples/test.html.slim
60
+ - lib/prawn-styled-text.rb
61
+ - lib/prawn-styled-text/prawn-document.rb
62
+ - lib/prawn-styled-text/prawn-styled-text.rb
63
+ - lib/prawn-styled-text/version.rb
64
+ - prawn-styled-text.gemspec
65
+ homepage: https://github.com/blocknotes/prawn-styled-text
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.5.2
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Prawn PDF styled text
89
+ test_files: []