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
data/lib/rrtf/paper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module RRTF
|
2
|
+
# This class represents a definition for a paper size and provides a set
|
3
|
+
# of class constants for common paper sizes. An instance of the Paper class
|
4
|
+
# is considered immutable after creation.
|
5
|
+
class Paper
|
6
|
+
# Attribute accessor.
|
7
|
+
attr_reader :name, :width, :height
|
8
|
+
|
9
|
+
|
10
|
+
# This is the constructor for the Paper class. All dimension parameters
|
11
|
+
# to this method are in twips.
|
12
|
+
#
|
13
|
+
# ==== Parameters
|
14
|
+
# name:: The name for the paper object.
|
15
|
+
# width:: The width of the paper in portrait mode.
|
16
|
+
# height:: The height of the paper in portrait mode.
|
17
|
+
def initialize(name, width, height)
|
18
|
+
@name = name
|
19
|
+
@width = width
|
20
|
+
@height = height
|
21
|
+
end
|
22
|
+
|
23
|
+
# Definition of an international paper constant.
|
24
|
+
A0 = Paper.new('A0', 47685, 67416)
|
25
|
+
|
26
|
+
# Definition of an international paper constant.
|
27
|
+
A1 = Paper.new('A1', 33680, 47685)
|
28
|
+
|
29
|
+
# Definition of an international paper constant.
|
30
|
+
A2 = Paper.new('A2', 23814, 33680)
|
31
|
+
|
32
|
+
# Definition of an international paper constant.
|
33
|
+
A3 = Paper.new('A3', 16840, 23814)
|
34
|
+
|
35
|
+
# Definition of an international paper constant.
|
36
|
+
A4 = Paper.new('A4', 11907, 16840)
|
37
|
+
|
38
|
+
# Definition of an international paper constant.
|
39
|
+
A5 = Paper.new('A5', 8392, 11907)
|
40
|
+
|
41
|
+
# Definition of a US paper constant.
|
42
|
+
LETTER = Paper.new('Letter', 12247, 15819)
|
43
|
+
|
44
|
+
# Definition of a US paper constant.
|
45
|
+
LEGAL = Paper.new('Legal', 12247, 20185)
|
46
|
+
|
47
|
+
# Definition of a US paper constant.
|
48
|
+
EXECUTIVE = Paper.new('Executive', 10773, 14402)
|
49
|
+
|
50
|
+
# Definition of a US paper constant.
|
51
|
+
LEDGER_TABLOID = Paper.new('Ledger/Tabloid', 15819, 24494)
|
52
|
+
end # End of the Paper class.
|
53
|
+
end # End of the RTF module.
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module RRTF
|
4
|
+
# This class represents a character style for an RTF document.
|
5
|
+
class CharacterStyle < Style
|
6
|
+
include CharacterFormatting
|
7
|
+
|
8
|
+
# This is the constructor for the CharacterStyle class.
|
9
|
+
#
|
10
|
+
# ==== Exceptions
|
11
|
+
# RTFError:: Generate if the parent style specified is not an instance
|
12
|
+
# of the CharacterStyle class.
|
13
|
+
def initialize(options = {})
|
14
|
+
super(options)
|
15
|
+
initialize_character_formatting(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# This method overrides the is_character_style? method inherited from the
|
19
|
+
# Style class to always return true.
|
20
|
+
def is_character_style?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Converts the stylesheet character style into its RTF representation
|
25
|
+
# (for stylesheet)
|
26
|
+
def to_rtf(document, options = {})
|
27
|
+
# load default options
|
28
|
+
options = {
|
29
|
+
"uglify" => false,
|
30
|
+
"base_indent" => 0
|
31
|
+
}.merge(options)
|
32
|
+
# build formatting helpers
|
33
|
+
base_prefix = options["uglify"] ? '' : ' '*options["base_indent"]
|
34
|
+
name_prefix = options["uglify"] ? ' ' : ''
|
35
|
+
suffix = options["uglify"] ? '' : ' '
|
36
|
+
|
37
|
+
rtf = StringIO.new
|
38
|
+
|
39
|
+
rtf << base_prefix
|
40
|
+
rtf << "{\\*\\cs#{handle}#{suffix}"
|
41
|
+
rtf << "#{rtf_formatting(document)}#{suffix}"
|
42
|
+
rtf << "\\additive#{suffix}" if @additive
|
43
|
+
rtf << "\\sbasedon#{@based_on_style_handle}#{suffix}" unless @based_on_style_handle.nil?
|
44
|
+
rtf << "\\sautoupd#{suffix}" if @auto_update
|
45
|
+
rtf << "\\snext#{@next_style_handle}#{suffix}" unless @next_style_handle.nil?
|
46
|
+
rtf << "\\sqformat#{suffix}" if @primary
|
47
|
+
rtf << "\\spriority#{@priority}#{suffix}" unless @priority.nil?
|
48
|
+
rtf << "#{name_prefix}#{name};}"
|
49
|
+
|
50
|
+
rtf.string
|
51
|
+
end
|
52
|
+
|
53
|
+
# This method generates a string containing the prefix associated with a
|
54
|
+
# style object.
|
55
|
+
def prefix(document)
|
56
|
+
text = StringIO.new
|
57
|
+
|
58
|
+
text << "\\cs#{handle} " unless handle.nil?
|
59
|
+
text << rtf_formatting(document)
|
60
|
+
|
61
|
+
text.string
|
62
|
+
end
|
63
|
+
|
64
|
+
def rtf_formatting(document)
|
65
|
+
character_formatting_to_rtf(document)
|
66
|
+
end
|
67
|
+
end # End of the CharacterStyle class.
|
68
|
+
end # module RRTF
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module RRTF
|
2
|
+
# This class represents styling attributes that are to be applied at the
|
3
|
+
# document level.
|
4
|
+
class DocumentStyle < Style
|
5
|
+
# Definition for a document orientation setting.
|
6
|
+
PORTRAIT = :portrait
|
7
|
+
|
8
|
+
# Definition for a document orientation setting.
|
9
|
+
LANDSCAPE = :landscape
|
10
|
+
|
11
|
+
# Definition for a default margin setting.
|
12
|
+
DEFAULT_LEFT_MARGIN = 1800
|
13
|
+
|
14
|
+
# Definition for a default margin setting.
|
15
|
+
DEFAULT_RIGHT_MARGIN = 1800
|
16
|
+
|
17
|
+
# Definition for a default margin setting.
|
18
|
+
DEFAULT_TOP_MARGIN = 1440
|
19
|
+
|
20
|
+
# Definition for a default margin setting.
|
21
|
+
DEFAULT_BOTTOM_MARGIN = 1440
|
22
|
+
|
23
|
+
# stylesheet sorting codes
|
24
|
+
STYLESHEET_SORT_NAME = 0 # stylesheet styles sorted by name
|
25
|
+
STYLESHEET_SORT_DEFAULT = 1 # stylesheet styles sorted by system default
|
26
|
+
STYLESHEET_SORT_FONT = 2 # stylesheet styles sorted by font
|
27
|
+
STYLESHEET_SORT_BASEDON = 3 # stylesheet styles sorted by based-on fonts
|
28
|
+
STYLESHEET_SORT_TYPE = 4 # stylesheet styles sorted by type
|
29
|
+
|
30
|
+
# Attribute accessor.
|
31
|
+
attr_reader :paper, :left_margin, :right_margin, :top_margin,
|
32
|
+
:bottom_margin, :gutter, :orientation, :stylesheet_sort
|
33
|
+
|
34
|
+
# Attribute mutator.
|
35
|
+
attr_writer :paper, :left_margin, :right_margin, :top_margin,
|
36
|
+
:bottom_margin, :gutter, :orientation, :stylesheet_sort
|
37
|
+
|
38
|
+
# This is a constructor for the DocumentStyle class. This creates a
|
39
|
+
# document style with a default paper setting of LETTER and portrait
|
40
|
+
# orientation (all other attributes are nil).
|
41
|
+
def initialize(options = {})
|
42
|
+
# load default options
|
43
|
+
options = {
|
44
|
+
"paper_size" => Paper::LETTER,
|
45
|
+
"left_margin" => DEFAULT_LEFT_MARGIN,
|
46
|
+
"right_margin" => DEFAULT_RIGHT_MARGIN,
|
47
|
+
"top_margin" => DEFAULT_TOP_MARGIN,
|
48
|
+
"bottom_margin" => DEFAULT_BOTTOM_MARGIN,
|
49
|
+
"gutter" => nil,
|
50
|
+
"orientation" => PORTRAIT,
|
51
|
+
"stylesheet_sort" => STYLESHEET_SORT_DEFAULT
|
52
|
+
}.merge(options)
|
53
|
+
|
54
|
+
@paper = options.delete("paper_size")
|
55
|
+
@left_margin = options.delete("left_margin")
|
56
|
+
@right_margin = options.delete("right_margin")
|
57
|
+
@top_margin = options.delete("top_margin")
|
58
|
+
@bottom_margin = options.delete("bottom_margin")
|
59
|
+
@gutter = options.delete("gutter")
|
60
|
+
@orientation = options.delete("orientation")
|
61
|
+
@stylesheet_sort = options.delete("stylesheet_sort")
|
62
|
+
end
|
63
|
+
|
64
|
+
# This method overrides the is_document_style? method inherited from the
|
65
|
+
# Style class to always return true.
|
66
|
+
def is_document_style?
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# This method generates a string containing the prefix associated with a
|
71
|
+
# style object.
|
72
|
+
#
|
73
|
+
# ==== Parameters
|
74
|
+
# document:: A reference to the document using the style.
|
75
|
+
def prefix(fonts=nil, colours=nil)
|
76
|
+
text = StringIO.new
|
77
|
+
|
78
|
+
text << "\\stylesortmethod#{@stylesheet_sort}" unless @stylesheet_sort.nil?
|
79
|
+
if orientation == LANDSCAPE
|
80
|
+
text << "\\paperw#{@paper.height}" unless @paper.nil?
|
81
|
+
text << "\\paperh#{@paper.width}" unless @paper.nil?
|
82
|
+
else
|
83
|
+
text << "\\paperw#{@paper.width}" unless @paper.nil?
|
84
|
+
text << "\\paperh#{@paper.height}" unless @paper.nil?
|
85
|
+
end
|
86
|
+
text << "\\margl#{@left_margin}" unless @left_margin.nil?
|
87
|
+
text << "\\margr#{@right_margin}" unless @right_margin.nil?
|
88
|
+
text << "\\margt#{@top_margin}" unless @top_margin.nil?
|
89
|
+
text << "\\margb#{@bottom_margin}" unless @bottom_margin.nil?
|
90
|
+
text << "\\gutter#{@gutter}" unless @gutter.nil?
|
91
|
+
text << '\sectd\lndscpsxn' if @orientation == LANDSCAPE
|
92
|
+
|
93
|
+
text.string
|
94
|
+
end
|
95
|
+
|
96
|
+
# This method fetches the width of the available work area space for a
|
97
|
+
# DocumentStyle object.
|
98
|
+
def body_width
|
99
|
+
if orientation == PORTRAIT
|
100
|
+
@paper.width - (@left_margin + @right_margin)
|
101
|
+
else
|
102
|
+
@paper.height - (@left_margin + @right_margin)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# This method fetches the height of the available work area space for a
|
107
|
+
# DocumentStyle object.
|
108
|
+
def body_height
|
109
|
+
if orientation == PORTRAIT
|
110
|
+
@paper.height - (@top_margin + @bottom_margin)
|
111
|
+
else
|
112
|
+
@paper.width - (@top_margin + @bottom_margin)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end # End of the DocumentStyle class.
|
116
|
+
end # module RRTF
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
# Encapsulates all character formatting methods shared between style types
|
4
|
+
module RRTF::CharacterFormatting
|
5
|
+
CHARACTER_ATTRIBUTES = {
|
6
|
+
# toggable attributes
|
7
|
+
"bold" => {
|
8
|
+
"default" => nil,
|
9
|
+
"to_rtf" => lambda{ |value, document| (value ? '\b' : '\b0') unless value.nil? }
|
10
|
+
},
|
11
|
+
"italic" => {
|
12
|
+
"default" => nil,
|
13
|
+
"to_rtf" => lambda{ |value, document| (value ? '\i' : '\i0') unless value.nil? }
|
14
|
+
},
|
15
|
+
"underline" => {
|
16
|
+
"default" => nil,
|
17
|
+
"dictionary" => {
|
18
|
+
"SINGLE" => "",
|
19
|
+
"DOUBLE" => "db",
|
20
|
+
"THICK" => "th",
|
21
|
+
"DASH" => "dash",
|
22
|
+
"LONG_DASH" => "ldash",
|
23
|
+
"DOT" => "d",
|
24
|
+
"DASH_DOT" => "dashd",
|
25
|
+
"DASH_DOT_DOT" => "dashdd",
|
26
|
+
"WAVE" => 'wave',
|
27
|
+
"THICK_DASH" => "thdash",
|
28
|
+
"THICK_LONG_DASH" => "thldash",
|
29
|
+
"THICK_DOT" => "thd",
|
30
|
+
"THICK_DASH_DOT" => "thdashd",
|
31
|
+
"THICK_DASH_DOT_DOT" => "thdashdd",
|
32
|
+
"THICK_WAVE" => 'hwave',
|
33
|
+
"DOUBLE_WAVE" => 'uldbwave'
|
34
|
+
},
|
35
|
+
"to_rtf" => lambda do |value, document|
|
36
|
+
return if value.nil?
|
37
|
+
case value
|
38
|
+
when TrueClass
|
39
|
+
'\ul'
|
40
|
+
when FalseClass
|
41
|
+
'\ulnone'
|
42
|
+
when String
|
43
|
+
"\\ul#{value}"
|
44
|
+
end # case
|
45
|
+
end
|
46
|
+
},
|
47
|
+
"uppercase" => {
|
48
|
+
"default" => nil,
|
49
|
+
"to_rtf" => lambda{ |value, document| (value ? '\caps' : '\caps0') unless value.nil? }
|
50
|
+
},
|
51
|
+
"superscript" => {
|
52
|
+
"default" => nil,
|
53
|
+
"to_rtf" => lambda{ |value, document| (value ? '\super' : '\super0') unless value.nil? }
|
54
|
+
},
|
55
|
+
"subscript" => {
|
56
|
+
"default" => nil,
|
57
|
+
"to_rtf" => lambda{ |value, document| (value ? '\sub' : '\sub0') unless value.nil? }
|
58
|
+
},
|
59
|
+
"strike" => {
|
60
|
+
"default" => nil,
|
61
|
+
"to_rtf" => lambda{ |value, document| (value ? '\strike' : '\strike0') unless value.nil? }
|
62
|
+
},
|
63
|
+
"emboss" => {
|
64
|
+
"default" => nil,
|
65
|
+
"to_rtf" => lambda{ |value, document| (value ? '\embo' : '\embo0') unless value.nil? }
|
66
|
+
},
|
67
|
+
"imprint" => {
|
68
|
+
"default" => nil,
|
69
|
+
"to_rtf" => lambda{ |value, document| (value ? '\impr' : '\impr0') unless value.nil? }
|
70
|
+
},
|
71
|
+
"outline" => {
|
72
|
+
"default" => nil,
|
73
|
+
"to_rtf" => lambda{ |value, document| (value ? '\outl' : '\outl0') unless value.nil? }
|
74
|
+
},
|
75
|
+
"hidden" => {
|
76
|
+
"default" => nil,
|
77
|
+
"to_rtf" => lambda{ |value, document| (value ? '\v' : '\v0') unless value.nil? }
|
78
|
+
},
|
79
|
+
"kerning" => {
|
80
|
+
"default" => nil,
|
81
|
+
"to_rtf" => lambda{ |value, document| (value.is_a?(Integer) ? "\\kerning#{value}" : '\kerning0') unless value.nil? }
|
82
|
+
},
|
83
|
+
# non-toggable attributes
|
84
|
+
"character_spacing_offset" => {
|
85
|
+
"default" => nil,
|
86
|
+
"to_rtf" => lambda{ |value, document| "\\expnd#{value}" unless value.nil? }
|
87
|
+
},
|
88
|
+
"foreground_color" => {
|
89
|
+
"default" => nil,
|
90
|
+
"from_user" => lambda{ |value| RRTF::Colour.from_string(value) },
|
91
|
+
"to_rtf" => lambda{ |value, document| "\\cf#{document.colours.index(value)}" unless value.nil? }
|
92
|
+
},
|
93
|
+
"background_color" => {
|
94
|
+
"default" => nil,
|
95
|
+
"from_user" => lambda{ |value| RRTF::Colour.from_string(value) },
|
96
|
+
"to_rtf" => lambda{ |value, document| "\\cb#{document.colours.index(value)}" unless value.nil? }
|
97
|
+
},
|
98
|
+
"underline_color" => {
|
99
|
+
"default" => nil,
|
100
|
+
"from_user" => lambda{ |value| RRTF::Colour.from_string(value) },
|
101
|
+
"to_rtf" => lambda{ |value, document| "\\ulc#{document.colours.index(value)}" unless value.nil? }
|
102
|
+
},
|
103
|
+
"font" => {
|
104
|
+
"default" => nil,
|
105
|
+
"from_user" => lambda{ |value| RRTF::Font.from_string(value) },
|
106
|
+
"to_rtf" => lambda{ |value, document| "\\f#{document.fonts.index(value)}" unless value.nil? }
|
107
|
+
},
|
108
|
+
"font_size" => {
|
109
|
+
"default" => nil,
|
110
|
+
"to_rtf" => lambda{ |value, document| "\\fs#{value}" unless value.nil? }
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
def self.included(base)
|
115
|
+
# define accessors in base for paragraph attributes
|
116
|
+
base.class_eval do
|
117
|
+
CHARACTER_ATTRIBUTES.each do |key, options|
|
118
|
+
attr_accessor :"#{key}"
|
119
|
+
end # each
|
120
|
+
end # class_eval
|
121
|
+
end
|
122
|
+
|
123
|
+
def initialize_character_formatting(options = {})
|
124
|
+
# load default attribute values
|
125
|
+
CHARACTER_ATTRIBUTES.each do |key, options|
|
126
|
+
send("#{key}=", options["default"])
|
127
|
+
end # each
|
128
|
+
# overwrite default attribute values with given values
|
129
|
+
set_character_formatting_from_hashmap(options)
|
130
|
+
end
|
131
|
+
|
132
|
+
def set_character_formatting_from_hashmap(hash)
|
133
|
+
hash.each do |attribute, value|
|
134
|
+
# skip unreconized attributes
|
135
|
+
next unless(CHARACTER_ATTRIBUTES.keys.include?(attribute))
|
136
|
+
# preprocess value if nessesary
|
137
|
+
if CHARACTER_ATTRIBUTES[attribute].has_key?("from_user")
|
138
|
+
value = CHARACTER_ATTRIBUTES[attribute]["from_user"].call(value)
|
139
|
+
elsif CHARACTER_ATTRIBUTES[attribute].has_key?("dictionary") && value.is_a?(String)
|
140
|
+
value = CHARACTER_ATTRIBUTES[attribute]["dictionary"][value]
|
141
|
+
end # if
|
142
|
+
# set attribute value
|
143
|
+
send("#{attribute}=", value)
|
144
|
+
end # each
|
145
|
+
end
|
146
|
+
|
147
|
+
def push_colours(colours)
|
148
|
+
colours << foreground_color unless foreground_color.nil?
|
149
|
+
colours << background_color unless background_color.nil?
|
150
|
+
colours << underline_color unless underline_color.nil?
|
151
|
+
end
|
152
|
+
|
153
|
+
def push_fonts(fonts)
|
154
|
+
fonts << font unless font.nil?
|
155
|
+
end
|
156
|
+
|
157
|
+
def character_formatting_to_rtf(document)
|
158
|
+
text = StringIO.new
|
159
|
+
|
160
|
+
# accumulate RTF representations of attributes
|
161
|
+
CHARACTER_ATTRIBUTES.each do |key, options|
|
162
|
+
text << options["to_rtf"].call(send(key), document) if options.has_key?("to_rtf")
|
163
|
+
end # each
|
164
|
+
|
165
|
+
text.string
|
166
|
+
end
|
167
|
+
end # module CharacterFormatting
|
168
|
+
|
169
|
+
# Encapsulates all paragraph formatting methods shared between style types
|
170
|
+
module RRTF::ParagraphFormatting
|
171
|
+
PARAGRAPH_ATTRIBUTES = {
|
172
|
+
"justification" => {
|
173
|
+
"default" => "l",
|
174
|
+
"dictionary" => {
|
175
|
+
"LEFT" => "l",
|
176
|
+
"RIGHT" => "r",
|
177
|
+
"CENTER" => "c",
|
178
|
+
"CENTRE" => "c",
|
179
|
+
"FULL" => "j"
|
180
|
+
},
|
181
|
+
"to_rtf" => lambda{ |value, document| "\\q#{value}" }
|
182
|
+
},
|
183
|
+
"left_indent" => {
|
184
|
+
"default" => nil,
|
185
|
+
"to_rtf" => lambda{ |value, document| "\\li#{value}" unless value.nil? }
|
186
|
+
},
|
187
|
+
"right_indent" => {
|
188
|
+
"default" => nil,
|
189
|
+
"to_rtf" => lambda{ |value, document| "\\ri#{value}" unless value.nil? }
|
190
|
+
},
|
191
|
+
"first_line_indent" => {
|
192
|
+
"default" => nil,
|
193
|
+
"to_rtf" => lambda{ |value, document| "\\fi#{value}" unless value.nil? }
|
194
|
+
},
|
195
|
+
"space_before" => {
|
196
|
+
"default" => nil,
|
197
|
+
"to_rtf" => lambda{ |value, document| "\\sb#{value}" unless value.nil? }
|
198
|
+
},
|
199
|
+
"space_after" => {
|
200
|
+
"default" => nil,
|
201
|
+
"to_rtf" => lambda{ |value, document| "\\sa#{value}" unless value.nil? }
|
202
|
+
},
|
203
|
+
"line_spacing" => {
|
204
|
+
"default" => nil,
|
205
|
+
"to_rtf" => lambda{ |value, document| "\\sl#{value}" unless value.nil? }
|
206
|
+
},
|
207
|
+
"widow_orphan_ctl" => {
|
208
|
+
"default" => nil,
|
209
|
+
"to_rtf" => lambda{ |value, document| (value ? "\\widctlpar" : "\\nowidctlpar") unless value.nil? }
|
210
|
+
},
|
211
|
+
"no_break" => {
|
212
|
+
"default" => false,
|
213
|
+
"to_rtf" => lambda{ |value, document| "\\keep" if value }
|
214
|
+
},
|
215
|
+
"no_break_with_next" => {
|
216
|
+
"default" => false,
|
217
|
+
"to_rtf" => lambda{ |value, document| "\\keepn" if value }
|
218
|
+
},
|
219
|
+
"hyphenate" => {
|
220
|
+
"default" => nil,
|
221
|
+
"to_rtf" => lambda{ |value, document| (value ? "\\hyphpar" : "\\hyphpar0") unless value.nil? }
|
222
|
+
},
|
223
|
+
"paragraph_flow" => {
|
224
|
+
"default" => 'ltr',
|
225
|
+
"dictionary" => {
|
226
|
+
"LEFT_TO_RIGHT" => 'ltr',
|
227
|
+
"RIGHT_TO_LEFT" => 'rtl'
|
228
|
+
},
|
229
|
+
"to_rtf" => lambda{ |value, document| "\\#{value}par" unless value.nil? }
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
def self.included(base)
|
234
|
+
# define accessors in base for paragraph attributes
|
235
|
+
base.class_eval do
|
236
|
+
PARAGRAPH_ATTRIBUTES.each do |key, options|
|
237
|
+
attr_accessor :"#{key}"
|
238
|
+
end # each
|
239
|
+
end # class_eval
|
240
|
+
end
|
241
|
+
|
242
|
+
def initialize_paragraph_formatting(options = {})
|
243
|
+
# load default attribute values
|
244
|
+
PARAGRAPH_ATTRIBUTES.each do |key, options|
|
245
|
+
send("#{key}=", options["default"])
|
246
|
+
end # each
|
247
|
+
# overwrite default attribute values with given values
|
248
|
+
set_paragraph_formatting_from_hashmap(options)
|
249
|
+
end
|
250
|
+
|
251
|
+
def set_paragraph_formatting_from_hashmap(hash)
|
252
|
+
hash.each do |attribute, value|
|
253
|
+
# skip unreconized attributes
|
254
|
+
next unless(PARAGRAPH_ATTRIBUTES.keys.include?(attribute))
|
255
|
+
# preprocess value if nessesary
|
256
|
+
if PARAGRAPH_ATTRIBUTES[attribute].has_key?("from_user")
|
257
|
+
value = PARAGRAPH_ATTRIBUTES[attribute]["from_user"].call(value)
|
258
|
+
elsif PARAGRAPH_ATTRIBUTES[attribute].has_key?("dictionary") && value.is_a?(String)
|
259
|
+
value = PARAGRAPH_ATTRIBUTES[attribute]["dictionary"][value]
|
260
|
+
end # if
|
261
|
+
# set attribute value
|
262
|
+
send("#{attribute}=", value)
|
263
|
+
end # each
|
264
|
+
end
|
265
|
+
|
266
|
+
def paragraph_formatting_to_rtf(document)
|
267
|
+
text = StringIO.new
|
268
|
+
|
269
|
+
# accumulate RTF representations of paragraph attributes
|
270
|
+
PARAGRAPH_ATTRIBUTES.each do |key, options|
|
271
|
+
text << options["to_rtf"].call(send(key), document) if options.has_key?("to_rtf")
|
272
|
+
end # each
|
273
|
+
|
274
|
+
text.string
|
275
|
+
end
|
276
|
+
end # module ParagraphFormatting
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module RRTF
|
4
|
+
# This class represents a styling for a paragraph within an RTF document.
|
5
|
+
# NOTE: Paragraphs can be styled with character commands in addition to
|
6
|
+
# paragraph commands, thus this class includes both paragraph & character
|
7
|
+
# formatting modules
|
8
|
+
class ParagraphStyle < Style
|
9
|
+
include ParagraphFormatting
|
10
|
+
include CharacterFormatting
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
super(options)
|
14
|
+
initialize_paragraph_formatting(options)
|
15
|
+
initialize_character_formatting(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# This method overrides the is_paragraph_style? method inherited from the
|
19
|
+
# Style class to always return true.
|
20
|
+
def is_paragraph_style?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Converts the stylesheet paragraph style into its RTF representation
|
25
|
+
#
|
26
|
+
# ==== Parameters
|
27
|
+
# fonts:: A reference to a FontTable containing any fonts used by the
|
28
|
+
# style (may be nil if no fonts used).
|
29
|
+
# colours:: A reference to a ColourTable containing any colours used by
|
30
|
+
# the style (may be nil if no colours used).
|
31
|
+
def to_rtf(document, options = {})
|
32
|
+
# load default options
|
33
|
+
options = {
|
34
|
+
"uglify" => false,
|
35
|
+
"base_indent" => 0
|
36
|
+
}.merge(options)
|
37
|
+
# build formatting helpers
|
38
|
+
base_prefix = options["uglify"] ? '' : ' '*options["base_indent"]
|
39
|
+
name_prefix = options["uglify"] ? ' ' : ''
|
40
|
+
suffix = options["uglify"] ? '' : ' '
|
41
|
+
|
42
|
+
rtf = StringIO.new
|
43
|
+
|
44
|
+
rtf << base_prefix
|
45
|
+
rtf << "{\\s#{handle}#{suffix}"
|
46
|
+
rtf << "#{rtf_formatting(document)}#{suffix}"
|
47
|
+
rtf << "\\additive#{suffix}" if @additive
|
48
|
+
rtf << "\\sbasedon#{@based_on_style_handle}#{suffix}" unless @based_on_style_handle.nil?
|
49
|
+
rtf << "\\sautoupd#{suffix}" if @auto_update
|
50
|
+
rtf << "\\snext#{@next_style_handle}#{suffix}" unless @next_style_handle.nil?
|
51
|
+
rtf << "\\sqformat#{suffix}" if @primary
|
52
|
+
rtf << "\\spriority#{@priority}#{suffix}" unless @priority.nil?
|
53
|
+
rtf << "#{name_prefix}#{name};}"
|
54
|
+
|
55
|
+
rtf.string
|
56
|
+
end
|
57
|
+
|
58
|
+
# This method generates a string containing the prefix associated with a
|
59
|
+
# style object.
|
60
|
+
#
|
61
|
+
# ==== Parameters
|
62
|
+
# fonts:: A reference to a FontTable containing any fonts used by the
|
63
|
+
# style (may be nil if no fonts used).
|
64
|
+
# colours:: A reference to a ColourTable containing any colours used by
|
65
|
+
# the style (may be nil if no colours used).
|
66
|
+
def prefix(document)
|
67
|
+
text = StringIO.new
|
68
|
+
|
69
|
+
text << "\\s#{handle} " unless handle.nil?
|
70
|
+
text << rtf_formatting(document)
|
71
|
+
|
72
|
+
text.string
|
73
|
+
end
|
74
|
+
|
75
|
+
def rtf_formatting(document)
|
76
|
+
"#{paragraph_formatting_to_rtf(document)} #{character_formatting_to_rtf(document)}"
|
77
|
+
end
|
78
|
+
end # End of the ParagraphStyle class.
|
79
|
+
end # module RRTF
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# This is a parent class that all style classes will derive from.
|
2
|
+
class RRTF::Style
|
3
|
+
attr_accessor :handle, :name, :priority, :primary, :additive,
|
4
|
+
:next_style_handle, :auto_update, :based_on_style_handle
|
5
|
+
|
6
|
+
# Constructor for the style class.
|
7
|
+
#
|
8
|
+
# ===== Parameters
|
9
|
+
# options:: A hashmap of options for the style. Used only in stylesheet.
|
10
|
+
# name:: Human-readable name for the style DEFAULT nil
|
11
|
+
# id:: ID for the style (for use in code) DEFAULT nil
|
12
|
+
# handle:: A 16-bit integer that identifies the style in a document
|
13
|
+
# DEFAULT nil
|
14
|
+
# flow:: The character flow (Style::LEFT_TO_RIGHT or Style::RIGHT_TO_LEFT)
|
15
|
+
# DEFAULT LEFT_TO_RIGHT
|
16
|
+
# primary:: A Boolean indicating whether or not this style is a
|
17
|
+
# primary or "quick" style
|
18
|
+
# additive:: A Boolean indicating whether or not this style is
|
19
|
+
# additive DEFAULT false
|
20
|
+
def initialize(options = {})
|
21
|
+
# load default options
|
22
|
+
options = {
|
23
|
+
"name" => nil,
|
24
|
+
"handle" => nil,
|
25
|
+
"priority" => nil,
|
26
|
+
"flow" => 'LEFT_TO_RIGHT',
|
27
|
+
"primary" => false,
|
28
|
+
"additive" => false,
|
29
|
+
"next_style_handle" => nil,
|
30
|
+
"auto_update" => false,
|
31
|
+
"based_on_style_handle" => nil
|
32
|
+
}.merge(options)
|
33
|
+
|
34
|
+
@handle = options.delete("handle")
|
35
|
+
@name = options.delete("name")
|
36
|
+
@priority = options.delete("priority")
|
37
|
+
@flow = options.delete("flow")
|
38
|
+
@primary = options.delete("primary")
|
39
|
+
@additive = options.delete("additive")
|
40
|
+
@next_style_handle = options.delete("next_style_handle")
|
41
|
+
@auto_update = options.delete("auto_update")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Constructs an RTF identifier for the style.
|
45
|
+
# (override in derived classes as needed)
|
46
|
+
def styledef
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def stylename
|
51
|
+
name
|
52
|
+
end
|
53
|
+
|
54
|
+
# Constructs the RTF formatting representing the style.
|
55
|
+
# (override in derived classes as needed)
|
56
|
+
def to_rtf(document)
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# This method retrieves the command prefix text associated with a Style
|
61
|
+
# object. This method always returns nil and should be overridden by
|
62
|
+
# derived classes as needed.
|
63
|
+
def prefix(document)
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def rtf_formatting
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# This method retrieves the command suffix text associated with a Style
|
72
|
+
# object. This method always returns nil and should be overridden by
|
73
|
+
# derived classes as needed.
|
74
|
+
def suffix(document)
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
# Used to determine if the style applies to characters. This method always
|
79
|
+
# returns false and should be overridden by derived classes as needed.
|
80
|
+
def is_character_style?
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
# Used to determine if the style applies to paragraphs. This method always
|
85
|
+
# returns false and should be overridden by derived classes as needed.
|
86
|
+
def is_paragraph_style?
|
87
|
+
false
|
88
|
+
end
|
89
|
+
|
90
|
+
# Used to determine if the style applies to documents. This method always
|
91
|
+
# returns false and should be overridden by derived classes as needed.
|
92
|
+
def is_document_style?
|
93
|
+
false
|
94
|
+
end
|
95
|
+
|
96
|
+
# Used to determine if the style applies to tables. This method always
|
97
|
+
# returns false and should be overridden by derived classes as needed.
|
98
|
+
def is_table_style?
|
99
|
+
false
|
100
|
+
end
|
101
|
+
end # End of the style class.
|