panmind-rtf 0.3.1 → 0.4.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.
- data/{README → README.rdoc} +56 -52
- data/VERSION.yml +2 -2
- data/lib/rtf.rb +2 -1
- data/lib/rtf/list.rb +219 -0
- data/lib/rtf/node.rb +186 -34
- data/lib/rtf/style.rb +1 -1
- data/test/command_node_test.rb +32 -1
- data/test/information_test.rb +1 -1
- metadata +7 -6
data/{README → README.rdoc}
RENAMED
@@ -1,3 +1,14 @@
|
|
1
|
+
== Purpose of this fork
|
2
|
+
|
3
|
+
* Add support for ordered and unordered lists [done]
|
4
|
+
* Write comprehensive tests for OL and UL [TODO]
|
5
|
+
* Clean up the API [TODO]
|
6
|
+
* DRY the code [TODO]
|
7
|
+
|
8
|
+
Please, please, please: if you come along this library and would lend me an
|
9
|
+
hand to complete tests, please help. Thank you!
|
10
|
+
|
11
|
+
|
1
12
|
== Ruby Rich Text Format (RTF) Library
|
2
13
|
The RTF library provides a pure Ruby set of functionality that can be used to
|
3
14
|
programmatically create RTF documents. The main aim in developing this library
|
@@ -58,8 +69,6 @@ you are already familiar with the Ruby language. So, for a start, consider...
|
|
58
69
|
require 'rubygems'
|
59
70
|
require 'rtf'
|
60
71
|
|
61
|
-
include RTF
|
62
|
-
|
63
72
|
The first thing to look at here at the are the first two lines. The RTF library
|
64
73
|
is provided as a Ruby gem and these two lines load the libraries functionality
|
65
74
|
into the script. The third line of code includes the RTF module into the current
|
@@ -67,7 +76,7 @@ name space. This is a convenience mechanism that saves on specifically having
|
|
67
76
|
to refer to the module when accessing the RTF library. Next we want to create
|
68
77
|
an RTF document and that is done like this...
|
69
78
|
|
70
|
-
document = Document.new(Font.new(Font::ROMAN, 'Times New Roman'))
|
79
|
+
document = RTF::Document.new(RTF::Font.new(RTF::Font::ROMAN, 'Times New Roman'))
|
71
80
|
|
72
81
|
This line of code creates a new Document object, specifying that the default
|
73
82
|
font for the document will the the Times New Roman font. So we have a document,
|
@@ -93,74 +102,69 @@ some code into the document. We want the code to appear in the document slightly
|
|
93
102
|
indented on the left hand side, in a non-proportionately space font and we want
|
94
103
|
it in bold text. Heres the code that shows how to do that...
|
95
104
|
|
96
|
-
01
|
97
|
-
02
|
98
|
-
03
|
99
|
-
04
|
100
|
-
05
|
101
|
-
06
|
102
|
-
07
|
103
|
-
08
|
104
|
-
09
|
105
|
-
10
|
106
|
-
11
|
107
|
-
12
|
108
|
-
13
|
109
|
-
14
|
110
|
-
15
|
111
|
-
16
|
112
|
-
17
|
113
|
-
18
|
114
|
-
19
|
115
|
-
20
|
116
|
-
21 end
|
105
|
+
01 code_para = ParagraphStyle.new
|
106
|
+
02 code_para.left_indent = 200
|
107
|
+
03
|
108
|
+
04 code_char = CharacterStyle.new
|
109
|
+
05 code_char.font = RTF::Font::MODERN
|
110
|
+
06 code_char.bold = true
|
111
|
+
07
|
112
|
+
08 document.paragraph(code_para) do |p|
|
113
|
+
09 p.apply(code_char) do |c|
|
114
|
+
10 c << "count = 0"
|
115
|
+
11 c.line_break
|
116
|
+
12 c << "File.open('file.txt', 'r') do |file|"
|
117
|
+
13 c.line_break
|
118
|
+
14 c << " file.each_line {|line| count += 1}"
|
119
|
+
15 c.line_break
|
120
|
+
16 c << "end"
|
121
|
+
17 c.line_break
|
122
|
+
18 c << "puts \"File contains \#{count} lines.\""
|
123
|
+
19 end
|
124
|
+
20 end
|
117
125
|
|
118
126
|
This is a much larger piece of code and covers a number of topics that need to
|
119
127
|
be addressed. I have included line numbers with code so that individual elements
|
120
|
-
can be referenced. Lines 1 to
|
121
|
-
Hash and assign it to a variable called styles. On the next two lines we create
|
128
|
+
can be referenced. Lines 1 to 6 are the first new elements. Here we create
|
122
129
|
two style objects, one that can be applied to paragraphs and one that applies
|
123
130
|
to characters.
|
124
131
|
|
125
|
-
On
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
132
|
+
On line 2 we set the left indentation value of the paragraph style to 200 *twips*.
|
133
|
+
A twip is a type setting measurement that equates to one twentieth of a point
|
134
|
+
(about a fifty seventh of a millimetre or one seventy second of an inch). This is
|
135
|
+
the measurement scale used by RTF documents.
|
136
|
+
|
137
|
+
On lines 5 and 6 we update the character style to use a courier font and to
|
138
|
+
apply bold styling to the text.
|
131
139
|
|
132
|
-
On
|
133
|
-
|
134
|
-
|
135
|
-
a style that will be applied to the paragraph created, the paragraph style we
|
136
|
-
had prepared earlier.
|
140
|
+
On line 8 we start a new paragraph in our document. This differs from our previous
|
141
|
+
use of this method in that we specify a style that will be applied to the paragraph
|
142
|
+
using the one we prepared earlier.
|
137
143
|
|
138
144
|
The block accompanying the paragraph method takes the single parameter that we
|
139
145
|
have seen previously. At this point its probably a good idea to point out that
|
140
146
|
the elements that make up an RTF document created with the library are all
|
141
|
-
stored as nodes in a tree.
|
142
|
-
|
147
|
+
stored as nodes in a tree.
|
148
|
+
|
149
|
+
Within the block, the +apply+ method applies a character style, and we're using
|
150
|
+
the one we prepared earlier.
|
143
151
|
|
144
|
-
Within the block we've called a method on the paragraph node called apply. This
|
145
|
-
method applies a character style and we're using the one we prepared earlier.
|
146
152
|
Like the call to the paragraph method, the apply method is passed a block. All
|
147
|
-
text added to the blocks node
|
148
|
-
|
153
|
+
text added to the blocks node will have the styling we've defined (bold courier
|
154
|
+
font) applied to it.
|
149
155
|
|
150
|
-
Note, that within the apply block we could still use the
|
151
|
-
added to this would appear in the paragraph but wouldn't be styled and,
|
152
|
-
should be noted, will appear before any text added to
|
153
|
-
becomes part of the document when the apply block completes
|
156
|
+
Note, that within the apply block we could still use the previous (p) node. Any
|
157
|
+
text we added to this would appear in the paragraph but wouldn't be styled and,
|
158
|
+
it should be noted, will appear before any text added to c, as the c node only
|
159
|
+
becomes part of the document when the apply block completes.
|
154
160
|
|
155
|
-
Within the apply method block we add some lines of text to the
|
161
|
+
Within the apply method block we add some lines of text to the c node. Note
|
156
162
|
that, as this is all encompassed within the parapgraph block, all the text is
|
157
163
|
part of a single paragraph. To get each of the lines of code to appear on a
|
158
164
|
line of their own we have used the line_break method which inserts a carriage
|
159
165
|
return into the document. Note you should use this method to insert line breaks
|
160
166
|
rather than trying to add a string containing "\n". RTF is a text based standard
|
161
|
-
and won't treat "\n" as you're expecting.
|
162
|
-
escape the '#' in one of the code lines to stop Ruby considering it as an
|
163
|
-
interpolated value.
|
167
|
+
and won't treat "\n" as you're expecting.
|
164
168
|
|
165
169
|
Okay, so we've seen have the basics of creating a document and adding elements
|
166
170
|
to that document. How do we get what we've created to a file. Well thats
|
@@ -183,4 +187,4 @@ Chris O'Sullivan
|
|
183
187
|
COPYRIGHT
|
184
188
|
=========
|
185
189
|
|
186
|
-
Copyright (c) 2009-2010 Peter Wood. See LICENSE for details.
|
190
|
+
Copyright (c) 2009-2010 Peter Wood. See LICENSE for details.
|
data/VERSION.yml
CHANGED
data/lib/rtf.rb
CHANGED
@@ -7,6 +7,7 @@ require 'rtf/style'
|
|
7
7
|
require 'rtf/information'
|
8
8
|
require 'rtf/paper'
|
9
9
|
require 'rtf/node'
|
10
|
+
require 'rtf/list'
|
10
11
|
|
11
12
|
# This module encapsulates all the classes and definitions relating to the RTF
|
12
13
|
# library.
|
@@ -31,4 +32,4 @@ module RTF
|
|
31
32
|
raise RTFError.new(message)
|
32
33
|
end
|
33
34
|
end # End of the RTFError class.
|
34
|
-
end # End of the RTF module.
|
35
|
+
end # End of the RTF module.
|
data/lib/rtf/list.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
module RTF
|
2
|
+
class ListTable
|
3
|
+
def initialize
|
4
|
+
@templates = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def new_template
|
8
|
+
@templates.push ListTemplate.new(next_template_id)
|
9
|
+
@templates.last
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_rtf(indent=0)
|
13
|
+
return '' if @templates.empty?
|
14
|
+
|
15
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
16
|
+
|
17
|
+
# List table
|
18
|
+
text = "#{prefix}{\\*\\listtable"
|
19
|
+
@templates.each {|tpl| text << tpl.to_rtf}
|
20
|
+
text << "}"
|
21
|
+
|
22
|
+
# List override table, a Cargo Cult.
|
23
|
+
text << "#{prefix}{\\*\\listoverridetable"
|
24
|
+
@templates.each do |tpl|
|
25
|
+
text << "{\\listoverride\\listid#{tpl.id}\\listoverridecount0\\ls#{tpl.id}}"
|
26
|
+
end
|
27
|
+
text << "}\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
def next_template_id
|
32
|
+
@templates.size + 1
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class ListMarker
|
38
|
+
def initialize(name, codepoint=nil)
|
39
|
+
@name = name
|
40
|
+
@codepoint = codepoint
|
41
|
+
end
|
42
|
+
|
43
|
+
def bullet?
|
44
|
+
!@codepoint.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
def type
|
48
|
+
bullet? ? :bullet : :decimal
|
49
|
+
end
|
50
|
+
|
51
|
+
def number_type
|
52
|
+
# 23: bullet, 0: arabic
|
53
|
+
# applies to the \levelnfcN macro
|
54
|
+
#
|
55
|
+
bullet? ? 23 : 0
|
56
|
+
end
|
57
|
+
|
58
|
+
def name
|
59
|
+
name = "\\{#@name\\}"
|
60
|
+
name << '.' unless bullet?
|
61
|
+
name
|
62
|
+
end
|
63
|
+
|
64
|
+
def template_format
|
65
|
+
# The first char is the string size, the next ones are
|
66
|
+
# either placeholders (\'0X) or actual characters to
|
67
|
+
# include in the format. In the bullet case, \uc0 is
|
68
|
+
# used to get rid of the multibyte translation: we want
|
69
|
+
# an Unicode character.
|
70
|
+
#
|
71
|
+
# In the decimal case, we have a fixed format, with a
|
72
|
+
# dot following the actual number.
|
73
|
+
#
|
74
|
+
if bullet?
|
75
|
+
"\\'01\\uc0\\u#@codepoint"
|
76
|
+
else
|
77
|
+
"\\'02\\'00. "
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def text_format(n=nil)
|
82
|
+
text =
|
83
|
+
if bullet?
|
84
|
+
"\\uc0\\u#@codepoint"
|
85
|
+
else
|
86
|
+
"#{n}."
|
87
|
+
end
|
88
|
+
|
89
|
+
"\t#{text}\t"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class ListTemplate
|
94
|
+
attr_reader :id
|
95
|
+
|
96
|
+
Markers = {
|
97
|
+
:disc => ListMarker.new('disc', 0x2022),
|
98
|
+
:hyphen => ListMarker.new('hyphen', 0x2043),
|
99
|
+
:decimal => ListMarker.new('decimal' )
|
100
|
+
}
|
101
|
+
|
102
|
+
def initialize(id)
|
103
|
+
@levels = []
|
104
|
+
@id = id
|
105
|
+
end
|
106
|
+
|
107
|
+
def level_for(level, kind = :bullets)
|
108
|
+
@levels[level-1] ||= begin
|
109
|
+
# Only disc for now: we'll add support
|
110
|
+
# for more customization options later
|
111
|
+
marker = Markers[kind == :bullets ? :disc : :decimal]
|
112
|
+
ListLevel.new(self, marker, level)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_rtf(indent=0)
|
117
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
118
|
+
|
119
|
+
text = "#{prefix}{\\list\\listtemplate#{id}\\listhybrid"
|
120
|
+
@levels.each {|lvl| text << lvl.to_rtf}
|
121
|
+
text << "{\\listname;}\\listid#{id}}\n"
|
122
|
+
|
123
|
+
text
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class ListLevel
|
128
|
+
ValidLevels = (1..9)
|
129
|
+
|
130
|
+
LevelTabs = [
|
131
|
+
220, 720, 1133, 1700, 2267,
|
132
|
+
2834, 3401, 3968, 4535, 5102,
|
133
|
+
5669, 6236, 6803
|
134
|
+
].freeze
|
135
|
+
|
136
|
+
ResetTabs = [560].concat(LevelTabs[2..-1]).freeze
|
137
|
+
|
138
|
+
attr_reader :level, :marker
|
139
|
+
|
140
|
+
def initialize(template, marker, level)
|
141
|
+
unless marker.kind_of? ListMarker
|
142
|
+
RTFError.fire("Invalid marker #{marker.inspect}")
|
143
|
+
end
|
144
|
+
|
145
|
+
unless ValidLevels.include? level
|
146
|
+
RTFError.fire("Invalid list level: #{level}")
|
147
|
+
end
|
148
|
+
|
149
|
+
@template = template
|
150
|
+
@level = level
|
151
|
+
@marker = marker
|
152
|
+
end
|
153
|
+
|
154
|
+
def type
|
155
|
+
@marker.type
|
156
|
+
end
|
157
|
+
|
158
|
+
def reset_tabs
|
159
|
+
ResetTabs
|
160
|
+
end
|
161
|
+
|
162
|
+
def tabs
|
163
|
+
@tabs ||= begin
|
164
|
+
tabs = LevelTabs.dup # Kernel#tap would be prettier here
|
165
|
+
|
166
|
+
(@level - 1).times do
|
167
|
+
# Reverse-engineered while looking at Textedit.app
|
168
|
+
# generated output: they already made sure that it
|
169
|
+
# would look good on every RTF editor :-p
|
170
|
+
#
|
171
|
+
a, = tabs.shift(3)
|
172
|
+
a,b = a + 720, a + 1220
|
173
|
+
tabs.shift while tabs.first < b
|
174
|
+
tabs.unshift a, b
|
175
|
+
end
|
176
|
+
|
177
|
+
tabs
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def id
|
182
|
+
@id ||= @template.id * 10 + level
|
183
|
+
end
|
184
|
+
|
185
|
+
def indent
|
186
|
+
@indent ||= level * 720
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_rtf(indent=0)
|
190
|
+
prefix = indent > 0 ? ' ' * indent : ''
|
191
|
+
|
192
|
+
text = "#{prefix}{\\listlevel\\levelstartat1"
|
193
|
+
|
194
|
+
# Marker type. The first declaration is for Backward Compatibility (BC).
|
195
|
+
nfc = @marker.number_type
|
196
|
+
text << "\\levelnfc#{nfc}\\levelnfcn#{nfc}"
|
197
|
+
|
198
|
+
# Justification, currently only left justified (0). First decl for BC.
|
199
|
+
text << '\leveljc0\leveljcn0'
|
200
|
+
|
201
|
+
# Character that follows the level text, currently only TAB.
|
202
|
+
text << '\levelfollow0'
|
203
|
+
|
204
|
+
# BC: Minimum distance from the left & right edges.
|
205
|
+
text << '\levelindent0\levelspace360'
|
206
|
+
|
207
|
+
# Marker name
|
208
|
+
text << "{\\*\\levelmarker #{@marker.name}}"
|
209
|
+
|
210
|
+
# Marker text format
|
211
|
+
text << "{\\leveltext\\leveltemplateid#{id}#{@marker.template_format};}"
|
212
|
+
text << '{\levelnumbers;}'
|
213
|
+
|
214
|
+
# The actual spacing
|
215
|
+
text << "\\fi-360\\li#{self.indent}\\lin#{self.indent}}\n"
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
end
|
data/lib/rtf/node.rb
CHANGED
@@ -212,6 +212,9 @@ module RTF
|
|
212
212
|
# be written to separate lines whether the node is converted
|
213
213
|
# to RTF. Defaults to true
|
214
214
|
attr_accessor :split
|
215
|
+
# A boolean to indicate whether the prefix and suffix should
|
216
|
+
# be wrapped in curly braces. Defaults to true.
|
217
|
+
attr_accessor :wrap
|
215
218
|
|
216
219
|
# This is the constructor for the CommandNode class.
|
217
220
|
#
|
@@ -223,11 +226,14 @@ module RTF
|
|
223
226
|
# split:: A boolean to indicate whether the prefix and suffix should
|
224
227
|
# be written to separate lines whether the node is converted
|
225
228
|
# to RTF. Defaults to true.
|
226
|
-
|
229
|
+
# wrap:: A boolean to indicate whether the prefix and suffix should
|
230
|
+
# be wrapped in curly braces. Defaults to true.
|
231
|
+
def initialize(parent, prefix, suffix=nil, split=true, wrap=true)
|
227
232
|
super(parent)
|
228
233
|
@prefix = prefix
|
229
234
|
@suffix = suffix
|
230
235
|
@split = split
|
236
|
+
@wrap = wrap
|
231
237
|
end
|
232
238
|
|
233
239
|
# This method adds text to a command node. If the last child node of the
|
@@ -246,19 +252,20 @@ module RTF
|
|
246
252
|
|
247
253
|
# This method generates the RTF text for a CommandNode object.
|
248
254
|
def to_rtf
|
249
|
-
text
|
250
|
-
|
251
|
-
|
255
|
+
text = StringIO.new
|
256
|
+
|
257
|
+
text << '{' if wrap?
|
258
|
+
text << @prefix if @prefix
|
252
259
|
|
253
|
-
text << "{#{@prefix}"
|
254
|
-
text << separator if self.size > 0
|
255
260
|
self.each do |entry|
|
256
|
-
text << "\n" if
|
257
|
-
|
258
|
-
text << "#{entry.to_rtf}"
|
261
|
+
text << "\n" if split?
|
262
|
+
text << entry.to_rtf
|
259
263
|
end
|
260
|
-
|
261
|
-
text << "
|
264
|
+
|
265
|
+
text << "\n" if split?
|
266
|
+
text << @suffix if @suffix
|
267
|
+
text << '}' if wrap?
|
268
|
+
|
262
269
|
text.string
|
263
270
|
end
|
264
271
|
|
@@ -273,16 +280,43 @@ module RTF
|
|
273
280
|
# for the new paragraph. Defaults to nil to indicate that the
|
274
281
|
# currently applied paragraph styling should be used.
|
275
282
|
def paragraph(style=nil)
|
276
|
-
|
277
|
-
text = StringIO.new
|
278
|
-
text << '\pard'
|
279
|
-
text << style.prefix(nil, nil) if style != nil
|
280
|
-
|
281
|
-
node = CommandNode.new(self, text.string, '\par')
|
283
|
+
node = ParagraphNode.new(self, style)
|
282
284
|
yield node if block_given?
|
283
285
|
self.store(node)
|
284
286
|
end
|
285
287
|
|
288
|
+
# This method provides a short cut means of creating a new ordered or
|
289
|
+
# unordered list. The method requires a block that will be passed a
|
290
|
+
# single parameter that'll be a reference to the first level of the
|
291
|
+
# list. See the +ListLevelNode+ doc for more information.
|
292
|
+
#
|
293
|
+
# Example usage:
|
294
|
+
#
|
295
|
+
# rtf.list do |level1|
|
296
|
+
# level1.item do |li|
|
297
|
+
# li << 'some text'
|
298
|
+
# li.apply(some_style) {|x| x << 'some styled text'}
|
299
|
+
# end
|
300
|
+
#
|
301
|
+
# level1.list(:decimal) do |level2|
|
302
|
+
# level2.item {|li| li << 'some other text in a decimal list'}
|
303
|
+
# level2.item {|li| li << 'and here we go'}
|
304
|
+
# end
|
305
|
+
# end
|
306
|
+
#
|
307
|
+
def list(kind=:bullets)
|
308
|
+
node = ListNode.new(self)
|
309
|
+
yield node.list(kind)
|
310
|
+
self.store(node)
|
311
|
+
end
|
312
|
+
|
313
|
+
def link(url, text=nil)
|
314
|
+
node = LinkNode.new(self, url)
|
315
|
+
node << text if text
|
316
|
+
yield node if block_given?
|
317
|
+
self.store(node)
|
318
|
+
end
|
319
|
+
|
286
320
|
# This method provides a short cut means of creating a line break command
|
287
321
|
# node. This command node does not take a block and may possess no other
|
288
322
|
# content.
|
@@ -424,6 +458,21 @@ module RTF
|
|
424
458
|
end
|
425
459
|
end
|
426
460
|
|
461
|
+
# This method provides a short cut means of creating a strike command
|
462
|
+
# node. The method accepts a block that will be passed a single parameter
|
463
|
+
# which will be a reference to the strike node created. After the
|
464
|
+
# block is complete the strike node is appended to the end of the
|
465
|
+
# child nodes on the object that the method is call against.
|
466
|
+
def strike
|
467
|
+
style = CharacterStyle.new
|
468
|
+
style.strike = true
|
469
|
+
if block_given?
|
470
|
+
apply(style) {|node| yield node}
|
471
|
+
else
|
472
|
+
apply(style)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
427
476
|
# This method provides a short cut means of creating a font command node.
|
428
477
|
# The method accepts a block that will be passed a single parameter which
|
429
478
|
# will be a reference to the font node created. After the block is
|
@@ -526,11 +575,123 @@ module RTF
|
|
526
575
|
node
|
527
576
|
end
|
528
577
|
|
529
|
-
alias :write
|
530
|
-
alias :color
|
578
|
+
alias :write :<<
|
579
|
+
alias :color :colour
|
531
580
|
alias :split? :split
|
581
|
+
alias :wrap? :wrap
|
532
582
|
end # End of the CommandNode class.
|
533
583
|
|
584
|
+
# This class represents a paragraph within an RTF document.
|
585
|
+
class ParagraphNode < CommandNode
|
586
|
+
def initialize(parent, style=nil)
|
587
|
+
prefix = '\pard'
|
588
|
+
prefix << style.prefix(nil, nil) if style
|
589
|
+
|
590
|
+
super(parent, prefix, '\par')
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
# This class represents an ordered/unordered list within an RTF document.
|
595
|
+
#
|
596
|
+
# Currently list nodes can contain any type of node, but this behaviour
|
597
|
+
# will change in future releases. The class overrides the +list+ method
|
598
|
+
# to return a +ListLevelNode+.
|
599
|
+
#
|
600
|
+
class ListNode < CommandNode
|
601
|
+
def initialize(parent)
|
602
|
+
prefix = "\\"
|
603
|
+
|
604
|
+
suffix = '\pard'
|
605
|
+
suffix << ListLevel::ResetTabs.map {|tw| "\\tx#{tw}"}.join
|
606
|
+
suffix << '\ql\qlnatural\pardirnatural\cf0 \\'
|
607
|
+
|
608
|
+
super(parent, prefix, suffix, true, false)
|
609
|
+
|
610
|
+
@template = root.lists.new_template
|
611
|
+
end
|
612
|
+
|
613
|
+
# This method creates a new +ListLevelNode+ of the given kind and
|
614
|
+
# stores it in the document tree.
|
615
|
+
#
|
616
|
+
# ==== Parameters
|
617
|
+
# kind:: The kind of this list level, may be either :bullets or :decimal
|
618
|
+
def list(kind)
|
619
|
+
self.store ListLevelNode.new(self, @template, kind)
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
# This class represents a list level, and carries out indenting information
|
624
|
+
# and the bullet or number that is prepended to each +ListTextNode+.
|
625
|
+
#
|
626
|
+
# The class overrides the +list+ method to implement nesting, and provides
|
627
|
+
# the +item+ method to add a new list item, the +ListTextNode+.
|
628
|
+
class ListLevelNode < CommandNode
|
629
|
+
def initialize(parent, template, kind, level=1)
|
630
|
+
@template = template
|
631
|
+
@kind = kind
|
632
|
+
@level = template.level_for(level, kind)
|
633
|
+
|
634
|
+
prefix = '\pard'
|
635
|
+
prefix << @level.tabs.map {|tw| "\\tx#{tw}"}.join
|
636
|
+
prefix << "\\li#{@level.indent}\\fi-#{@level.indent}"
|
637
|
+
prefix << "\\ql\\qlnatural\\pardirnatural\n"
|
638
|
+
prefix << "\\ls#{@template.id}\\ilvl#{@level.level-1}\\cf0"
|
639
|
+
|
640
|
+
super(parent, prefix, nil, true, false)
|
641
|
+
end
|
642
|
+
|
643
|
+
# Returns the kind of this level, either :bullets or :decimal
|
644
|
+
attr_reader :kind
|
645
|
+
|
646
|
+
# Returns the indenting level of this list, from 1 to 9
|
647
|
+
def level
|
648
|
+
@level.level
|
649
|
+
end
|
650
|
+
|
651
|
+
# Creates a new +ListTextNode+ and yields it to the calling block
|
652
|
+
def item
|
653
|
+
node = ListTextNode.new(self, @level)
|
654
|
+
yield node
|
655
|
+
self.store(node)
|
656
|
+
end
|
657
|
+
|
658
|
+
# Creates a new +ListLevelNode+ to implement nested lists
|
659
|
+
def list(kind=@kind)
|
660
|
+
node = ListLevelNode.new(self, @template, kind, @level.level+1)
|
661
|
+
yield node
|
662
|
+
self.store(node)
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
# This class represents a list item, that can contain text or
|
667
|
+
# other nodes. Currently any type of node is accepted, but after
|
668
|
+
# more extensive testing this behaviour may change.
|
669
|
+
class ListTextNode < CommandNode
|
670
|
+
def initialize(parent, level)
|
671
|
+
@level = level
|
672
|
+
@parent = parent
|
673
|
+
|
674
|
+
number = siblings_count + 1 if parent.kind == :decimal
|
675
|
+
prefix = "{\\listtext#{@level.marker.text_format(number)}}"
|
676
|
+
suffix = '\\'
|
677
|
+
|
678
|
+
super(parent, prefix, suffix, false, false)
|
679
|
+
end
|
680
|
+
|
681
|
+
private
|
682
|
+
def siblings_count
|
683
|
+
parent.children.select {|n| n.kind_of?(self.class)}.size
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
class LinkNode < CommandNode
|
688
|
+
def initialize(parent, url)
|
689
|
+
prefix = "\\field{\\*\\fldinst HYPERLINK \"#{url}\"}{\\fldrslt "
|
690
|
+
suffix = "}"
|
691
|
+
|
692
|
+
super(parent, prefix, suffix, false)
|
693
|
+
end
|
694
|
+
end
|
534
695
|
|
535
696
|
# This class represents a table node within an RTF document. Table nodes are
|
536
697
|
# specialised container nodes that contain only TableRowNodes and have their
|
@@ -936,19 +1097,8 @@ module RTF
|
|
936
1097
|
# ComamndNode class to forbid the creation of paragraphs.
|
937
1098
|
#
|
938
1099
|
# ==== Parameters
|
939
|
-
#
|
940
|
-
|
941
|
-
# the paragraph. Defaults to nil.
|
942
|
-
# after:: The amount of space, in twips, to be inserted after
|
943
|
-
# the paragraph. Defaults to nil.
|
944
|
-
# left:: The amount of indentation to place on the left of the
|
945
|
-
# paragraph. Defaults to nil.
|
946
|
-
# right:: The amount of indentation to place on the right of the
|
947
|
-
# paragraph. Defaults to nil.
|
948
|
-
# first:: The amount of indentation to place on the left of the
|
949
|
-
# first line in the paragraph. Defaults to nil.
|
950
|
-
def paragraph(justification=CommandNode::LEFT_JUSTIFY, before=nil,
|
951
|
-
after=nil, left=nil, right=nil, first=nil)
|
1100
|
+
# style:: The paragraph style, ignored
|
1101
|
+
def paragraph(style=nil)
|
952
1102
|
RTFError.fire("TableCellNode#paragraph() called. Table cells cannot "\
|
953
1103
|
"contain paragraphs.")
|
954
1104
|
end
|
@@ -1481,8 +1631,8 @@ module RTF
|
|
1481
1631
|
LC_VIETNAMESE = 1066
|
1482
1632
|
|
1483
1633
|
# Attribute accessor.
|
1484
|
-
attr_reader :fonts, :colours, :information, :character_set,
|
1485
|
-
:style
|
1634
|
+
attr_reader :fonts, :lists, :colours, :information, :character_set,
|
1635
|
+
:language, :style
|
1486
1636
|
|
1487
1637
|
# Attribute mutator.
|
1488
1638
|
attr_writer :character_set, :language
|
@@ -1501,6 +1651,7 @@ module RTF
|
|
1501
1651
|
def initialize(font, style=nil, character=CS_ANSI, language=LC_ENGLISH_UK)
|
1502
1652
|
super(nil, '\rtf1')
|
1503
1653
|
@fonts = FontTable.new(font)
|
1654
|
+
@lists = ListTable.new
|
1504
1655
|
@default_font = 0
|
1505
1656
|
@colours = ColourTable.new
|
1506
1657
|
@information = Information.new
|
@@ -1654,6 +1805,7 @@ module RTF
|
|
1654
1805
|
text << "\n#{@fonts.to_rtf}"
|
1655
1806
|
text << "\n#{@colours.to_rtf}" if @colours.size > 0
|
1656
1807
|
text << "\n#{@information.to_rtf}"
|
1808
|
+
text << "\n#{@lists.to_rtf}"
|
1657
1809
|
if @headers.compact != []
|
1658
1810
|
text << "\n#{@headers[3].to_rtf}" if @headers[3] != nil
|
1659
1811
|
text << "\n#{@headers[2].to_rtf}" if @headers[2] != nil
|
data/lib/rtf/style.rb
CHANGED
data/test/command_node_test.rb
CHANGED
@@ -40,7 +40,7 @@ class CommandNodeTest < Test::Unit::TestCase
|
|
40
40
|
|
41
41
|
assert(root.paragraph(style) != nil)
|
42
42
|
assert(root.size == 1)
|
43
|
-
assert(root[0].class ==
|
43
|
+
assert(root[0].class == ParagraphNode)
|
44
44
|
assert(root[0].prefix == '\pard\ql')
|
45
45
|
assert(root[0].suffix == '\par')
|
46
46
|
assert(root.split == true)
|
@@ -161,6 +161,11 @@ class CommandNodeTest < Test::Unit::TestCase
|
|
161
161
|
assert(node.prefix == '\sub')
|
162
162
|
assert(node.suffix == nil)
|
163
163
|
assert(node == root[-1])
|
164
|
+
|
165
|
+
node = root.strike
|
166
|
+
assert(node.prefix == '\strike')
|
167
|
+
assert(node.suffix == nil)
|
168
|
+
assert(node == root[-1])
|
164
169
|
end
|
165
170
|
|
166
171
|
# Test text node addition.
|
@@ -202,6 +207,32 @@ class CommandNodeTest < Test::Unit::TestCase
|
|
202
207
|
assert(table[0][2].width == 200)
|
203
208
|
end
|
204
209
|
|
210
|
+
# List object model test
|
211
|
+
def test_list
|
212
|
+
root = Document.new(Font.new(Font::ROMAN, 'Arial'))
|
213
|
+
root.list do |l1|
|
214
|
+
assert l1.class == ListLevelNode
|
215
|
+
assert l1.level == 1
|
216
|
+
|
217
|
+
l1.item do |li|
|
218
|
+
assert li.class == ListTextNode
|
219
|
+
text = li << 'text'
|
220
|
+
assert text.class == TextNode
|
221
|
+
assert text.text == 'text'
|
222
|
+
end
|
223
|
+
|
224
|
+
l1.list do |l2|
|
225
|
+
assert l2.class == ListLevelNode
|
226
|
+
assert l2.level == 2
|
227
|
+
l2.item do |li|
|
228
|
+
text = li << 'text'
|
229
|
+
assert text.class == TextNode
|
230
|
+
assert text.text == 'text'
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
205
236
|
# This test checks the previous_node and next_node methods that could not be
|
206
237
|
# fully and properly checked in the NodeTest.rb file.
|
207
238
|
def test_peers
|
data/test/information_test.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Peter Wood
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-09-
|
17
|
+
date: 2010-09-15 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -26,11 +26,11 @@ extensions: []
|
|
26
26
|
|
27
27
|
extra_rdoc_files:
|
28
28
|
- LICENSE
|
29
|
-
- README
|
29
|
+
- README.rdoc
|
30
30
|
files:
|
31
31
|
- CHANGES
|
32
32
|
- LICENSE
|
33
|
-
- README
|
33
|
+
- README.rdoc
|
34
34
|
- Rakefile
|
35
35
|
- VERSION.yml
|
36
36
|
- examples/example01.rb
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/rtf/colour.rb
|
46
46
|
- lib/rtf/font.rb
|
47
47
|
- lib/rtf/information.rb
|
48
|
+
- lib/rtf/list.rb
|
48
49
|
- lib/rtf/node.rb
|
49
50
|
- lib/rtf/paper.rb
|
50
51
|
- lib/rtf/style.rb
|