Browse files

Merged thechrisoshow branch

  • Loading branch information...
2 parents 9a13c4d + 1f91b59 commit dd9a05ae8d91d67ad22fa8ffb0e6048b59cf1526 @clbustos committed Jan 5, 2012
Showing with 228 additions and 24 deletions.
  1. +1 −0 .gitignore
  2. +187 −0 README.rdoc
  3. +3 −2 README.txt
  4. +10 −0 Rakefile
  5. +0 −5 VERSION.yml
  6. +8 −8 lib/rtf/node.rb
  7. +1 −0 test/test_table_node.rb
  8. +18 −9 test/text_node_test.rb
@@ -10,3 +10,4 @@ coverage
@@ -0,0 +1,187 @@
+== Ruby Rich Text Format (RTF) Library
+The RTF library provides a pure Ruby set of functionality that can be used to
+programmatically create RTF documents. The main aim in developing this library
+is to ease the complexity involved in assembling RTF documents although some
+consideration has also been given to generating documents that are easier to
+manually interpret too.
+This library does not include functionality for parsing RTF documents. Nor does
+the library claim to provide extensive coverage of the RTF specification. The
+library was developed mostly with reference to the RTF Pocket Guide by Sean M.
+Burke and some reference to the RTF specification itself. The introduction to
+the RTF Pocket Guide states that the book covers version 1.7 of the RTF
+specification so I guess, as this was the primary source, that this is the
+version that the library covers too. Finally, no consideration was given to
+making the functionality within the library thread safe.
+In creating this library I set out to make it reasonably easy to create RTF
+documents in code. Having said that I'm certain that it is possible to generate
+invalid RTF documents with this library.
+=== Known Issues
+I've tried to assemble a reasonably extensive (although I won't claim
+exhaustive) unit test for the library. Despite that, this is an early release of
+the code and I'm sure there will be issues with it given the complexity inherent
+in RTF. The following are issues that I'm already aware of with the library...
+* The implementation of headers and footers is incomplete. Stick to using
+ universal headers and footers for the time being.
+* The library makes no attempt to split large chunks of text into separate
+ lines. This can make editing the resulting document in a text editor a little
+ awkward.
+* RTF is, when it comes down to it, a Microsoft standard. As a result I have
+ taken Word and Wordpad to be definitive when it comes to displaying the RTF
+ documents generated by the library. I have tried things like Abiword and
+ Open Office with varying degrees of success. I'm certainly not saying that
+ this is due to deficencies in these particular applications as it could
+ equally be a lack of my understanding of the RTF standard or problems with my
+ implementation. Still, I think I should mention the point that I don't get
+ consistent appearance across all of the RTF viewers I've tried.
+=== To Do
+This section details that areas where I feel the library is currently lacking
+or incomplete. I hope to address the things detailed here in later releases of
+the code.
+* Check into RTF image handling with a view to adding support for the insertion
+ of images into a Document.
+* Provide a complete implementation for the headers and footers.
+=== Some Examples
+Okay, so how is the library used. Well lets look at some examples to see if we
+can cast a little light on the matter. The examples provided here assume that
+you are already familiar with the Ruby language. So, for a start, consider...
+ require 'rubygems'
+ require 'rtf'
+ include RTF
+The first thing to look at here at the are the first two lines. The RTF library
+is provided as a Ruby gem and these two lines load the libraries functionality
+into the script. The third line of code includes the RTF module into the current
+name space. This is a convenience mechanism that saves on specifically having
+to refer to the module when accessing the RTF library. Next we want to create
+an RTF document and that is done like this...
+ document =, 'Times New Roman'))
+This line of code creates a new Document object, specifying that the default
+font for the document will the the Times New Roman font. So we have a document,
+what can we do with it. Well, lets add a short paragraph of text...
+ document.paragraph << "This is a short paragraph of text."
+That's fine, but what if we wanted to extend that paragraph or we simply wanted
+to add more text than we've added here? Well, the paragraph method accepts a
+block to which it passes the actual paragraph object, so we could do something
+like the following...
+ document.paragraph do |p|
+ p << "This is the first sentence in the paragraph. "
+ p << "This is the second sentence in the paragraph. "
+ p << "And this is the third sentence in the paragraph."
+ end
+This is a common approach used by the RTF library, allowing a block to define
+the scope of a document element. Lets see a more complicated example of this
+in which we apply a number of document effects. Lets say that we want to insert
+some code into the document. We want the code to appear in the document slightly
+indented on the left hand side, in a non-proportionately space font and we want
+it in bold text. Heres the code that shows how to do that...
+ 01 styles = {}
+ 02 styles['PS_CODE'] =
+ 03 styles['CS_CODE'] =
+ 04
+ 05 styles['PS_CODE'].left_indent = 200
+ 06 styles['CS_CODE'].font =, 'Courier')
+ 07 styles['CS_CODE'].bold = true
+ 08
+ 09 document.paragraph(styles['PS_CODE']) do |n1|
+ 10 n1.apply(styles['CS_CODE']) do |n2|
+ 11 n2 << "count = 0"
+ 12 n2.line_break
+ 13 n2 << "'file.txt', 'r') do |file|"
+ 14 n2.line_break
+ 15 n2 << " file.each_line {|line| count += 1}"
+ 16 n2.line_break
+ 17 n2 << "end"
+ 18 n2.line_break
+ 19 n2 << "puts \"File contains \#{count} lines.\""
+ 20 end
+ 21 end
+This is a much larger piece of code and covers a number of topics that need to
+be addressed. I have included line numbers with code so that individual elements
+can be referenced. Lines 1 to 3 are the first new elements. Here we create a
+Hash and assign it to a variable called styles. On the next two lines we create
+two style objects, one that can be applied to paragraphs and one that applies
+to characters.
+On lines 5 to 7 we update settings on the style objects we've created. We set
+the left indentation value of the paragraph style to 200. The 200 in this case
+refers to a measurement of twips. A twip is a type setting measurement that
+equates to one twentieth of a point (about a fifty seventh of a millimetre or
+one seventy second of an inch). This is the measurement scale used by RTF
+documents so it is also employed in the library.
+On lines 6 and 7 we update the character style to use a courier font and to
+apply bold styling to the text. On line 9 we start a new paragraph in our
+document. This differs from our previous use of this method in that we specify
+a style that will be applied to the paragraph created, the paragraph style we
+had prepared earlier.
+The block accompanying the paragraph method takes the single parameter that we
+have seen previously. At this point its probably a good idea to point out that
+the elements that make up an RTF document created with the library are all
+stored as nodes in a tree. We've named the one passed to the paragraph method as
+n1 to reflect this.
+Within the block we've called a method on the paragraph node called apply. This
+method applies a character style and we're using the one we prepared earlier.
+Like the call to the paragraph method, the apply method is passed a block. All
+text added to the blocks node (n2 in this case) will have the styling we've
+defined (bold courier font) applied to it.
+Note, that within the apply block we could still use the n1 node. Any text we
+added to this would appear in the paragraph but wouldn't be styled and, it
+should be noted, will appear before any text added to n2 (as the n2 node only
+becomes part of the document when the apply block completes).
+Within the apply method block we add some lines of text to the n2 node. Note
+that, as this is all encompassed within the parapgraph block, all the text is
+part of a single paragraph. To get each of the lines of code to appear on a
+line of their own we have used the line_break method which inserts a carriage
+return into the document. Note you should use this method to insert line breaks
+rather than trying to add a string containing "\n". RTF is a text based standard
+and won't treat "\n" as you're expecting. You should note also that we've had to
+escape the '#' in one of the code lines to stop Ruby considering it as an
+interpolated value.
+Okay, so we've seen have the basics of creating a document and adding elements
+to that document. How do we get what we've created to a file. Well thats
+actually quite straight forward. As was mentioned previously, RTF is a text
+based standard so you simply generate the RTF and write it to a file. Heres an
+'my_document.rtf') {|file| file.write(document.to_rtf)}
+There you have it. You've been given a quick overview of the basics of using
+the library. For more information consult the HTML based API documentation that
+is installed with the library gem (if you're reading this you may already be
+looking at this documentation). Another source of information is the examples
+directory, so check that out too.
+* Marcello Barnaba
+* Claudio Bustos
+* Sam Mullen
+* Chris O'Sullivan
+Copyright (c) 2009-2012 Peter Wood. See LICENSE for details.
@@ -191,9 +191,10 @@ directory, so check that out too.
* Marcello Barnaba
* Claudio Bustos
+* Sam Mullen
* Chris O'Sullivan
-Copyright (c) 2009-2010 Peter Wood. See LICENSE for details.
+Copyright (c) 2009-2012 Peter Wood. See LICENSE for details.
@@ -22,3 +22,13 @@ h=Hoe.spec 'clbustos-rtf' do
self.rdoc_locations << remote_dir
self.extra_dev_deps << ["hoe",">=0"]
+ require 'rcov/rcovtask'
+ do |t|
+ t.libs << 'test'
+ t.test_files = FileList['test/**/*_test.rb']
+ t.verbose = true
+ end
+rescue LoadError
+ puts "RCov is not available. In order to run rcov, you must: sudo gem install rcov"
@@ -1,5 +0,0 @@
-:major: 0
-:minor: 4
-:patch: 2
@@ -115,12 +115,12 @@ def insert(text, offset)
# method escapes any special sequences that appear in the text.
def to_rtf
rtf=(@text == nil ? '' : @text.gsub("{", "\\{").gsub("}", "\\}").gsub("\\", "\\\\"))
- # This is from lfarcy / rtf-extensions
+ # This is from lfarcy / rtf-extensions
# I don't see the point of coding different 128<n<256 range
#f1=lambda { |n| n < 128 ? n.chr : n < 256 ? "\\'#{n.to_s(16)}" : "\\u#{n}\\'3f" }
# Encode as Unicode.
f=lambda { |n| n < 128 ? n.chr : "\\u#{n}\\'3f" }
# Ruby 1.9 is safe, cause detect original encoding
# and convert text to utf-16 first
@@ -708,7 +708,7 @@ def initialize(parent, url)
class TableNode < ContainerNode
# Cell margin. Default to 100
attr_accessor :cell_margin
# This is a constructor for the TableNode class.
# ==== Parameters
@@ -727,7 +727,7 @@ def initialize(parent, *args, &block)
rows.times {entries.push(, columns, *widths))}
elsif block
block.arity<1 ? self.instance_eval(&block) :
@@ -829,7 +829,7 @@ def to_rtf
text << row.to_rtf
- text.string
+ text.string.sub(/\\row(?!.*\\row)/m, "\\lastrow\n\\row")
alias :column_shading_color :column_shading_colour
@@ -940,7 +940,7 @@ class TableCellNode < CommandNode
attr_accessor :width
# Attribute accessor.
attr_reader :shading_colour, :style
# This is the constructor for the TableCellNode class.
# ==== Parameters
@@ -1438,7 +1438,7 @@ def read_source(size=nil)
if size > 0
total = 0
while @source.eof? == false && total < size
@read << @source.getbyte
total += 1
@@ -19,6 +19,7 @@ def test_basics
assert(table.columns == 5)
assert(table.size == 3)
assert(table.cell_margin == 100)
+ assert_match(/\\lastrow\n\\row$/, table.to_rtf)
def test_mutators
@@ -6,41 +6,41 @@ class TextNodeTest < Test::Unit::TestCase
def setup
@node =
def test01
nodes = []
nodes.push(, 'Node 2'))
nodes.push(, ''))
- assert(nodes[0].text == nil)
- assert(nodes[1].text == 'Node 2')
- assert(nodes[2].text == nil)
+ assert(nodes[0].text == nil)
+ assert(nodes[1].text == 'Node 2')
+ assert(nodes[2].text == nil)
assert(nodes[3].text == '')
nodes[0].text = 'This is the altered text for node 1.'
assert(nodes[0].text == 'This is the altered text for node 1.')
nodes[1].append('La la la')
nodes[2].append('La la la')
assert(nodes[1].text == 'Node 2La la la')
assert(nodes[2].text == 'La la la')
nodes[2].text = nil
nodes[1].insert(' - ', 6)
nodes[2].insert('TEXT', 2)
assert(nodes[1].text == 'Node 2 - La la la')
assert(nodes[2].text == 'TEXT')
nodes[2].text = nil
nodes[3].text = '{\}'
assert(nodes[0].to_rtf == 'This is the altered text for node 1.')
assert(nodes[1].to_rtf == 'Node 2 - La la la')
assert(nodes[2].to_rtf == '')
assert(nodes[3].to_rtf == '\{\\\}')
def test02
@@ -55,12 +55,21 @@ def test_utf8
assert_equal("ASCCI", nodes[0].to_rtf)
+<<<<<<< HEAD
+>>>>>>> 1f91b59bae2b26554f7d1d8e8b1b20d9add52b3d
assert_equal(exp, nodes[0].to_rtf)
+<<<<<<< HEAD
+>>>>>>> 1f91b59bae2b26554f7d1d8e8b1b20d9add52b3d

0 comments on commit dd9a05a

Please sign in to comment.