panmind-rtf 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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.