lib/rtf/node.rb in clbustos-rtf-0.3.1 vs lib/rtf/node.rb in clbustos-rtf-0.4.2
- old
+ new
@@ -118,10 +118,12 @@
# Encode as Unicode.
if RUBY_VERSION>"1.9.0"
rtf.encode("UTF-16LE").each_codepoint.map {|cp|
cp < 128 ? cp.chr : "\\u#{cp}\\'3f"
}.join("")
+ else
+ rtf
end
end
end # End of the TextNode class.
@@ -210,10 +212,13 @@
attr_accessor :suffix
# A boolean to indicate whether the prefix and suffix should
# be written to separate lines whether the node is converted
# to RTF. Defaults to true
attr_accessor :split
+ # A boolean to indicate whether the prefix and suffix should
+ # be wrapped in curly braces. Defaults to true.
+ attr_accessor :wrap
# This is the constructor for the CommandNode class.
#
# ==== Parameters
# parent:: A reference to the node that owns the new node.
@@ -221,15 +226,18 @@
# suffix:: A String containing the suffix text for the command. Defaults
# to nil.
# split:: A boolean to indicate whether the prefix and suffix should
# be written to separate lines whether the node is converted
# to RTF. Defaults to true.
- def initialize(parent, prefix, suffix=nil, split=true)
+ # wrap:: A boolean to indicate whether the prefix and suffix should
+ # be wrapped in curly braces. Defaults to true.
+ def initialize(parent, prefix, suffix=nil, split=true, wrap=true)
super(parent)
@prefix = prefix
@suffix = suffix
@split = split
+ @wrap = wrap
end
# This method adds text to a command node. If the last child node of the
# target node is a TextNode then the text is appended to that. Otherwise
# a new TextNode is created and append to the node.
@@ -244,23 +252,24 @@
end
end
# This method generates the RTF text for a CommandNode object.
def to_rtf
- text = StringIO.new
- separator = split? ? "\n" : " "
- line = (separator == " ")
+ text = StringIO.new
- text << "{#{@prefix}"
- text << separator if self.size > 0
+ text << '{' if wrap?
+ text << @prefix if @prefix
+
self.each do |entry|
- text << "\n" if line
- line = true
- text << "#{entry.to_rtf}"
+ text << "\n" if split?
+ text << entry.to_rtf
end
- text << "\n" if split?
- text << "#{@suffix}}"
+
+ text << "\n" if split?
+ text << @suffix if @suffix
+ text << '}' if wrap?
+
text.string
end
# This method provides a short cut means of creating a paragraph command
# node. The method accepts a block that will be passed a single parameter
@@ -271,20 +280,47 @@
# ==== Parameters
# style:: A reference to a ParagraphStyle object that defines the style
# for the new paragraph. Defaults to nil to indicate that the
# currently applied paragraph styling should be used.
def paragraph(style=nil)
- # Create the node prefix.
- text = StringIO.new
- text << '\pard'
- text << style.prefix(nil, nil) if style != nil
-
- node = CommandNode.new(self, text.string, '\par')
+ node = ParagraphNode.new(self, style)
yield node if block_given?
self.store(node)
end
+ # This method provides a short cut means of creating a new ordered or
+ # unordered list. The method requires a block that will be passed a
+ # single parameter that'll be a reference to the first level of the
+ # list. See the +ListLevelNode+ doc for more information.
+ #
+ # Example usage:
+ #
+ # rtf.list do |level1|
+ # level1.item do |li|
+ # li << 'some text'
+ # li.apply(some_style) {|x| x << 'some styled text'}
+ # end
+ #
+ # level1.list(:decimal) do |level2|
+ # level2.item {|li| li << 'some other text in a decimal list'}
+ # level2.item {|li| li << 'and here we go'}
+ # end
+ # end
+ #
+ def list(kind=:bullets)
+ node = ListNode.new(self)
+ yield node.list(kind)
+ self.store(node)
+ end
+
+ def link(url, text=nil)
+ node = LinkNode.new(self, url)
+ node << text if text
+ yield node if block_given?
+ self.store(node)
+ end
+
# This method provides a short cut means of creating a line break command
# node. This command node does not take a block and may possess no other
# content.
def line_break
self.store(CommandNode.new(self, '\line', nil, false))
@@ -539,16 +575,128 @@
yield node if block_given?
store(node)
node
end
- alias :write :<<
- alias :color :colour
+ alias :write :<<
+ alias :color :colour
alias :split? :split
+ alias :wrap? :wrap
end # End of the CommandNode class.
+ # This class represents a paragraph within an RTF document.
+ class ParagraphNode < CommandNode
+ def initialize(parent, style=nil)
+ prefix = '\pard'
+ prefix << style.prefix(nil, nil) if style
+ super(parent, prefix, '\par')
+ end
+ end
+
+ # This class represents an ordered/unordered list within an RTF document.
+ #
+ # Currently list nodes can contain any type of node, but this behaviour
+ # will change in future releases. The class overrides the +list+ method
+ # to return a +ListLevelNode+.
+ #
+ class ListNode < CommandNode
+ def initialize(parent)
+ prefix = "\\"
+
+ suffix = '\pard'
+ suffix << ListLevel::ResetTabs.map {|tw| "\\tx#{tw}"}.join
+ suffix << '\ql\qlnatural\pardirnatural\cf0 \\'
+
+ super(parent, prefix, suffix, true, false)
+
+ @template = root.lists.new_template
+ end
+
+ # This method creates a new +ListLevelNode+ of the given kind and
+ # stores it in the document tree.
+ #
+ # ==== Parameters
+ # kind:: The kind of this list level, may be either :bullets or :decimal
+ def list(kind)
+ self.store ListLevelNode.new(self, @template, kind)
+ end
+ end
+
+ # This class represents a list level, and carries out indenting information
+ # and the bullet or number that is prepended to each +ListTextNode+.
+ #
+ # The class overrides the +list+ method to implement nesting, and provides
+ # the +item+ method to add a new list item, the +ListTextNode+.
+ class ListLevelNode < CommandNode
+ def initialize(parent, template, kind, level=1)
+ @template = template
+ @kind = kind
+ @level = template.level_for(level, kind)
+
+ prefix = '\pard'
+ prefix << @level.tabs.map {|tw| "\\tx#{tw}"}.join
+ prefix << "\\li#{@level.indent}\\fi-#{@level.indent}"
+ prefix << "\\ql\\qlnatural\\pardirnatural\n"
+ prefix << "\\ls#{@template.id}\\ilvl#{@level.level-1}\\cf0"
+
+ super(parent, prefix, nil, true, false)
+ end
+
+ # Returns the kind of this level, either :bullets or :decimal
+ attr_reader :kind
+
+ # Returns the indenting level of this list, from 1 to 9
+ def level
+ @level.level
+ end
+
+ # Creates a new +ListTextNode+ and yields it to the calling block
+ def item
+ node = ListTextNode.new(self, @level)
+ yield node
+ self.store(node)
+ end
+
+ # Creates a new +ListLevelNode+ to implement nested lists
+ def list(kind=@kind)
+ node = ListLevelNode.new(self, @template, kind, @level.level+1)
+ yield node
+ self.store(node)
+ end
+ end
+
+ # This class represents a list item, that can contain text or
+ # other nodes. Currently any type of node is accepted, but after
+ # more extensive testing this behaviour may change.
+ class ListTextNode < CommandNode
+ def initialize(parent, level)
+ @level = level
+ @parent = parent
+
+ number = siblings_count + 1 if parent.kind == :decimal
+ prefix = "{\\listtext#{@level.marker.text_format(number)}}"
+ suffix = '\\'
+
+ super(parent, prefix, suffix, false, false)
+ end
+
+ private
+ def siblings_count
+ parent.children.select {|n| n.kind_of?(self.class)}.size
+ end
+ end
+
+ class LinkNode < CommandNode
+ def initialize(parent, url)
+ prefix = "\\field{\\*\\fldinst HYPERLINK \"#{url}\"}{\\fldrslt "
+ suffix = "}"
+
+ super(parent, prefix, suffix, false)
+ end
+ end
+
# This class represents a table node within an RTF document. Table nodes are
# specialised container nodes that contain only TableRowNodes and have their
# size specified when they are created an cannot be resized after that.
class TableNode < ContainerNode
# Cell margin. Default to 100
@@ -949,23 +1097,12 @@
# This method overloads the paragraph method inherited from the
# ComamndNode class to forbid the creation of paragraphs.
#
# ==== Parameters
- # justification:: The justification to be applied to the paragraph.
- # before:: The amount of space, in twips, to be inserted before
- # the paragraph. Defaults to nil.
- # after:: The amount of space, in twips, to be inserted after
- # the paragraph. Defaults to nil.
- # left:: The amount of indentation to place on the left of the
- # paragraph. Defaults to nil.
- # right:: The amount of indentation to place on the right of the
- # paragraph. Defaults to nil.
- # first:: The amount of indentation to place on the left of the
- # first line in the paragraph. Defaults to nil.
- def paragraph(justification=CommandNode::LEFT_JUSTIFY, before=nil,
- after=nil, left=nil, right=nil, first=nil)
+ # style:: The paragraph style, ignored
+ def paragraph(style=nil)
RTFError.fire("TableCellNode#paragraph() called. Table cells cannot "\
"contain paragraphs.")
end
# This method overloads the parent= method inherited from the Node class
@@ -1494,12 +1631,12 @@
# A definition for a document language setting.
LC_VIETNAMESE = 1066
# Attribute accessor.
- attr_reader :fonts, :colours, :information, :character_set, :language,
- :style
+ attr_reader :fonts, :lists, :colours, :information, :character_set,
+ :language, :style
# Attribute mutator.
attr_writer :character_set, :language
@@ -1514,10 +1651,11 @@
# language:: The language setting to be applied to document. This
# defaults to Document::LC_ENGLISH_UK.
def initialize(font, style=nil, character=CS_ANSI, language=LC_ENGLISH_UK)
super(nil, '\rtf1')
@fonts = FontTable.new(font)
+ @lists = ListTable.new
@default_font = 0
@colours = ColourTable.new
@information = Information.new
@character_set = character
@language = language
@@ -1667,9 +1805,10 @@
text << "\\deflang#{@language}" if @language != nil
text << "\\plain\\fs24\\fet1"
text << "\n#{@fonts.to_rtf}"
text << "\n#{@colours.to_rtf}" if @colours.size > 0
text << "\n#{@information.to_rtf}"
+ text << "\n#{@lists.to_rtf}"
if @headers.compact != []
text << "\n#{@headers[3].to_rtf}" if @headers[3] != nil
text << "\n#{@headers[2].to_rtf}" if @headers[2] != nil
text << "\n#{@headers[1].to_rtf}" if @headers[1] != nil
if @headers[1] == nil or @headers[2] == nil