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,47 @@
1
+ module RRTF
2
+ # This class represents a document footer.
3
+ class FooterNode < CommandNode
4
+ # A definition for a header type.
5
+ UNIVERSAL = :footer
6
+
7
+ # A definition for a header type.
8
+ LEFT_PAGE = :footerl
9
+
10
+ # A definition for a header type.
11
+ RIGHT_PAGE = :footerr
12
+
13
+ # A definition for a header type.
14
+ FIRST_PAGE = :footerf
15
+
16
+ # Attribute accessor.
17
+ attr_reader :type
18
+
19
+ # Attribute mutator.
20
+ attr_writer :type
21
+
22
+
23
+ # This is the constructor for the FooterNode class.
24
+ #
25
+ # ==== Parameters
26
+ # document:: A reference to the Document object that will own the new
27
+ # footer.
28
+ # type:: The style type for the new footer. Defaults to a value of
29
+ # FooterNode::UNIVERSAL.
30
+ def initialize(document, type=UNIVERSAL)
31
+ super(document, "\\#{type.id2name}", nil, false)
32
+ @type = type
33
+ end
34
+
35
+ # This method overloads the footnote method inherited from the CommandNode
36
+ # class to prevent footnotes being added to footers.
37
+ #
38
+ # ==== Parameters
39
+ # text:: Not used.
40
+ #
41
+ # ==== Exceptions
42
+ # RTFError:: Always generated whenever this method is called.
43
+ def footnote(text)
44
+ RTFError.fire("Footnotes are not permitted in page footers.")
45
+ end
46
+ end # End of the FooterNode class.
47
+ end
@@ -0,0 +1,65 @@
1
+ module RRTF
2
+ # This class represents a geometry object (shape or text box) within an RTF
3
+ # document.
4
+ # @author Wesley Hileman
5
+ # @since 1.0.0
6
+ class GeometryNode < CommandNode
7
+
8
+ attr_reader :properties
9
+
10
+ # Constructor for the GeometryNode class.
11
+ #
12
+ # @param [Hash, GeometryProperties] properties a hash or GeometryProperties
13
+ # object specifying the properties of the geometry object.
14
+ def initialize(parent, properties = nil)
15
+ case properties
16
+ when Hash
17
+ @properties = GeometryProperties.new(properties)
18
+ when GeometryProperties
19
+ @properties = properties
20
+ else
21
+ RTFError.fire("Invalid geometry properties '#{properties}'.")
22
+ end unless properties.nil?
23
+
24
+ prefix = '{\shp{\*\shpinst'
25
+ prefix << @properties.to_rtf unless properties.nil?
26
+
27
+ super(parent, prefix, '}}', false, false)
28
+ end
29
+
30
+ def to_rtf
31
+ text = StringIO.new
32
+
33
+ text << @prefix
34
+
35
+ unless self.size() == 0
36
+ text << '{\shptxt'
37
+ self.each do |entry|
38
+ text << "\n"
39
+ text << entry.to_rtf
40
+ end # each
41
+ text << '}'
42
+ end # unless
43
+
44
+ text << @suffix
45
+
46
+ text.string
47
+ end
48
+
49
+ # Overrides the {CommandNode#geometry} method to prevent geometry objects
50
+ # from being nested in other geometry objects.
51
+ #
52
+ # @raise [RTFError] whenever called.
53
+ def geometry(properties = nil)
54
+ RTFError.fire("Cannot place a geometry object inside of another.")
55
+ end
56
+
57
+ # Overrides the {CommandNode#<<} method to prevent text from being added
58
+ # to geometry objects directly. Calls {#paragraph} instead.
59
+ #
60
+ # @raise [RTFError] whenever called.
61
+ def <<(text)
62
+ self.paragraph << text
63
+ end
64
+ end # class GeometryNode
65
+ end # module RRTF
@@ -0,0 +1,47 @@
1
+ module RRTF
2
+ # This class represents a document header.
3
+ class HeaderNode < CommandNode
4
+ # A definition for a header type.
5
+ UNIVERSAL = :header
6
+
7
+ # A definition for a header type.
8
+ LEFT_PAGE = :headerl
9
+
10
+ # A definition for a header type.
11
+ RIGHT_PAGE = :headerr
12
+
13
+ # A definition for a header type.
14
+ FIRST_PAGE = :headerf
15
+
16
+ # Attribute accessor.
17
+ attr_reader :type
18
+
19
+ # Attribute mutator.
20
+ attr_writer :type
21
+
22
+
23
+ # This is the constructor for the HeaderNode class.
24
+ #
25
+ # ==== Parameters
26
+ # document:: A reference to the Document object that will own the new
27
+ # header.
28
+ # type:: The style type for the new header. Defaults to a value of
29
+ # HeaderNode::UNIVERSAL.
30
+ def initialize(document, type=UNIVERSAL)
31
+ super(document, "\\#{type.id2name}", nil, false)
32
+ @type = type
33
+ end
34
+
35
+ # This method overloads the footnote method inherited from the CommandNode
36
+ # class to prevent footnotes being added to headers.
37
+ #
38
+ # ==== Parameters
39
+ # text:: Not used.
40
+ #
41
+ # ==== Exceptions
42
+ # RTFError:: Always generated whenever this method is called.
43
+ def footnote(text)
44
+ RTFError.fire("Footnotes are not permitted in page headers.")
45
+ end
46
+ end # End of the HeaderNode class.
47
+ end
@@ -0,0 +1,175 @@
1
+ require 'fastimage'
2
+
3
+ module RRTF
4
+ # This class represents an image within a RTF document. Currently only the
5
+ # PNG, JPEG and Windows Bitmap formats are supported. Efforts are made to
6
+ # identify the file type but these are not guaranteed to work.
7
+ # @author Peter Wood
8
+ # @author Wesley Hileman
9
+ # @since legacy
10
+ class ImageNode < Node
11
+ # Supported image types.
12
+ TYPE_DICTIONARY = {
13
+ :png => 'pngblip',
14
+ :jpeg => 'jpegblip',
15
+ :bmp => 'dibitmap0' # device independent bitmap
16
+ }.freeze
17
+
18
+ # Supported sizing modes.
19
+ SIZING_MODE_DICTIONARY = {
20
+ # Size the image absolutely according to the given width and height.
21
+ "ABSOLUTE" => "ABSOLUTE",
22
+ # Fit the image in the box specified by the given width and height,
23
+ # preserving the aspect ratio.
24
+ "FIX_ASPECT_RATIO" => "FIX_ASPECT_RATIO"
25
+ }.freeze
26
+
27
+ attr_reader :type, :width, :height, :displayed_width, :displayed_height, :sizing_mode, :border
28
+ attr_writer :displayed_width, :displayed_height, :sizing_mode, :border
29
+
30
+ # Attempts to extract the type, width, and height of an image using
31
+ # FastImage.
32
+ #
33
+ # @param [String] source the file path to the source image.
34
+ # @return [Array<Object>] a 3-tuple containing the type, width, and height
35
+ # of the image, respectively (type is a symbol, dimensions in pixels).
36
+ def self.inspect(source)
37
+ type, width, height = nil
38
+
39
+ type = TYPE_DICTIONARY[FastImage.type(source)]
40
+ width, height = FastImage.size(source) unless type.nil?
41
+
42
+ [type, width, height]
43
+ end # inspect()
44
+
45
+ def self.parse_border_array(border)
46
+ case border
47
+ when nil
48
+ []
49
+ when BorderStyle
50
+ [border]
51
+ when Hash
52
+ [BorderStyle.new(border)]
53
+ when Array
54
+ border.collect{ |b| parse_border_array(b) }.flatten.compact
55
+ else
56
+ RTFError.fire("Invalid border #{b}.")
57
+ end
58
+ end
59
+
60
+ # This is the constructor for the ImageNode class.
61
+ #
62
+ # @param parent [Node] a reference to the node that owns the new image node.
63
+ # @param source [String, File] a reference to the image source; this must be a String or a File.
64
+ # @param id [Integer] a unique 32-bit identifier for the image.
65
+ # @param options [Hash] a hash of options.
66
+ # @option options [String] "width" (nil) the display width of the image in twips (can be a string, see {Utilities.value2twips}).
67
+ # @option options [String] "height" (nil) the display height of the image in twips (can be a string, see {Utilities.value2twips}).
68
+ # @option options [String] "sizing_mode" ("ABSOLUTE") the method used to size the image (see {SIZING_MODE_DICTIONARY}).
69
+ # @raise [RTFError] whenever the image specified is not recognised as
70
+ # a supported image type, something other than a String or
71
+ # File or IO is passed as the source parameter or if the
72
+ # specified source does not exist or cannot be accessed.
73
+ def initialize(parent, source, id, options = {})
74
+ super(parent)
75
+ @source = nil
76
+ @id = id
77
+
78
+ # load default options
79
+ options = {
80
+ "width" => nil,
81
+ "height" => nil,
82
+ "sizing_mode" => "ABSOLUTE",
83
+ "border" => nil
84
+ }.merge(options)
85
+
86
+ # extract options
87
+ @displayed_width = Utilities.value2twips(options.delete("width"))
88
+ @displayed_height = Utilities.value2twips(options.delete("height"))
89
+ @sizing_mode = SIZING_MODE_DICTIONARY[options.delete("sizing_mode")]
90
+ @border = self.class.parse_border_array(options.delete("border"))
91
+
92
+ # store border colours in colour table
93
+ @border.each do |b|
94
+ b.push_colours(root.colours)
95
+ end
96
+
97
+ # Store path to image.
98
+ @source = source if source.instance_of?(String) || source.instance_of?(Tempfile)
99
+ @source = source.path if source.instance_of?(File)
100
+
101
+ # Check the file's existence and accessibility.
102
+ if !File.exist?(@source)
103
+ RTFError.fire("Unable to find the #{File.basename(@source)} file.")
104
+ end
105
+ if !File.readable?(@source)
106
+ RTFError.fire("Access to the #{File.basename(@source)} file denied.")
107
+ end
108
+ # Attempt to determine image type and dimensions.
109
+ @type, @width, @height = self.class.inspect(@source)
110
+ if @type.nil?
111
+ RTFError.fire("The #{File.basename(@source)} file contains an unknown or unsupported image type.")
112
+ elsif @width.nil? || @height.nil?
113
+ RTFError.fire("Could not determine the dimensions of #{File.basename(@source)}.")
114
+ end # if
115
+
116
+ @displayed_width, @displayed_height = size_image
117
+ end # initialize()
118
+
119
+ # This method generates the RTF for an ImageNode object.
120
+ def to_rtf
121
+ text = StringIO.new
122
+ count = 0
123
+
124
+ text << '{\pict'
125
+ @border.each{ |b| text << " #{b.prefix(self.root)}" }
126
+ text << "\\picwgoal#{@displayed_width}" if @displayed_width != nil
127
+ text << "\\pichgoal#{@displayed_height}" if @displayed_height != nil
128
+ text << "\\picw#{@width}\\pich#{@height}\\bliptag#{@id}"
129
+ text << "\\#{@type}\n"
130
+
131
+ open_file do |file|
132
+ file.each_byte do |byte|
133
+ hex_str = byte.to_s(16)
134
+ hex_str.insert(0,'0') if hex_str.length == 1
135
+ text << hex_str
136
+ count += 1
137
+ if count == 40
138
+ text << "\n"
139
+ count = 0
140
+ end # if
141
+ end # each_byte
142
+ end # open_file
143
+ text << "\n}"
144
+
145
+ text.string
146
+ end # to_rtf()
147
+
148
+ private
149
+
150
+ def size_image
151
+ case @sizing_mode
152
+ when 'ABSOLUTE'
153
+ [@displayed_width, @displayed_height]
154
+ when 'FIX_ASPECT_RATIO'
155
+ width_ratio = @displayed_width ? (@displayed_width.to_f / @width.to_f) : nil
156
+ height_ratio = @displayed_height ? (@displayed_height.to_f / @height.to_f) : nil
157
+ scale_factor = [width_ratio, height_ratio].compact.min
158
+
159
+ if scale_factor.nil?
160
+ [@displayed_width, @displayed_height]
161
+ else
162
+ [(@width*scale_factor).to_i, (@height*scale_factor).to_i]
163
+ end
164
+ end # case
165
+ end
166
+
167
+ def open_file(&block)
168
+ if block
169
+ File.open(@source, 'rb', &block)
170
+ else
171
+ File.open(@source, 'rb')
172
+ end # if
173
+ end # open_file()
174
+ end # End of the ImageNode class.
175
+ end # module RRTF
@@ -0,0 +1,10 @@
1
+ module RRTF
2
+ class LinkNode < CommandNode
3
+ def initialize(parent, url)
4
+ prefix = "\\field{\\*\\fldinst HYPERLINK \"#{url}\"}{\\fldrslt "
5
+ suffix = "}"
6
+
7
+ super(parent, prefix, suffix, false)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module RRTF
2
+ # This class represents a list level, and carries out indenting information
3
+ # and the bullet or number that is prepended to each +ListTextNode+.
4
+ #
5
+ # The class overrides the +list+ method to implement nesting, and provides
6
+ # the +item+ method to add a new list item, the +ListTextNode+.
7
+ class ListLevelNode < CommandNode
8
+ def initialize(parent, template, kind, level=1)
9
+ @template = template
10
+ @kind = kind
11
+ @level = template.level_for(level, kind)
12
+
13
+ prefix = '\pard'
14
+ prefix << @level.tabs.map {|tw| "\\tx#{tw}"}.join
15
+ prefix << "\\li#{@level.indent}\\fi-#{@level.indent}"
16
+ prefix << "\\ql\\qlnatural\\pardirnatural\n"
17
+ prefix << "\\ls#{@template.id}\\ilvl#{@level.level-1}\\cf0"
18
+
19
+ super(parent, prefix, nil, true, false)
20
+ end
21
+
22
+ # Returns the kind of this level, either :bullets or :decimal
23
+ attr_reader :kind
24
+
25
+ # Returns the indenting level of this list, from 1 to 9
26
+ def level
27
+ @level.level
28
+ end
29
+
30
+ # Creates a new +ListTextNode+ and yields it to the calling block
31
+ def item
32
+ node = ListTextNode.new(self, @level)
33
+ yield node
34
+ self.store(node)
35
+ end
36
+
37
+ # Creates a new +ListLevelNode+ to implement nested lists
38
+ def list(kind=@kind)
39
+ node = ListLevelNode.new(self, @template, kind, @level.level+1)
40
+ yield node
41
+ self.store(node)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ module RRTF
2
+ # This class represents an ordered/unordered list within an RTF document.
3
+ #
4
+ # Currently list nodes can contain any type of node, but this behaviour
5
+ # will change in future releases. The class overrides the +list+ method
6
+ # to return a +ListLevelNode+.
7
+ #
8
+ class ListNode < CommandNode
9
+ def initialize(parent)
10
+ prefix = "\\"
11
+
12
+ suffix = '\pard'
13
+ suffix << ListLevel::ResetTabs.map {|tw| "\\tx#{tw}"}.join
14
+ suffix << '\ql\qlnatural\pardirnatural\cf0 \\'
15
+
16
+ super(parent, prefix, suffix, true, false)
17
+
18
+ @template = root.lists.new_template
19
+ end
20
+
21
+ # This method creates a new +ListLevelNode+ of the given kind and
22
+ # stores it in the document tree.
23
+ #
24
+ # ==== Parameters
25
+ # kind:: The kind of this list level, may be either :bullets or :decimal
26
+ def list(kind)
27
+ self.store ListLevelNode.new(self, @template, kind)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ module RRTF
2
+ # This class represents a list item, that can contain text or
3
+ # other nodes. Currently any type of node is accepted, but after
4
+ # more extensive testing this behaviour may change.
5
+ class ListTextNode < CommandNode
6
+ def initialize(parent, level)
7
+ @level = level
8
+ @parent = parent
9
+
10
+ number = siblings_count + 1 if parent.kind == :decimal
11
+ prefix = "{\\listtext#{@level.marker.text_format(number)}}"
12
+ suffix = '\\'
13
+
14
+ super(parent, prefix, suffix, false, false)
15
+ end
16
+
17
+ private
18
+ def siblings_count
19
+ parent.children.select {|n| n.kind_of?(self.class)}.size
20
+ end
21
+ end
22
+ end