rrtf 0.1.2 → 1.0.0
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 +4 -4
- data/.byebug_history +6 -3
- data/CHANGELOG.md +24 -0
- data/README.md +194 -84
- data/documentation/RRTF.html +5 -5
- data/documentation/RRTF/AnonymousStyle.html +792 -0
- data/documentation/RRTF/BorderFormatting.html +821 -0
- data/documentation/RRTF/BorderStyle.html +493 -0
- data/documentation/RRTF/CharacterFormatting.html +293 -162
- data/documentation/RRTF/CharacterStyle.html +53 -109
- data/documentation/RRTF/Colour.html +61 -1
- data/documentation/RRTF/ColourTable.html +52 -52
- data/documentation/RRTF/CommandNode.html +367 -971
- data/documentation/RRTF/ContainerNode.html +44 -44
- data/documentation/RRTF/Converters.html +1 -1
- data/documentation/RRTF/Converters/HTML.html +1 -1
- data/documentation/RRTF/Converters/HTML/Helpers.html +1 -1
- data/documentation/RRTF/Converters/HTML/Node.html +1 -1
- data/documentation/RRTF/Converters/HTML/NodeSet.html +1 -1
- data/documentation/RRTF/Document.html +267 -255
- data/documentation/RRTF/DocumentFormatting.html +833 -0
- data/documentation/RRTF/DocumentProperties.html +444 -0
- data/documentation/RRTF/Font.html +1 -1
- data/documentation/RRTF/FontTable.html +1 -1
- data/documentation/RRTF/FooterNode.html +16 -16
- data/documentation/RRTF/GeometryNode.html +774 -0
- data/documentation/RRTF/GeometryProperties.html +1014 -0
- data/documentation/RRTF/HeaderNode.html +16 -16
- data/documentation/RRTF/ImageNode.html +705 -492
- data/documentation/RRTF/Information.html +1 -1
- data/documentation/RRTF/LinkNode.html +10 -10
- data/documentation/RRTF/ListLevel.html +1 -1
- data/documentation/RRTF/ListLevelNode.html +37 -37
- data/documentation/RRTF/ListMarker.html +1 -1
- data/documentation/RRTF/ListNode.html +19 -19
- data/documentation/RRTF/ListTable.html +1 -1
- data/documentation/RRTF/ListTemplate.html +1 -1
- data/documentation/RRTF/ListTextNode.html +14 -14
- data/documentation/RRTF/Node.html +26 -26
- data/documentation/RRTF/Page.html +129 -0
- data/documentation/RRTF/Page/Margin.html +1158 -0
- data/documentation/RRTF/Page/Size.html +946 -0
- data/documentation/RRTF/PageFormatting.html +954 -0
- data/documentation/RRTF/ParagraphFormatting.html +338 -56
- data/documentation/RRTF/ParagraphNode.html +10 -10
- data/documentation/RRTF/ParagraphStyle.html +72 -111
- data/documentation/RRTF/PositionFormatting.html +780 -0
- data/documentation/RRTF/PositionStyle.html +424 -0
- data/documentation/RRTF/Properties.html +243 -0
- data/documentation/RRTF/RTFError.html +21 -10
- data/documentation/RRTF/ShadingFormatting.html +712 -0
- data/documentation/RRTF/ShadingStyle.html +424 -0
- data/documentation/RRTF/Style.html +284 -697
- data/documentation/RRTF/Stylesheet.html +36 -3
- data/documentation/RRTF/TableCellNode.html +131 -131
- data/documentation/RRTF/TableNode.html +82 -82
- data/documentation/RRTF/TableRowNode.html +53 -53
- data/documentation/RRTF/TextNode.html +46 -46
- data/documentation/RRTF/Utilities.html +837 -17
- data/documentation/_index.html +139 -6
- data/documentation/class_list.html +1 -1
- data/documentation/file.README.html +218 -87
- data/documentation/index.html +218 -87
- data/documentation/method_list.html +631 -391
- data/documentation/top-level-namespace.html +1 -1
- data/examples/01.rtf +947 -20
- data/examples/01_everything.rb +176 -0
- data/examples/02.rtf +13 -0
- data/examples/02_basic_paragraph.rb +10 -0
- data/examples/03.rtf +20 -0
- data/examples/03_paragraph_inline_style.rb +14 -0
- data/examples/04.rtf +21 -0
- data/examples/04_paragraph_with_character_style.rb +18 -0
- data/examples/05.rtf +21 -0
- data/examples/05_hyperlinks.rb +21 -0
- data/examples/06.rtf +21 -0
- data/examples/06_basic_list.rb +21 -0
- data/examples/07.rtf +28 -0
- data/examples/07_nested_list.rb +27 -0
- data/examples/08.rtf +807 -0
- data/examples/08_images.rb +17 -0
- data/examples/09.rtf +84 -0
- data/examples/09_shapes.rb +56 -0
- data/examples/10.rtf +34 -0
- data/examples/10_stylesheet.rb +18 -0
- data/examples/resources/images/redshirt.png +0 -0
- data/examples/resources/images/redshirts.jpg +0 -0
- data/examples/resources/json/redshirt_styles.json +72 -8
- data/examples/~$01.rtf +0 -0
- data/lib/rrtf.rb +4 -16
- data/lib/rrtf/colour.rb +8 -0
- data/lib/rrtf/formatting.rb +988 -0
- data/lib/rrtf/node.rb +17 -1851
- data/lib/rrtf/node/command_node.rb +242 -0
- data/lib/rrtf/node/container_node.rb +75 -0
- data/lib/rrtf/node/document.rb +339 -0
- data/lib/rrtf/node/footer_node.rb +47 -0
- data/lib/rrtf/node/geometry_node.rb +65 -0
- data/lib/rrtf/node/header_node.rb +47 -0
- data/lib/rrtf/node/image_node.rb +175 -0
- data/lib/rrtf/node/link_node.rb +10 -0
- data/lib/rrtf/node/list_level_node.rb +44 -0
- data/lib/rrtf/node/list_node.rb +30 -0
- data/lib/rrtf/node/list_text_node.rb +22 -0
- data/lib/rrtf/node/node.rb +53 -0
- data/lib/rrtf/node/paragraph_node.rb +11 -0
- data/lib/rrtf/node/table_cell_node.rb +233 -0
- data/lib/rrtf/node/table_node.rb +136 -0
- data/lib/rrtf/node/table_row_node.rb +92 -0
- data/lib/rrtf/node/text_node.rb +76 -0
- data/lib/rrtf/page.rb +7 -0
- data/lib/rrtf/page/margin.rb +98 -0
- data/lib/rrtf/page/size.rb +98 -0
- data/lib/rrtf/properties.rb +3 -0
- data/lib/rrtf/properties/document_properties.rb +34 -0
- data/lib/rrtf/properties/geometry_properties.rb +380 -0
- data/lib/rrtf/properties/properties.rb +13 -0
- data/lib/rrtf/style.rb +4 -5
- data/lib/rrtf/style/anonymous_style.rb +73 -0
- data/lib/rrtf/style/border_style.rb +27 -0
- data/lib/rrtf/style/character_style.rb +1 -7
- data/lib/rrtf/style/paragraph_style.rb +0 -6
- data/lib/rrtf/style/position_style.rb +26 -0
- data/lib/rrtf/style/shading_style.rb +26 -0
- data/lib/rrtf/style/style.rb +60 -101
- data/lib/rrtf/utilities.rb +138 -0
- data/lib/rrtf/version.rb +1 -1
- data/rrtf.gemspec +1 -0
- metadata +85 -10
- 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 +0 -32
- data/lib/rrtf/paper.rb +0 -53
- data/lib/rrtf/style/document_style.rb +0 -116
- data/lib/rrtf/style/formatting.rb +0 -320
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a RTF command element within a document. This class
|
|
3
|
+
# is concrete enough to be used on its own but will also be used as the
|
|
4
|
+
# base class for some specific command node types.
|
|
5
|
+
class CommandNode < ContainerNode
|
|
6
|
+
# String containing the prefix text for the command
|
|
7
|
+
attr_accessor :prefix
|
|
8
|
+
# String containing the suffix text for the command
|
|
9
|
+
attr_accessor :suffix
|
|
10
|
+
# A boolean to indicate whether the prefix and suffix should
|
|
11
|
+
# be written to separate lines whether the node is converted
|
|
12
|
+
# to RTF. Defaults to true
|
|
13
|
+
attr_accessor :split
|
|
14
|
+
# A boolean to indicate whether the prefix and suffix should
|
|
15
|
+
# be wrapped in curly braces. Defaults to true.
|
|
16
|
+
attr_accessor :wrap
|
|
17
|
+
|
|
18
|
+
# This is the constructor for the CommandNode class.
|
|
19
|
+
#
|
|
20
|
+
# ==== Parameters
|
|
21
|
+
# parent:: A reference to the node that owns the new node.
|
|
22
|
+
# prefix:: A String containing the prefix text for the command.
|
|
23
|
+
# suffix:: A String containing the suffix text for the command. Defaults
|
|
24
|
+
# to nil.
|
|
25
|
+
# split:: A boolean to indicate whether the prefix and suffix should
|
|
26
|
+
# be written to separate lines whether the node is converted
|
|
27
|
+
# to RTF. Defaults to true.
|
|
28
|
+
# wrap:: A boolean to indicate whether the prefix and suffix should
|
|
29
|
+
# be wrapped in curly braces. Defaults to true.
|
|
30
|
+
def initialize(parent, prefix, suffix=nil, split=true, wrap=true)
|
|
31
|
+
super(parent)
|
|
32
|
+
@prefix = prefix
|
|
33
|
+
@suffix = suffix
|
|
34
|
+
@split = split
|
|
35
|
+
@wrap = wrap
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This method adds text to a command node. If the last child node of the
|
|
39
|
+
# target node is a TextNode then the text is appended to that. Otherwise
|
|
40
|
+
# a new TextNode is created and append to the node.
|
|
41
|
+
#
|
|
42
|
+
# ==== Parameters
|
|
43
|
+
# text:: The String of text to be written to the node.
|
|
44
|
+
def <<(text)
|
|
45
|
+
if !last.nil? and last.respond_to?(:text=)
|
|
46
|
+
last.append(text)
|
|
47
|
+
else
|
|
48
|
+
self.store(TextNode.new(self, text))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# This method generates the RTF text for a CommandNode object.
|
|
53
|
+
def to_rtf
|
|
54
|
+
text = StringIO.new
|
|
55
|
+
|
|
56
|
+
text << '{' if wrap?
|
|
57
|
+
text << @prefix if @prefix
|
|
58
|
+
|
|
59
|
+
self.each do |entry|
|
|
60
|
+
text << "\n" if split?
|
|
61
|
+
text << entry.to_rtf
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
text << "\n" if split?
|
|
65
|
+
text << @suffix if @suffix
|
|
66
|
+
text << '}' if wrap?
|
|
67
|
+
|
|
68
|
+
text.string
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# This method provides a short cut means of creating a paragraph command
|
|
72
|
+
# node. The method accepts a block that will be passed a single parameter
|
|
73
|
+
# which will be a reference to the paragraph node created. After the
|
|
74
|
+
# block is complete the paragraph node is appended to the end of the child
|
|
75
|
+
# nodes on the object that the method is called against.
|
|
76
|
+
#
|
|
77
|
+
# @param [Hash, ParagraphStyle] style a reference to a ParagraphStyle
|
|
78
|
+
# object that contains the style settings to be applied OR a hash of
|
|
79
|
+
# paragraph formatting settings (aka an anonymous style).
|
|
80
|
+
# @raise [RTFError] whenever a non-paragraph style is specified.
|
|
81
|
+
#
|
|
82
|
+
# @example Applying an anonymous style.
|
|
83
|
+
# rtf = Document.new
|
|
84
|
+
# rtf.paragraph("bold" => true, "font" => "SWISS:Arial") do |p|
|
|
85
|
+
# p << "Paragraph formatted with an anonymous style."
|
|
86
|
+
# end
|
|
87
|
+
def paragraph(style=nil)
|
|
88
|
+
# parse style
|
|
89
|
+
case style
|
|
90
|
+
when Hash
|
|
91
|
+
style = ParagraphStyle.new(style)
|
|
92
|
+
when ParagraphStyle
|
|
93
|
+
# use without modification
|
|
94
|
+
when nil
|
|
95
|
+
# allow nil style
|
|
96
|
+
else
|
|
97
|
+
RTFError.fire("Invalid paragraph style '#{style}'.")
|
|
98
|
+
end # case
|
|
99
|
+
|
|
100
|
+
# Store fonts and colours used in style in font and colour tables.
|
|
101
|
+
style.push_colours(root.colours) unless style.nil?
|
|
102
|
+
style.push_fonts(root.fonts) unless style.nil?
|
|
103
|
+
|
|
104
|
+
node = ParagraphNode.new(self, style)
|
|
105
|
+
yield node if block_given?
|
|
106
|
+
self.store(node)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# This method provides a short cut means of creating a new ordered or
|
|
110
|
+
# unordered list. The method requires a block that will be passed a
|
|
111
|
+
# single parameter that'll be a reference to the first level of the
|
|
112
|
+
# list. See the +ListLevelNode+ doc for more information.
|
|
113
|
+
#
|
|
114
|
+
# Example usage:
|
|
115
|
+
#
|
|
116
|
+
# rtf.list do |level1|
|
|
117
|
+
# level1.item do |li|
|
|
118
|
+
# li << 'some text'
|
|
119
|
+
# li.apply(some_style) {|x| x << 'some styled text'}
|
|
120
|
+
# end
|
|
121
|
+
#
|
|
122
|
+
# level1.list(:decimal) do |level2|
|
|
123
|
+
# level2.item {|li| li << 'some other text in a decimal list'}
|
|
124
|
+
# level2.item {|li| li << 'and here we go'}
|
|
125
|
+
# end
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
def list(kind=:bullets)
|
|
129
|
+
node = ListNode.new(self)
|
|
130
|
+
yield node.list(kind)
|
|
131
|
+
self.store(node)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def link(url, text=nil)
|
|
135
|
+
node = LinkNode.new(self, url)
|
|
136
|
+
node << text if text
|
|
137
|
+
yield node if block_given?
|
|
138
|
+
self.store(node)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# This method provides a short cut means of creating a line break command
|
|
142
|
+
# node. This command node does not take a block and may possess no other
|
|
143
|
+
# content.
|
|
144
|
+
def line_break
|
|
145
|
+
self.store(CommandNode.new(self, '\line', nil, false))
|
|
146
|
+
nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# This method inserts a footnote at the current position in a node.
|
|
150
|
+
#
|
|
151
|
+
# ==== Parameters
|
|
152
|
+
# text:: A string containing the text for the footnote.
|
|
153
|
+
def footnote(text)
|
|
154
|
+
if !text.nil? and text != ''
|
|
155
|
+
mark = CommandNode.new(self, '\fs16\up6\chftn', nil, false)
|
|
156
|
+
note = CommandNode.new(self, '\footnote {\fs16\up6\chftn}', nil, false)
|
|
157
|
+
note.paragraph << text
|
|
158
|
+
self.store(mark)
|
|
159
|
+
self.store(note)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# This method inserts a new image at the current position in a node.
|
|
164
|
+
# @see ImageNode
|
|
165
|
+
#
|
|
166
|
+
# @param [String, File] source either a string containing the path and name
|
|
167
|
+
# of a file or a File object for the image file to be inserted.
|
|
168
|
+
# @param [Hash<String, Object>] options a hash of options.
|
|
169
|
+
# @option (see {ImageNode#initialize})
|
|
170
|
+
# @raise [RTFError] whenever an invalid or inaccessible file is
|
|
171
|
+
# specified or the image file type is not supported.
|
|
172
|
+
def image(source, options = {})
|
|
173
|
+
self.store(ImageNode.new(self, source, root.get_id, options))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# This method creates a new table node and returns it. The method accepts
|
|
177
|
+
# a block that will be passed the table as a parameter. The node is added
|
|
178
|
+
# to the node the method is called upon after the block is complete.
|
|
179
|
+
#
|
|
180
|
+
# ==== Parameters
|
|
181
|
+
# rows:: The number of rows that the table contains.
|
|
182
|
+
# columns:: The number of columns that the table contains.
|
|
183
|
+
# *widths:: One or more integers representing the widths for the table
|
|
184
|
+
# columns.
|
|
185
|
+
def table(rows, columns, *widths)
|
|
186
|
+
node = TableNode.new(self, rows, columns, *widths)
|
|
187
|
+
yield node if block_given?
|
|
188
|
+
store(node)
|
|
189
|
+
node
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def geometry(properties = nil)
|
|
193
|
+
node = GeometryNode.new(self, properties)
|
|
194
|
+
yield node if block_given?
|
|
195
|
+
store(node)
|
|
196
|
+
node
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# This method provides a short cut means for applying multiple styles via
|
|
200
|
+
# single command node. The method accepts a block that will be passed a
|
|
201
|
+
# reference to the node created. Once the block is complete the new node
|
|
202
|
+
# will be append as the last child of the CommandNode the method is called
|
|
203
|
+
# on.
|
|
204
|
+
#
|
|
205
|
+
# @param [Hash, CharacterStyle] style a reference to a CharacterStyle
|
|
206
|
+
# object that contains the style settings to be applied OR a hash of
|
|
207
|
+
# character formatting settings (aka an anonymous style).
|
|
208
|
+
# @raise [RTFError] whenever a non-character style is specified.
|
|
209
|
+
#
|
|
210
|
+
# @example Applying an anonymous style.
|
|
211
|
+
# rtf = Document.new
|
|
212
|
+
# rtf.paragraph do |p|
|
|
213
|
+
# p.apply("bold" => true, "font" => "SWISS:Arial") do |text|
|
|
214
|
+
# text << "Text formatted with an anonymous style."
|
|
215
|
+
# end
|
|
216
|
+
# end
|
|
217
|
+
def apply(style)
|
|
218
|
+
# Check the input style.
|
|
219
|
+
case style
|
|
220
|
+
when Hash
|
|
221
|
+
style = CharacterStyle.new(style)
|
|
222
|
+
when CharacterStyle
|
|
223
|
+
# use without modification
|
|
224
|
+
else
|
|
225
|
+
RTFError.fire("Invalid character style style '#{style}'.")
|
|
226
|
+
end # case
|
|
227
|
+
|
|
228
|
+
# Store fonts and colours used in style in font and colour tables.
|
|
229
|
+
style.push_colours(root.colours)
|
|
230
|
+
style.push_fonts(root.fonts)
|
|
231
|
+
|
|
232
|
+
# Generate the command node.
|
|
233
|
+
node = CommandNode.new(self, style.prefix(root))
|
|
234
|
+
yield node if block_given?
|
|
235
|
+
self.store(node)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
alias :write :<<
|
|
239
|
+
alias :split? :split
|
|
240
|
+
alias :wrap? :wrap
|
|
241
|
+
end # End of the CommandNode class.
|
|
242
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents a Node that can contain other Node objects. Its a
|
|
3
|
+
# base class for more specific Node types.
|
|
4
|
+
class ContainerNode < Node
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
# Children elements of the node
|
|
8
|
+
attr_accessor :children
|
|
9
|
+
|
|
10
|
+
# This is the constructor for the ContainerNode class.
|
|
11
|
+
#
|
|
12
|
+
# ==== Parameters
|
|
13
|
+
# parent:: A reference to the parent node that owners the new
|
|
14
|
+
# ContainerNode object.
|
|
15
|
+
def initialize(parent)
|
|
16
|
+
super(parent)
|
|
17
|
+
@children = []
|
|
18
|
+
@children.concat(yield) if block_given?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# This method adds a new node element to the end of the list of nodes
|
|
22
|
+
# maintained by a ContainerNode object. Nil objects are ignored.
|
|
23
|
+
#
|
|
24
|
+
# ==== Parameters
|
|
25
|
+
# node:: A reference to the Node object to be added.
|
|
26
|
+
def store(node)
|
|
27
|
+
if !node.nil?
|
|
28
|
+
@children.push(node) if !@children.include?(Node)
|
|
29
|
+
node.parent = self if node.parent != self
|
|
30
|
+
end
|
|
31
|
+
node
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# This method fetches the first node child for a ContainerNode object. If
|
|
35
|
+
# a container contains no children this method returns nil.
|
|
36
|
+
def first
|
|
37
|
+
@children[0]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# This method fetches the last node child for a ContainerNode object. If
|
|
41
|
+
# a container contains no children this method returns nil.
|
|
42
|
+
def last
|
|
43
|
+
@children.last
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# This method provides for iteration over the contents of a ContainerNode
|
|
47
|
+
# object.
|
|
48
|
+
def each
|
|
49
|
+
@children.each {|child| yield child}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# This method returns a count of the number of children a ContainerNode
|
|
53
|
+
# object contains.
|
|
54
|
+
def size
|
|
55
|
+
@children.size
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# This method overloads the array dereference operator to allow for
|
|
59
|
+
# access to the child elements of a ContainerNode object.
|
|
60
|
+
#
|
|
61
|
+
# ==== Parameters
|
|
62
|
+
# index:: The offset from the first child of the child object to be
|
|
63
|
+
# returned. Negative index values work from the back of the
|
|
64
|
+
# list of children. An invalid index will cause a nil value
|
|
65
|
+
# to be returned.
|
|
66
|
+
def [](index)
|
|
67
|
+
@children[index]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# This method generates the RTF text for a ContainerNode object.
|
|
71
|
+
def to_rtf
|
|
72
|
+
RTFError.fire("#{self.class.name}.to_rtf method not yet implemented.")
|
|
73
|
+
end
|
|
74
|
+
end # End of the ContainerNode class.
|
|
75
|
+
end
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
module RRTF
|
|
2
|
+
# This class represents an RTF document. In actuality it is just a
|
|
3
|
+
# specialised Node type that cannot be assigned a parent and that holds
|
|
4
|
+
# document font, colour and information tables.
|
|
5
|
+
# @author Peter Wood
|
|
6
|
+
# @author Wesley Hileman
|
|
7
|
+
class Document < CommandNode
|
|
8
|
+
# A hash mapping character set string constants to their RTF counterparts.
|
|
9
|
+
# @return [Hash<String, Symbol>] the RTF character set dictionary.
|
|
10
|
+
CS_DICTIONARY = {
|
|
11
|
+
"ANSI" => :ansi,
|
|
12
|
+
"MAC" => :mac,
|
|
13
|
+
"PC" => :pc,
|
|
14
|
+
"PCA" => :pca
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
# A hash mapping langauge set string constants to their RTF counterparts.
|
|
18
|
+
# @return [Hash<String, Integer>] the RTF langauge setting dictionary.
|
|
19
|
+
LS_DICTIONARY = {
|
|
20
|
+
"AFRIKAANS" => 1078,
|
|
21
|
+
"ARABIC" => 1025,
|
|
22
|
+
"CATALAN" => 1027,
|
|
23
|
+
"CHINESE_TRADITIONAL" => 1028,
|
|
24
|
+
"CHINESE_SIMPLIFIED" => 2052,
|
|
25
|
+
"CZECH" => 1029,
|
|
26
|
+
"DANISH" => 1030,
|
|
27
|
+
"DUTCH" => 1043,
|
|
28
|
+
"DUTCH_BELGIAN" => 2067,
|
|
29
|
+
"ENGLISH_UK" => 2057,
|
|
30
|
+
"ENGLISH_US" => 1033,
|
|
31
|
+
"FINNISH" => 1035,
|
|
32
|
+
"FRENCH" => 1036,
|
|
33
|
+
"FRENCH_BELGIAN" => 2060,
|
|
34
|
+
"FRENCH_CANADIAN" => 3084,
|
|
35
|
+
"FRENCH_SWISS" => 4108,
|
|
36
|
+
"GERMAN" => 1031,
|
|
37
|
+
"GERMAN_SWISS" => 2055,
|
|
38
|
+
"GREEK" => 1032,
|
|
39
|
+
"HEBREW" => 1037,
|
|
40
|
+
"HUNGARIAN" => 1038,
|
|
41
|
+
"ICELANDIC" => 1039,
|
|
42
|
+
"INDONESIAN" => 1057,
|
|
43
|
+
"ITALIAN" => 1040,
|
|
44
|
+
"JAPANESE" => 1041,
|
|
45
|
+
"KOREAN" => 1042,
|
|
46
|
+
"NORWEGIAN_BOKMAL" => 1044,
|
|
47
|
+
"NORWEGIAN_NYNORSK" => 2068,
|
|
48
|
+
"POLISH" => 1045,
|
|
49
|
+
"PORTUGUESE" => 2070,
|
|
50
|
+
"POTUGUESE_BRAZILIAN" => 1046,
|
|
51
|
+
"ROMANIAN" => 1048,
|
|
52
|
+
"RUSSIAN" => 1049,
|
|
53
|
+
"SERBO_CROATIAN_CYRILLIC" => 2074,
|
|
54
|
+
"SERBO_CROATIAN_LATIN" => 1050,
|
|
55
|
+
"SLOVAK" => 1051,
|
|
56
|
+
"SPANISH_CASTILLIAN" => 1034,
|
|
57
|
+
"SPANISH_MEXICAN" => 2058,
|
|
58
|
+
"SWAHILI" => 1089,
|
|
59
|
+
"SWEDISH" => 1053,
|
|
60
|
+
"THAI" => 1054,
|
|
61
|
+
"TURKISH" => 1055,
|
|
62
|
+
"UNKNOWN" => 1024,
|
|
63
|
+
"VIETNAMESE" => 1066
|
|
64
|
+
}.freeze
|
|
65
|
+
|
|
66
|
+
# Attribute accessor.
|
|
67
|
+
attr_reader :fonts, :lists, :colours, :information, :character_set,
|
|
68
|
+
:language, :properties, :stylesheet
|
|
69
|
+
|
|
70
|
+
# Attribute mutator.
|
|
71
|
+
attr_writer :character_set, :language, :stylesheet
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Represents an entire RTF document.
|
|
75
|
+
# @note The "suppress_system_styles" option is ignored by most RTF platforms including Word and LibreOffice.
|
|
76
|
+
# @see DocumentProperties#initialize DocumentProperties#initialize for available document properties.
|
|
77
|
+
# @see Stylesheet#add_style Stylesheet#initialize for available stylesheet options.
|
|
78
|
+
#
|
|
79
|
+
# @param [Hash<String, Object>] options the options to use in creating the document.
|
|
80
|
+
# @option options [String, Font] "default_font" ("SWISS:Helvetica") a font object OR string encapsulating the default font to be used by the document (see {Font.from_string} for string format).
|
|
81
|
+
# @option options [String] "character_set" ("ANSI") the character set to be applied to the document (see {CS_DICTIONARY} for valid values).
|
|
82
|
+
# @option options [String] "language" ("ENGLISH_US") the language setting to be applied to the document (see {LS_DICTIONARY} for valid values).
|
|
83
|
+
# @option options [Boolean] "suppress_system_styles" (false) whether or not to suppress styles provided in the host platform (adds the \noqfpromote control word before stylesheet definition).
|
|
84
|
+
# @option options [DocumentProperties] "document_properties" (DocumentProperties.new) a DocumentProperties object OR options hash encapsulating the properties to be applied to the document.
|
|
85
|
+
# @option options [Array, Hash, Stylesheet] "stylesheet" (nil) a Stylesheet object OR array of style hashes OR hash of stylesheet options with which to use as or construct the stylesheet for the document.
|
|
86
|
+
def initialize(options = {})
|
|
87
|
+
# load default options
|
|
88
|
+
options = {
|
|
89
|
+
"default_font" => "SWISS:Helvetica",
|
|
90
|
+
"document_properties" => DocumentProperties.new,
|
|
91
|
+
"character_set" => "ANSI",
|
|
92
|
+
"language" => "ENGLISH_US",
|
|
93
|
+
"suppress_system_styles" => false,
|
|
94
|
+
"stylesheet" => nil
|
|
95
|
+
}.merge(options)
|
|
96
|
+
|
|
97
|
+
super(nil, '\rtf1')
|
|
98
|
+
|
|
99
|
+
# parse font
|
|
100
|
+
font = options.delete("default_font")
|
|
101
|
+
case font
|
|
102
|
+
when Font
|
|
103
|
+
when String
|
|
104
|
+
font = Font.from_string(font)
|
|
105
|
+
else
|
|
106
|
+
RTFError.fire("Unreconized font format #{font.class.to_s}")
|
|
107
|
+
end # case
|
|
108
|
+
|
|
109
|
+
# parse document properties
|
|
110
|
+
properties = options.delete("document_properties")
|
|
111
|
+
case properties
|
|
112
|
+
when DocumentProperties
|
|
113
|
+
when Hash
|
|
114
|
+
properties = DocumentProperties.new(properties)
|
|
115
|
+
else
|
|
116
|
+
RTFError.fire("Unreconized document style format #{font.class.to_s}")
|
|
117
|
+
end # case
|
|
118
|
+
|
|
119
|
+
# parse character set
|
|
120
|
+
cs_string = options.delete("character_set")
|
|
121
|
+
cs_val = CS_DICTIONARY[cs_string]
|
|
122
|
+
if cs_val.nil?
|
|
123
|
+
RTFError.fire("Unreconized character set '#{cs_string}'.")
|
|
124
|
+
end # if
|
|
125
|
+
|
|
126
|
+
# parse language setting
|
|
127
|
+
ls_string = options.delete("language")
|
|
128
|
+
ls_val = LS_DICTIONARY[ls_string]
|
|
129
|
+
if ls_val.nil?
|
|
130
|
+
RTFError.fire("Unreconized language '#{ls_string}'.")
|
|
131
|
+
end # if
|
|
132
|
+
|
|
133
|
+
@fonts = FontTable.new(font)
|
|
134
|
+
@lists = ListTable.new
|
|
135
|
+
@default_font = 0
|
|
136
|
+
@colours = ColourTable.new
|
|
137
|
+
@information = Information.new
|
|
138
|
+
@character_set = cs_val
|
|
139
|
+
@language = ls_val
|
|
140
|
+
@properties = properties
|
|
141
|
+
@headers = [nil, nil, nil, nil]
|
|
142
|
+
@footers = [nil, nil, nil, nil]
|
|
143
|
+
@id = 0
|
|
144
|
+
|
|
145
|
+
# parse stylesheet (must be done after font and colour tables are
|
|
146
|
+
# initialized since declared styles may push fonts/colours onto the
|
|
147
|
+
# tables)
|
|
148
|
+
stylesheet = options.delete("stylesheet")
|
|
149
|
+
case stylesheet
|
|
150
|
+
when Stylesheet
|
|
151
|
+
stylesheet.document = self
|
|
152
|
+
when Array
|
|
153
|
+
stylesheet = Stylesheet.new(self, "styles" => stylesheet)
|
|
154
|
+
when Hash
|
|
155
|
+
stylesheet = Stylesheet.new(self, stylesheet)
|
|
156
|
+
else
|
|
157
|
+
RTFError.fire("Unreconized stylesheet format #{font.class.to_s}")
|
|
158
|
+
end unless stylesheet.nil? # case
|
|
159
|
+
|
|
160
|
+
@stylesheet = stylesheet
|
|
161
|
+
# additional options
|
|
162
|
+
@options = options
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# This method provides a method that can be called to generate an
|
|
166
|
+
# identifier that is unique within the document.
|
|
167
|
+
def get_id
|
|
168
|
+
@id += 1
|
|
169
|
+
Time.now().strftime('%d%m%y') + @id.to_s
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Attribute accessor.
|
|
173
|
+
def default_font
|
|
174
|
+
@fonts[@default_font]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# This method assigns a new header to a document. A Document object can
|
|
178
|
+
# have up to four header - a default header, a header for left pages, a
|
|
179
|
+
# header for right pages and a header for the first page. The method
|
|
180
|
+
# checks the header type and stores it appropriately.
|
|
181
|
+
#
|
|
182
|
+
# ==== Parameters
|
|
183
|
+
# header:: A reference to the header object to be stored. Existing header
|
|
184
|
+
# objects are overwritten.
|
|
185
|
+
def header=(header)
|
|
186
|
+
if header.type == HeaderNode::UNIVERSAL
|
|
187
|
+
@headers[0] = header
|
|
188
|
+
elsif header.type == HeaderNode::LEFT_PAGE
|
|
189
|
+
@headers[1] = header
|
|
190
|
+
elsif header.type == HeaderNode::RIGHT_PAGE
|
|
191
|
+
@headers[2] = header
|
|
192
|
+
elsif header.type == HeaderNode::FIRST_PAGE
|
|
193
|
+
@headers[3] = header
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# This method assigns a new footer to a document. A Document object can
|
|
198
|
+
# have up to four footers - a default footer, a footer for left pages, a
|
|
199
|
+
# footer for right pages and a footer for the first page. The method
|
|
200
|
+
# checks the footer type and stores it appropriately.
|
|
201
|
+
#
|
|
202
|
+
# ==== Parameters
|
|
203
|
+
# footer:: A reference to the footer object to be stored. Existing footer
|
|
204
|
+
# objects are overwritten.
|
|
205
|
+
def footer=(footer)
|
|
206
|
+
if footer.type == FooterNode::UNIVERSAL
|
|
207
|
+
@footers[0] = footer
|
|
208
|
+
elsif footer.type == FooterNode::LEFT_PAGE
|
|
209
|
+
@footers[1] = footer
|
|
210
|
+
elsif footer.type == FooterNode::RIGHT_PAGE
|
|
211
|
+
@footers[2] = footer
|
|
212
|
+
elsif footer.type == FooterNode::FIRST_PAGE
|
|
213
|
+
@footers[3] = footer
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# This method fetches a header from a Document object.
|
|
218
|
+
#
|
|
219
|
+
# ==== Parameters
|
|
220
|
+
# type:: One of the header types defined in the header class. Defaults to
|
|
221
|
+
# HeaderNode::UNIVERSAL.
|
|
222
|
+
def header(type=HeaderNode::UNIVERSAL)
|
|
223
|
+
index = 0
|
|
224
|
+
if type == HeaderNode::LEFT_PAGE
|
|
225
|
+
index = 1
|
|
226
|
+
elsif type == HeaderNode::RIGHT_PAGE
|
|
227
|
+
index = 2
|
|
228
|
+
elsif type == HeaderNode::FIRST_PAGE
|
|
229
|
+
index = 3
|
|
230
|
+
end
|
|
231
|
+
@headers[index]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# This method fetches a footer from a Document object.
|
|
235
|
+
#
|
|
236
|
+
# ==== Parameters
|
|
237
|
+
# type:: One of the footer types defined in the footer class. Defaults to
|
|
238
|
+
# FooterNode::UNIVERSAL.
|
|
239
|
+
def footer(type=FooterNode::UNIVERSAL)
|
|
240
|
+
index = 0
|
|
241
|
+
if type == FooterNode::LEFT_PAGE
|
|
242
|
+
index = 1
|
|
243
|
+
elsif type == FooterNode::RIGHT_PAGE
|
|
244
|
+
index = 2
|
|
245
|
+
elsif type == FooterNode::FIRST_PAGE
|
|
246
|
+
index = 3
|
|
247
|
+
end
|
|
248
|
+
@footers[index]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Loads a stylesheet for the document from an array of hashmaps
|
|
252
|
+
# representing styles
|
|
253
|
+
def load_stylesheet(hashmap_array)
|
|
254
|
+
@stylesheet = Stylesheet.new(self, hashmap_array)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Attribute mutator.
|
|
258
|
+
#
|
|
259
|
+
# ==== Parameters
|
|
260
|
+
# font:: The new default font for the Document object.
|
|
261
|
+
def default_font=(font)
|
|
262
|
+
@fonts << font
|
|
263
|
+
@default_font = @fonts.index(font)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# This method provides a short cut for obtaining the Paper object
|
|
267
|
+
# associated with a Document object.
|
|
268
|
+
def paper
|
|
269
|
+
@style.paper
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# This method overrides the parent=() method inherited from the
|
|
273
|
+
# CommandNode class to disallow setting a parent on a Document object.
|
|
274
|
+
#
|
|
275
|
+
# ==== Parameters
|
|
276
|
+
# parent:: A reference to the new parent node for the Document object.
|
|
277
|
+
#
|
|
278
|
+
# ==== Exceptions
|
|
279
|
+
# RTFError:: Generated whenever this method is called.
|
|
280
|
+
def parent=(parent)
|
|
281
|
+
RTFError.fire("Document objects may not have a parent.")
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# This method inserts a page break into a document.
|
|
285
|
+
def page_break
|
|
286
|
+
self.store(CommandNode.new(self, '\page', nil, false))
|
|
287
|
+
nil
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# This method fetches the width of the available work area space for a
|
|
291
|
+
# typical Document object page.
|
|
292
|
+
def body_width
|
|
293
|
+
@style.body_width
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# This method fetches the height of the available work area space for a
|
|
297
|
+
# a typical Document object page.
|
|
298
|
+
def body_height
|
|
299
|
+
@style.body_height
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# This method generates the RTF text for a Document object.
|
|
303
|
+
def to_rtf
|
|
304
|
+
text = StringIO.new
|
|
305
|
+
|
|
306
|
+
text << "{#{prefix}\\#{@character_set.id2name}"
|
|
307
|
+
text << "\\deff#{@default_font}"
|
|
308
|
+
text << "\\deflang#{@language}" if !@language.nil?
|
|
309
|
+
text << "\\plain\\fs24\\fet1"
|
|
310
|
+
text << "\n#{@fonts.to_rtf}"
|
|
311
|
+
text << "\n#{@colours.to_rtf}" if @colours.size > 0
|
|
312
|
+
text << "\n\\noqfpromote" if @options["suppress_system_styles"]
|
|
313
|
+
text << "\n#{@stylesheet.to_rtf}" if !@stylesheet.nil?
|
|
314
|
+
text << "\n#{@information.to_rtf}"
|
|
315
|
+
text << "\n#{@lists.to_rtf}"
|
|
316
|
+
if @headers.compact != []
|
|
317
|
+
text << "\n#{@headers[3].to_rtf}" if !@headers[3].nil?
|
|
318
|
+
text << "\n#{@headers[2].to_rtf}" if !@headers[2].nil?
|
|
319
|
+
text << "\n#{@headers[1].to_rtf}" if !@headers[1].nil?
|
|
320
|
+
if @headers[1].nil? or @headers[2].nil?
|
|
321
|
+
text << "\n#{@headers[0].to_rtf}"
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
if @footers.compact != []
|
|
325
|
+
text << "\n#{@footers[3].to_rtf}" if !@footers[3].nil?
|
|
326
|
+
text << "\n#{@footers[2].to_rtf}" if !@footers[2].nil?
|
|
327
|
+
text << "\n#{@footers[1].to_rtf}" if !@footers[1].nil?
|
|
328
|
+
if @footers[1].nil? or @footers[2].nil?
|
|
329
|
+
text << "\n#{@footers[0].to_rtf}"
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
text << "\n#{@properties.to_rtf}" if !@properties.nil?
|
|
333
|
+
self.each {|entry| text << "\n#{entry.to_rtf}"}
|
|
334
|
+
text << "\n}"
|
|
335
|
+
|
|
336
|
+
text.string
|
|
337
|
+
end
|
|
338
|
+
end # End of the Document class.
|
|
339
|
+
end # module RRTF
|