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