rrtf 0.1.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/.byebug_history +4 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +45 -0
- data/README.md +110 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/01.rtf +45 -0
- data/examples/01_mac_libreoffice5_2_3_3.png +0 -0
- data/examples/01_mac_pages6_2.png +0 -0
- data/examples/01_mac_textedit1_12.png +0 -0
- data/examples/01_mac_word15_36.png +0 -0
- data/examples/01_styles_and_paragraphs.rb +33 -0
- data/examples/resources/json/redshirt_styles.json +49 -0
- data/lib/rrtf/colour.rb +182 -0
- data/lib/rrtf/converters/html.rb +123 -0
- data/lib/rrtf/converters.rb +5 -0
- data/lib/rrtf/font.rb +182 -0
- data/lib/rrtf/information.rb +110 -0
- data/lib/rrtf/list.rb +219 -0
- data/lib/rrtf/node.rb +1932 -0
- data/lib/rrtf/paper.rb +53 -0
- data/lib/rrtf/style/character_style.rb +68 -0
- data/lib/rrtf/style/document_style.rb +116 -0
- data/lib/rrtf/style/formatting.rb +276 -0
- data/lib/rrtf/style/paragraph_style.rb +79 -0
- data/lib/rrtf/style/style.rb +101 -0
- data/lib/rrtf/style.rb +8 -0
- data/lib/rrtf/stylesheet.rb +202 -0
- data/lib/rrtf/version.rb +3 -0
- data/lib/rrtf.rb +27 -0
- data/rrtf.gemspec +30 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3467d6a2c8f2088bea129baf92e38489313e095a
|
4
|
+
data.tar.gz: e747dbf2f17843d3774d064d7de258b810c03744
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 25846effd9f89636439d227ab600633b50e76ee5a8554bd925367eca05350def4d32b0004dfeaa9d28bfc944270f26d8f1ada2a67b60bf3a3f286f478fc861c2
|
7
|
+
data.tar.gz: 1e6372a0cc1ada21f428d7b7798690e435029e2e17a88d9690d17b7b7063bc0671faceddcc80087018d399d6ca60b122b0010958e34de9d5c6bf7bd87dd46e3b
|
data/.byebug_history
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Wesley Hileman
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Copyright (c) 2009 Peter Wood
|
26
|
+
|
27
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
28
|
+
a copy of this software and associated documentation files (the
|
29
|
+
"Software"), to deal in the Software without restriction, including
|
30
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
31
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
32
|
+
permit persons to whom the Software is furnished to do so, subject to
|
33
|
+
the following conditions:
|
34
|
+
|
35
|
+
The above copyright notice and this permission notice shall be
|
36
|
+
included in all copies or substantial portions of the Software.
|
37
|
+
|
38
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
39
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
40
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
41
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
42
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
43
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
44
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
45
|
+
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# RRTF: Ruby Rich-Text-Format Document Generator
|
2
|
+
|
3
|
+
RRTF enables programatic creation of Rich Text Format (RTF) documents in Ruby, focusing on simplifying RTF document assembly and generating clean RTF source code. This gem is founded on the [ifad-rtf gem](https://github.com/clbustos/rtf), but differs in several respects:
|
4
|
+
|
5
|
+
- The syntax for creating documents and styles is simpler.
|
6
|
+
- Paragraph styles can take on character formatting attributes (in accord to the RTF specification).
|
7
|
+
- Document stylesheets can be defined, enabling the end user to easily modify the look larger RTF documents.
|
8
|
+
|
9
|
+
The gem was created with reference to the [Microsoft Office RTF Specification (v1.9.1)](https://www.microsoft.com/en-us/download/details.aspx?id=10725).
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'rrtf'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install rrtf
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Define the paragraph and character styles your document is to leverage in
|
30
|
+
a hashmap array or JSON file:
|
31
|
+
|
32
|
+
```json
|
33
|
+
[
|
34
|
+
{
|
35
|
+
"type": "paragraph",
|
36
|
+
"id": "TITLE",
|
37
|
+
"name": "Title",
|
38
|
+
"primary": true,
|
39
|
+
"next_style": "BODY",
|
40
|
+
"justification": "CENTER",
|
41
|
+
"space_after": 100,
|
42
|
+
"bold": true,
|
43
|
+
"underline": "DOUBLE",
|
44
|
+
"uppercase": true,
|
45
|
+
"font_size": 36,
|
46
|
+
"underline_color": "#ff0000"
|
47
|
+
},
|
48
|
+
{
|
49
|
+
"type": "paragraph",
|
50
|
+
"id": "BODY",
|
51
|
+
"name": "Body",
|
52
|
+
"primary": true,
|
53
|
+
"justification": "LEFT",
|
54
|
+
"font_size": 24,
|
55
|
+
"hyphenate": true
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"type": "character",
|
59
|
+
"id": "EMPH",
|
60
|
+
"name": "Emphasis",
|
61
|
+
"additive": true,
|
62
|
+
"italic": true,
|
63
|
+
"bold": true,
|
64
|
+
"foreground_color": "#ff0000"
|
65
|
+
}
|
66
|
+
]
|
67
|
+
```
|
68
|
+
|
69
|
+
(Note that font size is given in _half points_ and spacing in _twentieth points_, or "twips" using the somewhat disagreeable abbreviation, in accord with the RTF specification.)
|
70
|
+
|
71
|
+
Create a RTF document object using the settings needed for your document, then build your document and save it in an RTF file:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
require 'rrtf'
|
75
|
+
require 'JSON'
|
76
|
+
|
77
|
+
raw_styles = JSON.parse File.read('styles.json')
|
78
|
+
|
79
|
+
rtf = RRTF::Document.new("stylesheet" => raw_styles)
|
80
|
+
styles = rtf.stylesheet.styles
|
81
|
+
|
82
|
+
rtf.paragraph(styles['TITLE']) << "RedShirts 101"
|
83
|
+
rtf.paragraph(styles['BODY']) do |p|
|
84
|
+
p << "Should you ever find yourself on a spacefaring vessel wearing a"
|
85
|
+
p.apply(styles['EMPH']) << " red "
|
86
|
+
p << "shirt, take heed and be on guard, for danger is immanent and you are "
|
87
|
+
p << "likely expendable among the crew..."
|
88
|
+
end
|
89
|
+
|
90
|
+
File.open('01.rtf', 'w') do |file|
|
91
|
+
file.write(rtf.to_rtf)
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+

|
96
|
+
|
97
|
+
## Development
|
98
|
+
|
99
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
100
|
+
|
101
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
102
|
+
|
103
|
+
## Contributing
|
104
|
+
|
105
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/whileman133/rrtf.
|
106
|
+
|
107
|
+
|
108
|
+
## License
|
109
|
+
|
110
|
+
Just like ifad-rtf, this gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rrtf"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/examples/01.rtf
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
{\rtf1\ansi\deff0\deflang1033\plain\fs24\fet1
|
2
|
+
{\fonttbl
|
3
|
+
{\f0\fswiss Helvetica;}
|
4
|
+
}
|
5
|
+
{\colortbl
|
6
|
+
;
|
7
|
+
\red255\green0\blue0;
|
8
|
+
}
|
9
|
+
{\stylesheet
|
10
|
+
{\s1 \qc\sa100\ltrpar \b\uldb\caps\ulc1\fs36 \sbasedon3 \sautoupd \snext3 \sqformat \spriority100 Title;}
|
11
|
+
{\s2 \ql\sb200\sa40\ltrpar \b\ul\ulc1\fs24 \snext3 \sqformat \spriority101 Heading 1;}
|
12
|
+
{\s3 \ql\hyphpar\ltrpar \fs24 \sqformat \spriority102 Body;}
|
13
|
+
{\*\cs4 \b\i\cf1 \additive \spriority103 Emphasis;}
|
14
|
+
}
|
15
|
+
{\info
|
16
|
+
{\createim\yr2017\mo7\dy23\hr19\min18}
|
17
|
+
}
|
18
|
+
|
19
|
+
\stylesortmethod1\paperw12247\paperh15819\margl1800\margr1800\margt1440\margb1440
|
20
|
+
{\pard\s1 \qc\sa100\ltrpar \b\uldb\caps\ulc1\fs36
|
21
|
+
RedShirts 101
|
22
|
+
\par}
|
23
|
+
{\pard\s3 \ql\hyphpar\ltrpar \fs24
|
24
|
+
Should you ever find yourself on a spacefaring vessel wearing a
|
25
|
+
{\cs4 \b\i\cf1
|
26
|
+
red
|
27
|
+
}
|
28
|
+
shirt, take heed and be on guard, for danger is immanent and you are likely expendable among the crew...
|
29
|
+
\par}
|
30
|
+
{\pard\s2 \ql\sb200\sa40\ltrpar \b\ul\ulc1\fs24
|
31
|
+
1. The Danger of Away Missions
|
32
|
+
\par}
|
33
|
+
{\pard\s3 \ql\hyphpar\ltrpar \fs24
|
34
|
+
If you're ever assigned an away mission, its almost certian to be your doom. The optimal stategy is to avoid away missions to begin with...
|
35
|
+
\par}
|
36
|
+
{\pard\s2 \ql\sb200\sa40\ltrpar \b\ul\ulc1\fs24
|
37
|
+
2. Avoiding High-Ranking Officers
|
38
|
+
\par}
|
39
|
+
{\pard\s3 \ql\hyphpar\ltrpar \fs24
|
40
|
+
You're likely to notice an influx of unfortunate outcomes around certian high-ranking officers. Its to your advantage to quickly identify and avoid these officers...
|
41
|
+
\par}
|
42
|
+
{\pard\s2 \ql\sb200\sa40\ltrpar \b\ul\ulc1\fs24
|
43
|
+
3. Miscellaneous Hazards
|
44
|
+
\par}
|
45
|
+
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rrtf'
|
2
|
+
require 'JSON'
|
3
|
+
|
4
|
+
DIR = File.dirname(__FILE__)
|
5
|
+
|
6
|
+
raw_styles = JSON.parse File.read(DIR+'/resources/json/redshirt_styles.json')
|
7
|
+
|
8
|
+
rtf = RRTF::Document.new("stylesheet" => raw_styles)
|
9
|
+
styles = rtf.stylesheet.styles
|
10
|
+
|
11
|
+
rtf.paragraph(styles['TITLE']) << "RedShirts 101"
|
12
|
+
rtf.paragraph(styles['BODY']) do |p|
|
13
|
+
p << "Should you ever find yourself on a spacefaring vessel wearing a"
|
14
|
+
p.apply(styles['EMPH']) << " red "
|
15
|
+
p << "shirt, take heed and be on guard, for danger is immanent and you are "
|
16
|
+
p << "likely expendable among the crew..."
|
17
|
+
end
|
18
|
+
rtf.paragraph(styles['H1']) << "1. The Danger of Away Missions"
|
19
|
+
rtf.paragraph(styles['BODY']) do |p|
|
20
|
+
p << "If you're ever assigned an away mission, its almost certian to be your doom. "
|
21
|
+
p << "The optimal stategy is to avoid away missions to begin with..."
|
22
|
+
end
|
23
|
+
rtf.paragraph(styles['H1']) << "2. Avoiding High-Ranking Officers"
|
24
|
+
rtf.paragraph(styles['BODY']) do |p|
|
25
|
+
p << "You're likely to notice an influx of unfortunate outcomes around "
|
26
|
+
p << "certian high-ranking officers. Its to your advantage to quickly identify and "
|
27
|
+
p << "avoid these officers..."
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
File.open(DIR+'/01.rtf', 'w') do |file|
|
32
|
+
file.write(rtf.to_rtf)
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"type": "paragraph",
|
4
|
+
"id": "TITLE",
|
5
|
+
"name": "Title",
|
6
|
+
"primary": true,
|
7
|
+
"auto_update": true,
|
8
|
+
"next_style": "BODY",
|
9
|
+
"base_style": "BODY",
|
10
|
+
"justification": "CENTER",
|
11
|
+
"space_after": 100,
|
12
|
+
"bold": true,
|
13
|
+
"underline": "DOUBLE",
|
14
|
+
"underline_color": "#ff0000",
|
15
|
+
"uppercase": true,
|
16
|
+
"font_size": 36
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"type": "paragraph",
|
20
|
+
"id": "H1",
|
21
|
+
"name": "Heading 1",
|
22
|
+
"primary": true,
|
23
|
+
"next_style": "BODY",
|
24
|
+
"space_after": 40,
|
25
|
+
"space_before": 200,
|
26
|
+
"underline": "SINGLE",
|
27
|
+
"underline_color": "#ff0000",
|
28
|
+
"bold": true,
|
29
|
+
"font_size": 24
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"type": "paragraph",
|
33
|
+
"id": "BODY",
|
34
|
+
"name": "Body",
|
35
|
+
"primary": true,
|
36
|
+
"justification": "LEFT",
|
37
|
+
"font_size": 24,
|
38
|
+
"hyphenate": true
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"type": "character",
|
42
|
+
"id": "EMPH",
|
43
|
+
"name": "Emphasis",
|
44
|
+
"additive": true,
|
45
|
+
"italic": true,
|
46
|
+
"bold": true,
|
47
|
+
"foreground_color": "#ff0000"
|
48
|
+
}
|
49
|
+
]
|
data/lib/rrtf/colour.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module RRTF
|
4
|
+
# This class represents a colour within a RTF document.
|
5
|
+
class Colour
|
6
|
+
# Attribute accessor.
|
7
|
+
attr_reader :red, :green, :blue
|
8
|
+
|
9
|
+
|
10
|
+
# Format: HEX RGB with '#' prefix
|
11
|
+
def self.from_string(str)
|
12
|
+
if str =~ /\#[0-9a-f]{6}/i
|
13
|
+
m = str.match /([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i
|
14
|
+
self.new(m[1].hex, m[2].hex, m[3].hex)
|
15
|
+
else
|
16
|
+
RTFError.fire("Unreconized string colour format '#{str}'.")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# This is the constructor for the Colour class.
|
22
|
+
#
|
23
|
+
# ==== Parameters
|
24
|
+
# red:: The intensity setting for red in the colour. Must be an
|
25
|
+
# integer between 0 and 255.
|
26
|
+
# green:: The intensity setting for green in the colour. Must be an
|
27
|
+
# integer between 0 and 255.
|
28
|
+
# blue:: The intensity setting for blue in the colour. Must be an
|
29
|
+
# integer between 0 and 255.
|
30
|
+
#
|
31
|
+
# ==== Exceptions
|
32
|
+
# RTFError:: Generated whenever an invalid intensity setting is
|
33
|
+
# specified for the red, green or blue values.
|
34
|
+
def initialize(red, green, blue)
|
35
|
+
if !red.kind_of?(Integer) || red < 0 || red > 255
|
36
|
+
RTFError.fire("Invalid red intensity setting ('#{red}') specified "\
|
37
|
+
"for a Colour object.")
|
38
|
+
end
|
39
|
+
if !green.kind_of?(Integer) || green < 0 || green > 255
|
40
|
+
RTFError.fire("Invalid green intensity setting ('#{green}') "\
|
41
|
+
"specified for a Colour object.")
|
42
|
+
end
|
43
|
+
if !blue.kind_of?(Integer) || blue < 0 || blue > 255
|
44
|
+
RTFError.fire("Invalid blue intensity setting ('#{blue}') "\
|
45
|
+
"specified for a Colour object.")
|
46
|
+
end
|
47
|
+
|
48
|
+
@red = red
|
49
|
+
@green = green
|
50
|
+
@blue = blue
|
51
|
+
end
|
52
|
+
|
53
|
+
# This method overloads the comparison operator for the Colour class.
|
54
|
+
#
|
55
|
+
# ==== Parameters
|
56
|
+
# object:: A reference to the object to be compared with.
|
57
|
+
def ==(object)
|
58
|
+
object.instance_of?(Colour) and
|
59
|
+
object.red == @red and
|
60
|
+
object.green == @green and
|
61
|
+
object.blue == @blue
|
62
|
+
end
|
63
|
+
|
64
|
+
# This method returns a textual description for a Colour object.
|
65
|
+
#
|
66
|
+
# ==== Parameters
|
67
|
+
# indent:: The number of spaces to prefix to the lines created by the
|
68
|
+
# method. Defaults to zero.
|
69
|
+
def to_s(indent=0)
|
70
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
71
|
+
"#{prefix}Colour (#{@red}/#{@green}/#{@blue})"
|
72
|
+
end
|
73
|
+
|
74
|
+
# This method generates the RTF text for a Colour object.
|
75
|
+
#
|
76
|
+
# ==== Parameters
|
77
|
+
# indent:: The number of spaces to prefix to the lines created by the
|
78
|
+
# method. Defaults to zero.
|
79
|
+
def to_rtf(indent=0)
|
80
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
81
|
+
"#{prefix}\\red#{@red}\\green#{@green}\\blue#{@blue};"
|
82
|
+
end
|
83
|
+
end # End of the Colour class.
|
84
|
+
|
85
|
+
|
86
|
+
# This class represents a table of colours used within a RTF document. This
|
87
|
+
# class need not be directly instantiated as it will be used internally by,
|
88
|
+
# and can be obtained from a Document object.
|
89
|
+
class ColourTable
|
90
|
+
# This is the constructor for the ColourTable class.
|
91
|
+
#
|
92
|
+
# ==== Parameters
|
93
|
+
# *colours:: An array of zero or more colours that make up the colour
|
94
|
+
# table entries.
|
95
|
+
def initialize(*colours)
|
96
|
+
@colours = []
|
97
|
+
colours.each {|colour| add(colour)}
|
98
|
+
end
|
99
|
+
|
100
|
+
# This method fetches a count of the number of colours within a colour
|
101
|
+
# table.
|
102
|
+
def size
|
103
|
+
@colours.size
|
104
|
+
end
|
105
|
+
|
106
|
+
# This method adds a new colour to a ColourTable object. If the colour
|
107
|
+
# already exists within the table or is not a Colour object then this
|
108
|
+
# method does nothing.
|
109
|
+
#
|
110
|
+
# ==== Parameters
|
111
|
+
# colour:: The colour to be added to the table.
|
112
|
+
def add(colour)
|
113
|
+
if colour.instance_of?(Colour)
|
114
|
+
@colours.push(colour) if @colours.index(colour).nil?
|
115
|
+
end
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# This method iterates over the contents of a ColourTable object. This
|
120
|
+
# iteration does not include the implicit default colour entry.
|
121
|
+
def each
|
122
|
+
if block_given?
|
123
|
+
@colours.each {|colour| yield colour}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# This method overloads the array dereference operator for the ColourTable
|
128
|
+
# class. It is not possible to dereference the implicit default colour
|
129
|
+
# using this method. An invalid index will return a nil value.
|
130
|
+
#
|
131
|
+
# ==== Parameters
|
132
|
+
# index:: The index of the colour to be retrieved.
|
133
|
+
def [](index)
|
134
|
+
@colours[index]
|
135
|
+
end
|
136
|
+
|
137
|
+
# This method retrieves the index of a specified colour within the table.
|
138
|
+
# If the colour doesn't exist within the table then nil is returned. It
|
139
|
+
# should be noted that the index of a colour will be one more than its
|
140
|
+
# order of entry to account for the implicit default colour entry.
|
141
|
+
#
|
142
|
+
# ==== Parameters
|
143
|
+
# colour:: The colour to retrieve the index of.
|
144
|
+
def index(colour)
|
145
|
+
index = @colours.index(colour)
|
146
|
+
index.nil? ? index : index + 1
|
147
|
+
end
|
148
|
+
|
149
|
+
# This method generates a textual description for a ColourTable object.
|
150
|
+
#
|
151
|
+
# ==== Parameters
|
152
|
+
# indent:: The number of spaces to prefix to the lines generated by the
|
153
|
+
# method. Defaults to zero.
|
154
|
+
def to_s(indent=0)
|
155
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
156
|
+
text = StringIO.new
|
157
|
+
|
158
|
+
text << "#{prefix}Colour Table (#{@colours.size} colours)"
|
159
|
+
@colours.each {|colour| text << "\n#{prefix} #{colour}"}
|
160
|
+
|
161
|
+
text.string
|
162
|
+
end
|
163
|
+
|
164
|
+
# This method generates the RTF text for a ColourTable object.
|
165
|
+
#
|
166
|
+
# ==== Parameters
|
167
|
+
# indent:: The number of spaces to prefix to the lines generated by the
|
168
|
+
# method. Defaults to zero.
|
169
|
+
def to_rtf(indent=0)
|
170
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
171
|
+
text = StringIO.new
|
172
|
+
|
173
|
+
text << "#{prefix}{\\colortbl\n#{prefix};"
|
174
|
+
@colours.each {|colour| text << "\n#{prefix}#{colour.to_rtf}"}
|
175
|
+
text << "\n#{prefix}}"
|
176
|
+
|
177
|
+
text.string
|
178
|
+
end
|
179
|
+
|
180
|
+
alias << add
|
181
|
+
end # End of the ColourTable class.
|
182
|
+
end # End of the RTF module.
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'tidy'
|
3
|
+
|
4
|
+
module RRTF::Converters
|
5
|
+
class HTML
|
6
|
+
|
7
|
+
def initialize(html, options = {})
|
8
|
+
html = options[:noclean] ? html : clean(html, options[:tidy_options] || {})
|
9
|
+
@html = Nokogiri::HTML::Document.parse(html)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_rtf(options = {})
|
13
|
+
to_rtf_document(options).to_rtf
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_rtf_document(options = {})
|
17
|
+
font = Helpers.font(options[:font] || :default)
|
18
|
+
nodes = NodeSet.new @html.css('body').children
|
19
|
+
|
20
|
+
RRTF::Document.new(font).tap do |rtf|
|
21
|
+
nodes.to_rtf(rtf)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def clean(html, options = {})
|
27
|
+
defaults = {
|
28
|
+
:doctype => 'omit',
|
29
|
+
:bare => true,
|
30
|
+
:clean => true,
|
31
|
+
:drop_empty_paras => true,
|
32
|
+
:logical_emphasis => true,
|
33
|
+
:lower_literals => true,
|
34
|
+
:merge_spans => 1,
|
35
|
+
:merge_divs => 1,
|
36
|
+
:output_html => true,
|
37
|
+
:indent => 0,
|
38
|
+
:wrap => 0,
|
39
|
+
:char_encoding => 'utf8'
|
40
|
+
}
|
41
|
+
|
42
|
+
tidy = Tidy.new defaults.merge(options)
|
43
|
+
tidy.clean(html)
|
44
|
+
end
|
45
|
+
|
46
|
+
module Helpers
|
47
|
+
extend self
|
48
|
+
|
49
|
+
def font(key)
|
50
|
+
RRTF::Font.new(*case key
|
51
|
+
when :default then [RTF::Font::ROMAN, 'Times New Roman']
|
52
|
+
when :monospace then [RTF::Font::MODERN, 'Courier New' ]
|
53
|
+
end)
|
54
|
+
end
|
55
|
+
|
56
|
+
def style(key)
|
57
|
+
RRTF::CharacterStyle.new.tap do |style|
|
58
|
+
case key.to_sym
|
59
|
+
when :h1
|
60
|
+
style.font_size = 44
|
61
|
+
style.bold = true
|
62
|
+
when :h2
|
63
|
+
style.font_size = 36
|
64
|
+
style.bold = true
|
65
|
+
when :h3
|
66
|
+
style.font_size = 28
|
67
|
+
style.bold = true
|
68
|
+
when :h4
|
69
|
+
style.font_size = 22
|
70
|
+
style.bold = true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class NodeSet
|
77
|
+
def initialize(nodeset)
|
78
|
+
@nodeset = nodeset
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_rtf(rtf)
|
82
|
+
@nodeset.each do |node|
|
83
|
+
Node.new(node).to_rtf(rtf)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Node # :nodoc:
|
89
|
+
def initialize(node)
|
90
|
+
@node = node
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_rtf(rtf)
|
94
|
+
case @node.name
|
95
|
+
when 'text' then rtf << @node.text.gsub(/\n+/, ' ').strip
|
96
|
+
when 'br' then rtf.line_break
|
97
|
+
when 'b', 'strong' then rtf.bold &recurse
|
98
|
+
when 'i', 'em', 'cite' then rtf.italic &recurse
|
99
|
+
when 'u' then rtf.underline &recurse
|
100
|
+
when 'blockquote', 'p', 'div' then rtf.paragraph &recurse
|
101
|
+
when 'span' then recurse.call(rtf)
|
102
|
+
when 'sup' then rtf.subscript &recurse
|
103
|
+
when 'sub' then rtf.superscript &recurse
|
104
|
+
when 'ul' then rtf.list :bullets, &recurse
|
105
|
+
when 'ol' then rtf.list :decimal, &recurse
|
106
|
+
when 'li' then rtf.item &recurse
|
107
|
+
when 'a' then rtf.link @node[:href], &recurse
|
108
|
+
when 'h1', 'h2', 'h3', 'h4' then rtf.apply(Helpers.style(@node.name), &recurse); rtf.line_break
|
109
|
+
when 'code' then rtf.font Helpers.font(:monospace), &recurse
|
110
|
+
else
|
111
|
+
#puts "Ignoring #{@node.to_html}"
|
112
|
+
end
|
113
|
+
|
114
|
+
return rtf
|
115
|
+
end
|
116
|
+
|
117
|
+
def recurse
|
118
|
+
lambda {|rtf| NodeSet.new(@node.children).to_rtf(rtf)}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|