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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.byebug_history +6 -3
  3. data/CHANGELOG.md +24 -0
  4. data/README.md +194 -84
  5. data/documentation/RRTF.html +5 -5
  6. data/documentation/RRTF/AnonymousStyle.html +792 -0
  7. data/documentation/RRTF/BorderFormatting.html +821 -0
  8. data/documentation/RRTF/BorderStyle.html +493 -0
  9. data/documentation/RRTF/CharacterFormatting.html +293 -162
  10. data/documentation/RRTF/CharacterStyle.html +53 -109
  11. data/documentation/RRTF/Colour.html +61 -1
  12. data/documentation/RRTF/ColourTable.html +52 -52
  13. data/documentation/RRTF/CommandNode.html +367 -971
  14. data/documentation/RRTF/ContainerNode.html +44 -44
  15. data/documentation/RRTF/Converters.html +1 -1
  16. data/documentation/RRTF/Converters/HTML.html +1 -1
  17. data/documentation/RRTF/Converters/HTML/Helpers.html +1 -1
  18. data/documentation/RRTF/Converters/HTML/Node.html +1 -1
  19. data/documentation/RRTF/Converters/HTML/NodeSet.html +1 -1
  20. data/documentation/RRTF/Document.html +267 -255
  21. data/documentation/RRTF/DocumentFormatting.html +833 -0
  22. data/documentation/RRTF/DocumentProperties.html +444 -0
  23. data/documentation/RRTF/Font.html +1 -1
  24. data/documentation/RRTF/FontTable.html +1 -1
  25. data/documentation/RRTF/FooterNode.html +16 -16
  26. data/documentation/RRTF/GeometryNode.html +774 -0
  27. data/documentation/RRTF/GeometryProperties.html +1014 -0
  28. data/documentation/RRTF/HeaderNode.html +16 -16
  29. data/documentation/RRTF/ImageNode.html +705 -492
  30. data/documentation/RRTF/Information.html +1 -1
  31. data/documentation/RRTF/LinkNode.html +10 -10
  32. data/documentation/RRTF/ListLevel.html +1 -1
  33. data/documentation/RRTF/ListLevelNode.html +37 -37
  34. data/documentation/RRTF/ListMarker.html +1 -1
  35. data/documentation/RRTF/ListNode.html +19 -19
  36. data/documentation/RRTF/ListTable.html +1 -1
  37. data/documentation/RRTF/ListTemplate.html +1 -1
  38. data/documentation/RRTF/ListTextNode.html +14 -14
  39. data/documentation/RRTF/Node.html +26 -26
  40. data/documentation/RRTF/Page.html +129 -0
  41. data/documentation/RRTF/Page/Margin.html +1158 -0
  42. data/documentation/RRTF/Page/Size.html +946 -0
  43. data/documentation/RRTF/PageFormatting.html +954 -0
  44. data/documentation/RRTF/ParagraphFormatting.html +338 -56
  45. data/documentation/RRTF/ParagraphNode.html +10 -10
  46. data/documentation/RRTF/ParagraphStyle.html +72 -111
  47. data/documentation/RRTF/PositionFormatting.html +780 -0
  48. data/documentation/RRTF/PositionStyle.html +424 -0
  49. data/documentation/RRTF/Properties.html +243 -0
  50. data/documentation/RRTF/RTFError.html +21 -10
  51. data/documentation/RRTF/ShadingFormatting.html +712 -0
  52. data/documentation/RRTF/ShadingStyle.html +424 -0
  53. data/documentation/RRTF/Style.html +284 -697
  54. data/documentation/RRTF/Stylesheet.html +36 -3
  55. data/documentation/RRTF/TableCellNode.html +131 -131
  56. data/documentation/RRTF/TableNode.html +82 -82
  57. data/documentation/RRTF/TableRowNode.html +53 -53
  58. data/documentation/RRTF/TextNode.html +46 -46
  59. data/documentation/RRTF/Utilities.html +837 -17
  60. data/documentation/_index.html +139 -6
  61. data/documentation/class_list.html +1 -1
  62. data/documentation/file.README.html +218 -87
  63. data/documentation/index.html +218 -87
  64. data/documentation/method_list.html +631 -391
  65. data/documentation/top-level-namespace.html +1 -1
  66. data/examples/01.rtf +947 -20
  67. data/examples/01_everything.rb +176 -0
  68. data/examples/02.rtf +13 -0
  69. data/examples/02_basic_paragraph.rb +10 -0
  70. data/examples/03.rtf +20 -0
  71. data/examples/03_paragraph_inline_style.rb +14 -0
  72. data/examples/04.rtf +21 -0
  73. data/examples/04_paragraph_with_character_style.rb +18 -0
  74. data/examples/05.rtf +21 -0
  75. data/examples/05_hyperlinks.rb +21 -0
  76. data/examples/06.rtf +21 -0
  77. data/examples/06_basic_list.rb +21 -0
  78. data/examples/07.rtf +28 -0
  79. data/examples/07_nested_list.rb +27 -0
  80. data/examples/08.rtf +807 -0
  81. data/examples/08_images.rb +17 -0
  82. data/examples/09.rtf +84 -0
  83. data/examples/09_shapes.rb +56 -0
  84. data/examples/10.rtf +34 -0
  85. data/examples/10_stylesheet.rb +18 -0
  86. data/examples/resources/images/redshirt.png +0 -0
  87. data/examples/resources/images/redshirts.jpg +0 -0
  88. data/examples/resources/json/redshirt_styles.json +72 -8
  89. data/examples/~$01.rtf +0 -0
  90. data/lib/rrtf.rb +4 -16
  91. data/lib/rrtf/colour.rb +8 -0
  92. data/lib/rrtf/formatting.rb +988 -0
  93. data/lib/rrtf/node.rb +17 -1851
  94. data/lib/rrtf/node/command_node.rb +242 -0
  95. data/lib/rrtf/node/container_node.rb +75 -0
  96. data/lib/rrtf/node/document.rb +339 -0
  97. data/lib/rrtf/node/footer_node.rb +47 -0
  98. data/lib/rrtf/node/geometry_node.rb +65 -0
  99. data/lib/rrtf/node/header_node.rb +47 -0
  100. data/lib/rrtf/node/image_node.rb +175 -0
  101. data/lib/rrtf/node/link_node.rb +10 -0
  102. data/lib/rrtf/node/list_level_node.rb +44 -0
  103. data/lib/rrtf/node/list_node.rb +30 -0
  104. data/lib/rrtf/node/list_text_node.rb +22 -0
  105. data/lib/rrtf/node/node.rb +53 -0
  106. data/lib/rrtf/node/paragraph_node.rb +11 -0
  107. data/lib/rrtf/node/table_cell_node.rb +233 -0
  108. data/lib/rrtf/node/table_node.rb +136 -0
  109. data/lib/rrtf/node/table_row_node.rb +92 -0
  110. data/lib/rrtf/node/text_node.rb +76 -0
  111. data/lib/rrtf/page.rb +7 -0
  112. data/lib/rrtf/page/margin.rb +98 -0
  113. data/lib/rrtf/page/size.rb +98 -0
  114. data/lib/rrtf/properties.rb +3 -0
  115. data/lib/rrtf/properties/document_properties.rb +34 -0
  116. data/lib/rrtf/properties/geometry_properties.rb +380 -0
  117. data/lib/rrtf/properties/properties.rb +13 -0
  118. data/lib/rrtf/style.rb +4 -5
  119. data/lib/rrtf/style/anonymous_style.rb +73 -0
  120. data/lib/rrtf/style/border_style.rb +27 -0
  121. data/lib/rrtf/style/character_style.rb +1 -7
  122. data/lib/rrtf/style/paragraph_style.rb +0 -6
  123. data/lib/rrtf/style/position_style.rb +26 -0
  124. data/lib/rrtf/style/shading_style.rb +26 -0
  125. data/lib/rrtf/style/style.rb +60 -101
  126. data/lib/rrtf/utilities.rb +138 -0
  127. data/lib/rrtf/version.rb +1 -1
  128. data/rrtf.gemspec +1 -0
  129. metadata +85 -10
  130. data/examples/01_mac_libreoffice5_2_3_3.png +0 -0
  131. data/examples/01_mac_pages6_2.png +0 -0
  132. data/examples/01_mac_textedit1_12.png +0 -0
  133. data/examples/01_mac_word15_36.png +0 -0
  134. data/examples/01_styles_and_paragraphs.rb +0 -32
  135. data/lib/rrtf/paper.rb +0 -53
  136. data/lib/rrtf/style/document_style.rb +0 -116
  137. 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