panmind-rtf 0.3.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.
Files changed (48) hide show
  1. data/CHANGES +8 -0
  2. data/LICENSE +20 -0
  3. data/README +186 -0
  4. data/Rakefile +48 -0
  5. data/VERSION.yml +5 -0
  6. data/examples/example01.rb +51 -0
  7. data/examples/example02.rb +45 -0
  8. data/examples/example03.rb +66 -0
  9. data/examples/example03.rtf +164 -0
  10. data/examples/example04.rb +50 -0
  11. data/examples/rubyrtf.bmp +0 -0
  12. data/examples/rubyrtf.jpg +0 -0
  13. data/examples/rubyrtf.png +0 -0
  14. data/lib/rtf.rb +34 -0
  15. data/lib/rtf/colour.rb +173 -0
  16. data/lib/rtf/font.rb +173 -0
  17. data/lib/rtf/information.rb +111 -0
  18. data/lib/rtf/node.rb +1680 -0
  19. data/lib/rtf/paper.rb +55 -0
  20. data/lib/rtf/style.rb +305 -0
  21. data/test/character_style_test.rb +136 -0
  22. data/test/colour_table_test.rb +93 -0
  23. data/test/colour_test.rb +116 -0
  24. data/test/command_node_test.rb +219 -0
  25. data/test/container_node_test.rb +64 -0
  26. data/test/document_style_test.rb +79 -0
  27. data/test/document_test.rb +106 -0
  28. data/test/fixtures/bitmap1.bmp +0 -0
  29. data/test/fixtures/bitmap2.bmp +0 -0
  30. data/test/fixtures/jpeg1.jpg +0 -0
  31. data/test/fixtures/jpeg2.jpg +0 -0
  32. data/test/fixtures/png1.png +0 -0
  33. data/test/fixtures/png2.png +0 -0
  34. data/test/font_table_test.rb +91 -0
  35. data/test/font_test.rb +48 -0
  36. data/test/footer_node_test.rb +30 -0
  37. data/test/header_node_test.rb +30 -0
  38. data/test/image_node_test.rb +125 -0
  39. data/test/information_test.rb +127 -0
  40. data/test/node_test.rb +25 -0
  41. data/test/paragraph_style_test.rb +81 -0
  42. data/test/style_test.rb +16 -0
  43. data/test/table_cell_node_test.rb +89 -0
  44. data/test/table_node_test.rb +83 -0
  45. data/test/table_row_node_test.rb +59 -0
  46. data/test/test_helper.rb +13 -0
  47. data/test/text_node_test.rb +50 -0
  48. metadata +133 -0
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'rtf'
5
+
6
+ include RTF
7
+
8
+ IMAGE_FILE = 'rubyrtf.png'
9
+
10
+ begin
11
+ document = Document.new(Font.new(Font::ROMAN, 'Times New Roman'))
12
+
13
+ # Add some text to the document and then add the scaled image.
14
+ document.paragraph do |p|
15
+ p << "This is a simple document that attempts to demonstrate the use "
16
+ p << "of images in a document. A simple image should appear in the page "
17
+ p << "header above, on the right hand side. The same image, scaled to "
18
+ p << "four times its normal size, should appear below this text."
19
+ p.line_break
20
+ end
21
+
22
+ # Add the scaled image.
23
+ image = document.image(IMAGE_FILE)
24
+ image.x_scaling = 400
25
+ image.y_scaling = 400
26
+
27
+ # Add some follow up text.
28
+ document.paragraph do |p|
29
+ p.line_break
30
+ p << "Due to the way images are stored in RTF documents, adding images "
31
+ p << "to a document can result in the document file becoming very large. "
32
+ p << "The Ruby RTF library supports the addition of images in the PNG, "
33
+ p << "JPEG and Windows device independent bitmap formats. A compressed "
34
+ p << "image format (like PNG or JPEG) is preferrable to the plain bitmap "
35
+ p << "format as this will result in a smaller document file."
36
+ end
37
+
38
+ # Add a header to the document.
39
+ style = ParagraphStyle.new
40
+ style.justification = ParagraphStyle::RIGHT_JUSTIFY
41
+ header = HeaderNode.new(document)
42
+ header.paragraph(style) {|n| n.image(IMAGE_FILE)}
43
+ document.header = header
44
+
45
+ # Write the document to a file.
46
+ File.open('example04.rtf', 'w') {|file| file.write(document.to_rtf)}
47
+ rescue => error
48
+ puts "ERROR: #{error.message}"
49
+ error.backtrace.each {|step| puts " #{step}"}
50
+ end
Binary file
Binary file
Binary file
data/lib/rtf.rb ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'stringio'
4
+ require 'rtf/font'
5
+ require 'rtf/colour'
6
+ require 'rtf/style'
7
+ require 'rtf/information'
8
+ require 'rtf/paper'
9
+ require 'rtf/node'
10
+
11
+ # This module encapsulates all the classes and definitions relating to the RTF
12
+ # library.
13
+ module RTF
14
+ # This is the exception class used by the RTF library code to indicate
15
+ # errors.
16
+ class RTFError < StandardError
17
+ # This is the constructor for the RTFError class.
18
+ #
19
+ # ==== Parameters
20
+ # message:: A reference to a string containing the error message for
21
+ # the exception defaults to nil.
22
+ def initialize(message=nil)
23
+ super(message == nil ? 'No error message available.' : message)
24
+ end
25
+
26
+ # This method provides a short cut for raising RTFErrors.
27
+ #
28
+ # ==== Parameters
29
+ # message:: A string containing the exception message. Defaults to nil.
30
+ def RTFError.fire(message=nil)
31
+ raise RTFError.new(message)
32
+ end
33
+ end # End of the RTFError class.
34
+ end # End of the RTF module.
data/lib/rtf/colour.rb ADDED
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'stringio'
4
+
5
+ module RTF
6
+ # This class represents a colour within a RTF document.
7
+ class Colour
8
+ # Attribute accessor.
9
+ attr_reader :red, :green, :blue
10
+
11
+
12
+ # This is the constructor for the Colour class.
13
+ #
14
+ # ==== Parameters
15
+ # red:: The intensity setting for red in the colour. Must be an
16
+ # integer between 0 and 255.
17
+ # green:: The intensity setting for green in the colour. Must be an
18
+ # integer between 0 and 255.
19
+ # blue:: The intensity setting for blue in the colour. Must be an
20
+ # integer between 0 and 255.
21
+ #
22
+ # ==== Exceptions
23
+ # RTFError:: Generated whenever an invalid intensity setting is
24
+ # specified for the red, green or blue values.
25
+ def initialize(red, green, blue)
26
+ if red.kind_of?(Integer) == false || red < 0 || red > 255
27
+ RTFError.fire("Invalid red intensity setting ('#{red}') specified "\
28
+ "for a Colour object.")
29
+ end
30
+ if green.kind_of?(Integer) == false || green < 0 || green > 255
31
+ RTFError.fire("Invalid green intensity setting ('#{green}') "\
32
+ "specified for a Colour object.")
33
+ end
34
+ if blue.kind_of?(Integer) == false || blue < 0 || blue > 255
35
+ RTFError.fire("Invalid blue intensity setting ('#{blue}') "\
36
+ "specified for a Colour object.")
37
+ end
38
+
39
+ @red = red
40
+ @green = green
41
+ @blue = blue
42
+ end
43
+
44
+ # This method overloads the comparison operator for the Colour class.
45
+ #
46
+ # ==== Parameters
47
+ # object:: A reference to the object to be compared with.
48
+ def ==(object)
49
+ object.instance_of?(Colour) &&
50
+ object.red == @red &&
51
+ object.green == @green &&
52
+ object.blue == @blue
53
+ end
54
+
55
+ # This method returns a textual description for a Colour object.
56
+ #
57
+ # ==== Parameters
58
+ # indent:: The number of spaces to prefix to the lines created by the
59
+ # method. Defaults to zero.
60
+ def to_s(indent=0)
61
+ prefix = indent > 0 ? ' ' * indent : ''
62
+ "#{prefix}Colour (#{@red}/#{@green}/#{@blue})"
63
+ end
64
+
65
+ # This method generates the RTF text for a Colour object.
66
+ #
67
+ # ==== Parameters
68
+ # indent:: The number of spaces to prefix to the lines created by the
69
+ # method. Defaults to zero.
70
+ def to_rtf(indent=0)
71
+ prefix = indent > 0 ? ' ' * indent : ''
72
+ "#{prefix}\\red#{@red}\\green#{@green}\\blue#{@blue};"
73
+ end
74
+ end # End of the Colour class.
75
+
76
+
77
+ # This class represents a table of colours used within a RTF document. This
78
+ # class need not be directly instantiated as it will be used internally by,
79
+ # and can be obtained from a Document object.
80
+ class ColourTable
81
+ # This is the constructor for the ColourTable class.
82
+ #
83
+ # ==== Parameters
84
+ # *colours:: An array of zero or more colours that make up the colour
85
+ # table entries.
86
+ def initialize(*colours)
87
+ @colours = []
88
+ colours.each {|colour| add(colour)}
89
+ end
90
+
91
+ # This method fetches a count of the number of colours within a colour
92
+ # table.
93
+ def size
94
+ @colours.size
95
+ end
96
+
97
+ # This method adds a new colour to a ColourTable object. If the colour
98
+ # already exists within the table or is not a Colour object then this
99
+ # method does nothing.
100
+ #
101
+ # ==== Parameters
102
+ # colour:: The colour to be added to the table.
103
+ def add(colour)
104
+ if colour.instance_of?(Colour)
105
+ @colours.push(colour) if @colours.index(colour) == nil
106
+ end
107
+ self
108
+ end
109
+
110
+ # This method iterates over the contents of a ColourTable object. This
111
+ # iteration does not include the implicit default colour entry.
112
+ def each
113
+ if block_given?
114
+ @colours.each {|colour| yield colour}
115
+ end
116
+ end
117
+
118
+ # This method overloads the array dereference operator for the ColourTable
119
+ # class. It is not possible to dereference the implicit default colour
120
+ # using this method. An invalid index will return a nil value.
121
+ #
122
+ # ==== Parameters
123
+ # index:: The index of the colour to be retrieved.
124
+ def [](index)
125
+ @colours[index]
126
+ end
127
+
128
+ # This method retrieves the index of a specified colour within the table.
129
+ # If the colour doesn't exist within the table then nil is returned. It
130
+ # should be noted that the index of a colour will be one more than its
131
+ # order of entry to account for the implicit default colour entry.
132
+ #
133
+ # ==== Parameters
134
+ # colour:: The colour to retrieve the index of.
135
+ def index(colour)
136
+ index = @colours.index(colour)
137
+ index == nil ? index : index + 1
138
+ end
139
+
140
+ # This method generates a textual description for a ColourTable object.
141
+ #
142
+ # ==== Parameters
143
+ # indent:: The number of spaces to prefix to the lines generated by the
144
+ # method. Defaults to zero.
145
+ def to_s(indent=0)
146
+ prefix = indent > 0 ? ' ' * indent : ''
147
+ text = StringIO.new
148
+
149
+ text << "#{prefix}Colour Table (#{@colours.size} colours)"
150
+ @colours.each {|colour| text << "\n#{prefix} #{colour}"}
151
+
152
+ text.string
153
+ end
154
+
155
+ # This method generates the RTF text for a ColourTable object.
156
+ #
157
+ # ==== Parameters
158
+ # indent:: The number of spaces to prefix to the lines generated by the
159
+ # method. Defaults to zero.
160
+ def to_rtf(indent=0)
161
+ prefix = indent > 0 ? ' ' * indent : ''
162
+ text = StringIO.new
163
+
164
+ text << "#{prefix}{\\colortbl\n#{prefix};"
165
+ @colours.each {|colour| text << "\n#{prefix}#{colour.to_rtf}"}
166
+ text << "\n#{prefix}}"
167
+
168
+ text.string
169
+ end
170
+
171
+ alias << add
172
+ end # End of the ColourTable class.
173
+ end # End of the RTF module.
data/lib/rtf/font.rb ADDED
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'stringio'
4
+
5
+ module RTF
6
+ # This class represents a font for use with some RTF content.
7
+ class Font
8
+ # A declaration for a font family. This family is used for monospaced
9
+ # fonts (e.g. Courier New).
10
+ MODERN = :modern
11
+
12
+ # A declaration for a font family. This family is used for proportionally
13
+ # spaced serif fonts (e.g. Arial, Times New Roman).
14
+ ROMAN = :roman
15
+
16
+ # A declaration for a font family. This family is used for proportionally
17
+ # spaced sans serif fonts (e.g. Tahoma, Lucida Sans).
18
+ SWISS = :swiss
19
+
20
+ # A declaration for a font family. This family is used where none of the
21
+ # other families apply.
22
+ NIL = 'nil'.intern
23
+
24
+
25
+ # Attribute accessor.
26
+ attr_reader :family, :name
27
+
28
+
29
+ # This is the constructor for the Font class.
30
+ #
31
+ # ==== Parameters
32
+ # family:: The font family for the new font. This should be one of
33
+ # Font::MODERN, Font::ROMAN, Font::SWISS or
34
+ # Font::NIL.
35
+ # name:: A string containing the font name.
36
+ #
37
+ # ==== Exceptions
38
+ # RTFError:: Generated whenever an invalid font family is specified.
39
+ def initialize(family, name)
40
+ # Check that a valid family has been provided.
41
+ if ![MODERN, ROMAN, SWISS, NIL].include?(family)
42
+ RTFError::fire("Unknown font family specified for Font object.")
43
+ end
44
+ @family = family
45
+ @name = name
46
+ end
47
+
48
+ # This method overloads the equivalence test operator for the Font class
49
+ # to allow for Font comparisons.
50
+ #
51
+ # ==== Parameters
52
+ # object:: A reference to the object to be compared with.
53
+ def ==(object)
54
+ object.instance_of?(Font) &&
55
+ object.family == @family &&
56
+ object.name == @name
57
+ end
58
+
59
+ # This method fetches a textual description for a Font object.
60
+ #
61
+ # ==== Parameters
62
+ # indent:: The number of spaces to prefix to the lines generated by the
63
+ # method. Defaults to zero.
64
+ def to_s(indent=0)
65
+ prefix = indent > 0 ? ' ' * indent : ''
66
+ "#{prefix}Family: #{@family.id2name}, Name: #{@name}"
67
+ end
68
+
69
+ # This method generates the RTF representation for a Font object as it
70
+ # would appear within a document font table.
71
+ #
72
+ # ==== Parameters
73
+ # indent:: The number of spaces to prefix to the lines generated by the
74
+ # method. Defaults to zero.
75
+ def to_rtf(indent=0)
76
+ prefix = indent > 0 ? ' ' * indent : ''
77
+ "#{prefix}\\f#{@family.id2name} #{@name};"
78
+ end
79
+ end # End of the Font class.
80
+
81
+
82
+ # This class represents the font table for an RTF document. An instance of
83
+ # the class is used internally by the Document class and should not need to
84
+ # be explicitly instantiated (although it can be obtained from a Document
85
+ # object if needed).
86
+ class FontTable
87
+ # This is the constructor for the RTFTable class.
88
+ #
89
+ # ==== Parameters
90
+ # *fonts:: Zero or more font objects that are to be added to the font
91
+ # table. Objects that are not Fonts will be ignored.
92
+ def initialize(*fonts)
93
+ @fonts = []
94
+ fonts.each {|font| add(font)}
95
+ end
96
+
97
+ # This method is used to retrieve a count of the number of fonts held
98
+ # within an instance of the FontTable class.
99
+ def size
100
+ @fonts.size
101
+ end
102
+
103
+ # This method adds a font to a FontTable instance. This method returns
104
+ # a reference to the FontTable object updated.
105
+ #
106
+ # ==== Parameters
107
+ # font:: A reference to the font to be added. If this is not a Font
108
+ # object or already exists in the table it will be ignored.
109
+ def add(font)
110
+ if font.instance_of?(Font)
111
+ @fonts.push(font) if @fonts.index(font) == nil
112
+ end
113
+ self
114
+ end
115
+
116
+ # This method iterates over the contents of a FontTable object. This
117
+ # method expects a block that takes a single parameter (the next font
118
+ # from the table).
119
+ def each
120
+ @fonts.each {|font| yield font} if block_given?
121
+ end
122
+
123
+ # This method overloads the array dereference operator for the FontTable
124
+ # class.
125
+ #
126
+ # ==== Parameters
127
+ # index:: The index into the font table of the font to be retrieved. If
128
+ # the index is invalid then nil is returned.
129
+ def [](index)
130
+ @fonts[index]
131
+ end
132
+
133
+ # This method fetches the index of a font within a FontTable object. If
134
+ # the font does not exist in the table then nil is returned.
135
+ #
136
+ # ==== Parameters
137
+ # font:: A reference to the font to check for.
138
+ def index(font)
139
+ @fonts.index(font)
140
+ end
141
+
142
+ # This method generates a textual description for a FontTable object.
143
+ #
144
+ # ==== Parameters
145
+ # indent:: The number of spaces to prefix to the lines generated by the
146
+ # method. Defaults to zero.
147
+ def to_s(indent=0)
148
+ prefix = indent > 0 ? ' ' * indent : ''
149
+ text = StringIO.new
150
+ text << "#{prefix}Font Table (#{@fonts.size} fonts)"
151
+ @fonts.each {|font| text << "\n#{prefix} #{font}"}
152
+ text.string
153
+ end
154
+
155
+ # This method generates the RTF text for a FontTable object.
156
+ #
157
+ # ==== Parameters
158
+ # indent:: The number of spaces to prefix to the lines generated by the
159
+ # method. Defaults to zero.
160
+ def to_rtf(indent=0)
161
+ prefix = indent > 0 ? ' ' * indent : ''
162
+ text = StringIO.new
163
+ text << "#{prefix}{\\fonttbl"
164
+ @fonts.each_index do |index|
165
+ text << "\n#{prefix}{\\f#{index}#{@fonts[index].to_rtf}}"
166
+ end
167
+ text << "\n#{prefix}}"
168
+ text.string
169
+ end
170
+
171
+ alias << add
172
+ end # End of the FontTable class.
173
+ end # End of the RTF module.
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'stringio'
4
+ require 'date'
5
+ module RTF
6
+ # This class represents an information group for a RTF document.
7
+ class Information
8
+ # Attribute accessor.
9
+ attr_reader :title, :author, :company, :created, :comments
10
+
11
+ # Attribute mutator.
12
+ attr_writer :title, :author, :company, :comments
13
+
14
+
15
+ # This is the constructor for the Information class.
16
+ #
17
+ # ==== Parameters
18
+ # title:: A string containing the document title information. Defaults
19
+ # to nil.
20
+ # author:: A string containing the document author information.
21
+ # Defaults to nil.
22
+ # company:: A string containing the company name information. Defaults
23
+ # to nil.
24
+ # comments:: A string containing the information comments. Defaults to
25
+ # nil to indicate no comments.
26
+ # creation:: A Time object or a String that can be parsed into a Time
27
+ # object (using ParseDate) indicating the document creation
28
+ # date and time. Defaults to nil to indicate the current
29
+ # date and time.
30
+ #
31
+ # ==== Exceptions
32
+ # RTFError:: Generated whenever invalid creation date/time details are
33
+ # specified.
34
+ def initialize(title=nil, author=nil, company=nil, comments=nil, creation=nil)
35
+ @title = title
36
+ @author = author
37
+ @company = company
38
+ @comments = comments
39
+ self.created = (creation == nil ? Time.new : creation)
40
+ end
41
+
42
+ # This method provides the created attribute mutator for the Information
43
+ # class.
44
+ #
45
+ # ==== Parameters
46
+ # setting:: The new creation date/time setting for the object. This
47
+ # should be either a Time object or a string containing
48
+ # date/time details that can be parsed into a Time object
49
+ # (using the parsedate method).
50
+ #
51
+ # ==== Exceptions
52
+ # RTFError:: Generated whenever invalid creation date/time details are
53
+ # specified.
54
+ def created=(setting)
55
+ if setting.instance_of?(Time)
56
+ @created = setting
57
+ else
58
+ datetime = Date._parse(setting.to_s).values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday)
59
+ if datetime == nil
60
+ RTFError.fire("Invalid document creation date/time information "\
61
+ "specified.")
62
+ end
63
+ @created = Time.local(datetime[0], datetime[1], datetime[2],
64
+ datetime[3], datetime[4], datetime[5])
65
+ end
66
+ end
67
+
68
+ # This method creates a textual description for an Information object.
69
+ #
70
+ # ==== Parameters
71
+ # indent:: The number of spaces to prefix to the lines generated by the
72
+ # method. Defaults to zero.
73
+ def to_s(indent=0)
74
+ prefix = indent > 0 ? ' ' * indent : ''
75
+ text = StringIO.new
76
+
77
+ text << "#{prefix}Information"
78
+ text << "\n#{prefix} Title: #{@title}" if @title != nil
79
+ text << "\n#{prefix} Author: #{@author}" if @author != nil
80
+ text << "\n#{prefix} Company: #{@company}" if @company != nil
81
+ text << "\n#{prefix} Comments: #{@comments}" if @comments != nil
82
+ text << "\n#{prefix} Created: #{@created}" if @created != nil
83
+
84
+ text.string
85
+ end
86
+
87
+ # This method generates the RTF text for an Information object.
88
+ #
89
+ # ==== Parameters
90
+ # indent:: The number of spaces to prefix to the lines generated by the
91
+ # method. Defaults to zero.
92
+ def to_rtf(indent=0)
93
+ prefix = indent > 0 ? ' ' * indent : ''
94
+ text = StringIO.new
95
+
96
+ text << "#{prefix}{\\info"
97
+ text << "\n#{prefix}{\\title #{@title}}" if @title != nil
98
+ text << "\n#{prefix}{\\author #{@author}}" if @author != nil
99
+ text << "\n#{prefix}{\\company #{@company}}" if @company != nil
100
+ text << "\n#{prefix}{\\doccomm #{@comments}}" if @comments != nil
101
+ if @created != nil
102
+ text << "\n#{prefix}{\\createim\\yr#{@created.year}"
103
+ text << "\\mo#{@created.month}\\dy#{@created.day}"
104
+ text << "\\hr#{@created.hour}\\min#{@created.min}}"
105
+ end
106
+ text << "\n#{prefix}}"
107
+
108
+ text.string
109
+ end
110
+ end # End of the Information class.
111
+ end # End of the RTF module.