review 0.6.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 (50) hide show
  1. data/COPYING +515 -0
  2. data/ChangeLog +1278 -0
  3. data/README.rdoc +21 -0
  4. data/Rakefile +42 -0
  5. data/VERSION +1 -0
  6. data/bin/review-check +190 -0
  7. data/bin/review-checkdep +63 -0
  8. data/bin/review-compile +165 -0
  9. data/bin/review-epubmaker +525 -0
  10. data/bin/review-index +108 -0
  11. data/bin/review-preproc +140 -0
  12. data/bin/review-validate +51 -0
  13. data/bin/review-vol +106 -0
  14. data/doc/format.re +486 -0
  15. data/doc/format.txt +434 -0
  16. data/doc/format_idg.txt +194 -0
  17. data/doc/format_sjis.txt +313 -0
  18. data/doc/sample.css +91 -0
  19. data/doc/sample.yaml +46 -0
  20. data/lib/lineinput.rb +155 -0
  21. data/lib/review/book.rb +580 -0
  22. data/lib/review/builder.rb +274 -0
  23. data/lib/review/compat.rb +22 -0
  24. data/lib/review/compiler.rb +483 -0
  25. data/lib/review/epubbuilder.rb +692 -0
  26. data/lib/review/ewbbuilder.rb +382 -0
  27. data/lib/review/exception.rb +21 -0
  28. data/lib/review/htmlbuilder.rb +370 -0
  29. data/lib/review/htmllayout.rb +19 -0
  30. data/lib/review/htmlutils.rb +27 -0
  31. data/lib/review/idgxmlbuilder.rb +1078 -0
  32. data/lib/review/index.rb +224 -0
  33. data/lib/review/latexbuilder.rb +420 -0
  34. data/lib/review/latexindex.rb +35 -0
  35. data/lib/review/latexutils.rb +52 -0
  36. data/lib/review/preprocessor.rb +520 -0
  37. data/lib/review/textutils.rb +19 -0
  38. data/lib/review/tocparser.rb +333 -0
  39. data/lib/review/tocprinter.rb +220 -0
  40. data/lib/review/topbuilder.rb +572 -0
  41. data/lib/review/unfold.rb +138 -0
  42. data/lib/review/volume.rb +66 -0
  43. data/lib/review.rb +4 -0
  44. data/review.gemspec +93 -0
  45. data/setup.rb +1587 -0
  46. data/test/test_epubbuilder.rb +73 -0
  47. data/test/test_helper.rb +2 -0
  48. data/test/test_htmlbuilder.rb +42 -0
  49. data/test/test_latexbuilder.rb +74 -0
  50. metadata +122 -0
@@ -0,0 +1,525 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (c) 2010 Kenshi Muto and Masayoshi Takahashi
4
+ #
5
+ # This program is free software.
6
+ # You can distribute or modify this program under the terms of
7
+ # the GNU LGPL, Lesser General Public License version 2.1.
8
+ # For details of the GNU LGPL, see the file "COPYING".
9
+ #
10
+
11
+ require 'tmpdir'
12
+ require 'fileutils'
13
+ require 'yaml'
14
+ require 'optparse'
15
+ require 'rexml/document'
16
+
17
+ def main
18
+ values = { # These parameters can be overridden by YAML file.
19
+ "bookname"=> "sample", # it defines epub file name also
20
+ "booktitle" => "ReVIEW EPUBサンプル",
21
+ "aut" => "吟遊詩人", # author
22
+ "prt" => nil, # printer(publisher)
23
+ "asn" => nil, # associated name
24
+ "ant" => nil, # bibliographic antecedent
25
+ "clb" => nil, # Collaborator
26
+ "edt" => nil, # Editor
27
+ "dsr" => nil, # Designer
28
+ "ill" => nil, # Illustrator
29
+ "pht" => nil, # Photographer
30
+ "trl" => nil, # Translator
31
+ "date" => nil, # publishing date
32
+ "rights" => nil, # Copyright messages
33
+ "description" => nil, # Description
34
+ "urnid" => "example.jp", # just used for unique ID
35
+ "stylesheet" => "stylesheet.css", # stylesheet file
36
+ "coverfile" => nil, # content file of body of cover page
37
+ "mytoc" => nil, # whether make own table of contents or not
38
+ "params" => "", # specify review2epub parameters
39
+ "toclevel" => 3, # level of toc
40
+ "secnolevel" => 2, # level of section #
41
+ "posthook" => nil, # command path of post hook
42
+ "debug" => nil, # debug flag
43
+ }
44
+ yamlfile = ""
45
+
46
+ if ARGV.size == 1
47
+ values = values.merge(YAML.load_file(ARGV[0]))
48
+ yamlfile = ARGV[0]
49
+ end
50
+
51
+ bookname = values["bookname"]
52
+
53
+ if File.exist?("#{bookname}.epub")
54
+ STDERR.puts "#{bookname}.epub exists. Please remove or rename first."
55
+ exit 1
56
+ end
57
+ if File.exist?("#{bookname}")
58
+ STDERR.puts "#{bookname} directory exists. Please remove or rename first."
59
+ exit 1
60
+ end
61
+
62
+ identifier = "urn:uuid:#{values["urnid"]}_#{bookname}"
63
+
64
+ tmp = values["debug"].nil? ? Dir.mktmpdir : "."
65
+ Dir.mkdir("#{tmp}/#{bookname}")
66
+
67
+ # MIME type
68
+ File.open("#{tmp}/#{bookname}/mimetype", "w") {|f|
69
+ f.puts "application/epub+zip"
70
+ }
71
+
72
+ Dir.mkdir("#{tmp}/#{bookname}/OEBPS")
73
+ # XHTML
74
+ pre = 0
75
+ body = 0
76
+ post = 0
77
+ @manifeststr = ""
78
+ @ncxstr = ""
79
+ @tocdesc = Array.new
80
+ toccount = 2
81
+
82
+ if File.exists?("PREDEF")
83
+ File.open("PREDEF") {|chaps|
84
+ chaps.each_line {|l|
85
+ next if l =~ /^#/
86
+ pre = pre + 1
87
+ toccount = toccount + 1
88
+ fork {
89
+ STDOUT.reopen("#{tmp}/#{bookname}/OEBPS/pre#{pre}.html")
90
+ exec("review-compile --target=epub --level=#{values["secnolevel"]} #{values["params"]} #{l}")
91
+ }
92
+ Process.waitall
93
+ getanchors("#{tmp}/#{bookname}/OEBPS/pre#{pre}.html")
94
+ @manifeststr << %Q(<item id="pre#{pre}" href="pre#{pre}.html" media-type="application/xhtml+xml" />\n)
95
+ @ncxstr << %Q(<itemref idref="pre#{pre}" />\n)
96
+ }
97
+ }
98
+ end
99
+ if File.exists?("CHAPS")
100
+ File.open("CHAPS") {|chaps|
101
+ chaps.each_line {|l|
102
+ body = body + 1
103
+ toccount = toccount + 1
104
+ next if l =~ /^#/
105
+ fork {
106
+ STDOUT.reopen("#{tmp}/#{bookname}/OEBPS/chap#{body}.html")
107
+ exec("review-compile --target=epub --level=#{values["secnolevel"]} #{values["params"]} #{l}")
108
+ }
109
+ Process.waitall
110
+ getanchors("#{tmp}/#{bookname}/OEBPS/chap#{body}.html")
111
+ @manifeststr << %Q(<item id="chap#{body}" href="chap#{body}.html" media-type="application/xhtml+xml" />\n)
112
+ @ncxstr << %Q(<itemref idref="chap#{body}" />\n)
113
+ }
114
+ }
115
+ end
116
+ if File.exists?("POSTDEF")
117
+ File.open("POSTDEF") {|chaps|
118
+ chaps.each_line {|l|
119
+ next if l =~ /^#/
120
+ post = post + 1
121
+ toccount = toccount + 1
122
+ fork {
123
+ STDOUT.reopen("#{tmp}/#{bookname}/OEBPS/post#{post}.html")
124
+ exec("review-compile --target=epub --level=#{values["secnolevel"]} #{values["params"]} #{l}")
125
+ }
126
+ Process.waitall
127
+ getanchors("#{tmp}/#{bookname}/OEBPS/post#{post}.html")
128
+ @manifeststr << %Q(<item id="post#{post}" href="post#{post}.html" media-type="application/xhtml+xml" />\n)
129
+ @ncxstr << %Q(<itemref idref="post#{post}" />\n)
130
+ }
131
+ }
132
+ end
133
+ if File.exist?("images")
134
+ Dir.mkdir("#{tmp}/#{bookname}/OEBPS/images")
135
+ copyImagesToDir("images", "#{tmp}/#{bookname}/OEBPS/images")
136
+ end
137
+
138
+ # container
139
+ Dir.mkdir("#{tmp}/#{bookname}/META-INF")
140
+ File.open("#{tmp}/#{bookname}/META-INF/container.xml", "w") {|f|
141
+ f.puts <<EOT
142
+ <?xml version="1.0" encoding="UTF-8"?>
143
+ <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
144
+ <rootfiles>
145
+ <rootfile full-path="OEBPS/#{bookname}.opf" media-type="application/oebps-package+xml" />
146
+ </rootfiles>
147
+ </container>
148
+ EOT
149
+ }
150
+
151
+ # opf (meta info)
152
+ File.open("#{tmp}/#{bookname}/OEBPS/#{bookname}.opf", "w") {|f|
153
+ f.puts <<EOT
154
+ <?xml version="1.0" encoding="UTF-8"?>
155
+ <package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookId">
156
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
157
+ <dc:title>#{values["booktitle"]}</dc:title>
158
+ EOT
159
+
160
+ f.puts %Q(<dc:creator opf:role="aut">#{values["aut"]}</dc:creator>) unless values["aut"].nil? # FIXME: support multiple members
161
+
162
+ f.puts %Q(<dc:publisher>#{values["prt"]}</dc:publisher>) unless values["prt"].nil?
163
+
164
+ f.puts %Q(<dc:date>#{values["date"]}</dc:date>) unless values["date"].nil?
165
+ f.puts %Q(<dc:rights>#{values["rights"]}</dc:rights>) unless values["rights"].nil?
166
+
167
+ f.puts %Q(<dc:contributor opf:role="asn">#{values["asn"]}</dc:contributor>) unless values["asn"].nil?
168
+ f.puts %Q(<dc:contributor opf:role="ant">#{values["ant"]}</dc:contributor>) unless values["ant"].nil?
169
+ f.puts %Q(<dc:contributor opf:role="clb">#{values["clb"]}</dc:contributor>) unless values["clb"].nil?
170
+ f.puts %Q(<dc:contributor opf:role="edt">#{values["edt"]}</dc:contributor>) unless values["edt"].nil?
171
+ f.puts %Q(<dc:contributor opf:role="dsr">#{values["dsr"]}</dc:contributor>) unless values["dsr"].nil?
172
+ f.puts %Q(<dc:contributor opf:role="ill">#{values["ill"]}</dc:contributor>) unless values["ill"].nil?
173
+ f.puts %Q(<dc:contributor opf:role="pht">#{values["pht"]}</dc:contributor>) unless values["pht"].nil?
174
+ f.puts %Q(<dc:contributor opf:role="trl">#{values["trl"]}</dc:contributor>) unless values["trl"].nil?
175
+
176
+ f.puts %Q(<dc:description>#{values["description"]}</dc:description>) unless values["description"].nil?
177
+
178
+ if values["coverimage"]
179
+ f.puts %Q(<meta name="cover" content="#{getFigId(values["coverimage"])}"/>)
180
+ end
181
+ f.puts <<EOT
182
+ <dc:language>ja</dc:language>
183
+ <dc:identifier id="BookId">#{identifier}</dc:identifier>
184
+ </metadata>
185
+ <manifest>
186
+ <item id="ncx" href="#{bookname}.ncx" media-type="application/x-dtbncx+xml" />
187
+ <item id="style" href="#{values["stylesheet"]}" media-type="text/css" />
188
+ <item id="#{bookname}" href="#{bookname}.html" media-type="application/xhtml+xml" />
189
+ <item id="top" href="top.html" media-type="application/xhtml+xml" />
190
+ EOT
191
+
192
+ unless values["mytoc"].nil?
193
+ f.puts <<EOT
194
+ <item id="toc" href="toc.html" media-type="application/xhtml+xml" />
195
+ EOT
196
+ end
197
+
198
+ f.puts @manifeststr
199
+ f.puts <<EOT
200
+ </manifest>
201
+ <spine toc="ncx">
202
+ <itemref idref="#{bookname}" linear="no" />
203
+ <itemref idref="top" />
204
+ <itemref idref="toc" />
205
+ EOT
206
+ f.puts @ncxstr
207
+ f.puts <<EOT
208
+ </spine>
209
+ <guide>
210
+ <reference type="cover" title="表紙" href="#{bookname}.html"/>
211
+ <reference type="title-page" title="Title Page" href="top.html"/>
212
+ EOT
213
+ unless values["mytoc"].nil?
214
+ f.puts <<EOT
215
+ <reference type="toc" title="目次" href="toc.html"/>
216
+ EOT
217
+ end
218
+ f.puts <<EOT
219
+ </guide>
220
+ </package>
221
+ EOT
222
+ }
223
+
224
+ # ncx (toc)
225
+ File.open("#{tmp}/#{bookname}/OEBPS/#{bookname}.ncx", "w") {|f|
226
+ f.puts <<EOT
227
+ <?xml version="1.0" encoding="UTF-8"?>
228
+ <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
229
+ <head>
230
+ <meta name="dtb:uid" content="#{identifier}"/>
231
+ <meta name="dtb:depth" content="1"/>
232
+ <meta name="dtb:totalPageCount" content="0"/>
233
+ <meta name="dtb:maxPageNumber" content="0"/>
234
+ </head>
235
+ <docTitle>
236
+ <text>#{values["booktitle"]}</text>
237
+ </docTitle>
238
+ <docAuthor>
239
+ <text>#{values["aut"].nil? ? "" : values["aut"]}</text>
240
+ </docAuthor>
241
+ <navMap>
242
+ <navPoint id="top" playOrder="1">
243
+ <navLabel>
244
+ <text>#{values["booktitle"]}</text>
245
+ </navLabel>
246
+ <content src="#{bookname}.html"/>
247
+ </navPoint>
248
+ EOT
249
+
250
+ nav_count = 2
251
+
252
+ unless values["mytoc"].nil?
253
+ f.puts <<EOT
254
+ <navPoint id="toc" playOrder="2">
255
+ <navLabel>
256
+ <text>目次</text>
257
+ </navLabel>
258
+ <content src="toc.html"/>
259
+ </navPoint>
260
+ EOT
261
+ nav_count = 3
262
+ end
263
+
264
+ @tocdesc.each {|item|
265
+ level, file, id, content = item
266
+ # values["level"]
267
+ next if level > values["toclevel"].to_i
268
+ indent = ""
269
+ if level > values["secnolevel"].to_i
270
+ indent = "- "
271
+ end
272
+ f.puts <<EOT
273
+ <navPoint id="navPoint-#{nav_count}" playOrder="#{nav_count}">
274
+ <navLabel>
275
+ <text>#{indent}#{content}</text>
276
+ </navLabel>
277
+ <content src="#{file}##{id}"/>
278
+ </navPoint>
279
+ EOT
280
+ nav_count += 1
281
+ }
282
+ f.puts <<EOT
283
+ </navMap>
284
+ </ncx>
285
+ EOT
286
+ }
287
+
288
+ # Cover page
289
+ File.open("#{tmp}/#{bookname}/OEBPS/#{bookname}.html", "w") {|f|
290
+ f.puts <<EOT
291
+ <?xml version="1.0" encoding="UTF-8"?>
292
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
293
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ops="http://www.idpf.org/2007/ops" xml:lang="ja">
294
+ <head>
295
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
296
+ <meta http-equiv="Content-Style-Type" content="text/css"/>
297
+ <link rel="stylesheet" type="text/css" href="#{values["stylesheet"]}"/>
298
+ <meta name="generator" content="ReVIEW EPUB Maker"/>
299
+ <title>#{values["booktitle"]}</title>
300
+ </head>
301
+ <body>
302
+ EOT
303
+ if !values["coverfile"].nil? && File.exist?(values["coverfile"])
304
+ File.open(values["coverfile"]) {|f2|
305
+ f2.each_line {|l|
306
+ f.puts l
307
+ }
308
+ }
309
+ else
310
+ f.puts <<EOT
311
+ <h1>#{values["booktitle"]}</h1>
312
+ EOT
313
+ end
314
+
315
+ f.puts <<EOT
316
+ </body>
317
+ </html>
318
+ EOT
319
+ }
320
+
321
+
322
+ # Title page
323
+ File.open("#{tmp}/#{bookname}/OEBPS/top.html", "w") {|f|
324
+ f.puts <<EOT
325
+ <?xml version="1.0" encoding="UTF-8"?>
326
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
327
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ops="http://www.idpf.org/2007/ops" xml:lang="ja">
328
+ <head>
329
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
330
+ <meta http-equiv="Content-Style-Type" content="text/css"/>
331
+ <link rel="stylesheet" type="text/css" href="#{values["stylesheet"]}"/>
332
+ <meta name="generator" content="ReVIEW EPUB Maker"/>
333
+ <title>#{values["booktitle"]}</title>
334
+ </head>
335
+ <body>
336
+ EOT
337
+ if !values["titlepagefile"].nil? && File.exist?(values["titlepagefile"])
338
+ File.open(values["titlepagefile"]) {|f2|
339
+ f2.each_line {|l|
340
+ f.puts l
341
+ }
342
+ }
343
+ else
344
+ f.puts <<EOT
345
+ <h1 class="tp-title">#{values["booktitle"]}</h1>
346
+ EOT
347
+ if values["aut"]
348
+ f.puts <<EOT
349
+ <p>
350
+ <br />
351
+ <br />
352
+ </p>
353
+ <h2 class="tp-author">#{values["aut"]}</h2>
354
+ EOT
355
+ end
356
+ if values["prt"]
357
+ f.puts <<EOT
358
+ <p>
359
+ <br />
360
+ <br />
361
+ <br />
362
+ <br />
363
+ </p>
364
+ <h3 class="tp-publisher">#{values["prt"]}</h3>
365
+ EOT
366
+ end
367
+ end
368
+
369
+ f.puts <<EOT
370
+ </body>
371
+ </html>
372
+ EOT
373
+ }
374
+
375
+ # Additional toc page
376
+ unless values["mytoc"].nil?
377
+ File.open("#{tmp}/#{bookname}/OEBPS/toc.html", "w") {|f|
378
+ f.puts <<EOT
379
+ <?xml version="1.0" encoding="UTF-8"?>
380
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
381
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ops="http://www.idpf.org/2007/ops" xml:lang="ja">
382
+ <head>
383
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
384
+ <meta http-equiv="Content-Style-Type" content="text/css"/>
385
+ <link rel="stylesheet" type="text/css" href="#{values["stylesheet"]}"/>
386
+ <meta name="generator" content="ReVIEW EPUB Maker"/>
387
+ <title>目次</title>
388
+ </head>
389
+ <body>
390
+ <h1>目次</h1>
391
+ <ul class=\"toc-h1\">
392
+ EOT
393
+
394
+ current = 1
395
+ init_item = true
396
+ @tocdesc.each {|item|
397
+ level, file, id, content = item
398
+ # values["level"]
399
+ next if level > values["toclevel"].to_i
400
+ if level > current
401
+ f.puts ""
402
+ f.puts "<ul class=\"toc-h#{level}\">"
403
+ current = level
404
+ elsif level < current
405
+ f.puts "</li>"
406
+ f.puts "</ul>"
407
+ f.puts "</li>"
408
+ current = level
409
+ elsif init_item
410
+ # noop
411
+ else
412
+ f.puts "</li>"
413
+ end
414
+ f.write "<li><a href=\"#{file}##{id}\">#{content}</a>"
415
+ init_item = false
416
+ }
417
+
418
+ (current - 1).downto(1) {|n|
419
+ f.puts "</li>"
420
+ f.puts "</ul>"
421
+ }
422
+ if !init_item
423
+ f.puts "</li>"
424
+ end
425
+
426
+ f.puts <<EOT
427
+ </ul>
428
+ </body>
429
+ </html>
430
+ EOT
431
+ }
432
+ end
433
+
434
+ # stylesheet
435
+ File.open("#{tmp}/#{bookname}/OEBPS/stylesheet.css", "w") {|f|
436
+ f.puts <<EOT
437
+ /* sample style sheet for epub */
438
+ @charset "utf-8";
439
+
440
+ body {
441
+ }
442
+ EOT
443
+ }
444
+ if File.exist?(values["stylesheet"])
445
+ FileUtils.cp values["stylesheet"], "#{tmp}/#{bookname}/OEBPS/#{values["stylesheet"]}"
446
+ end
447
+
448
+ # hook
449
+ if !values["posthook"].nil? && !values["posthook"].empty? && FileTest.executable?(values["posthook"])
450
+ fork {
451
+ exec(values["posthook"], "#{tmp}/#{bookname}", Dir.pwd, yamlfile)
452
+ }
453
+ Process.waitall
454
+ end
455
+
456
+ # Zip epubファイルの作成。mimetypeは圧縮しないようにする
457
+ fork {
458
+ basedir = Dir.pwd
459
+ Dir.chdir("#{tmp}/#{bookname}") {|d|
460
+ exec("zip -0X #{basedir}/#{bookname}.epub mimetype")
461
+ }
462
+ }
463
+ Process.waitall
464
+ fork {
465
+ basedir = Dir.pwd
466
+ Dir.chdir("#{tmp}/#{bookname}") {|d|
467
+ exec("zip -Xr9D #{basedir}/#{bookname}.epub META-INF OEBPS")
468
+ }
469
+ }
470
+ Process.waitall
471
+
472
+ FileUtils.rm_r(tmp) if values["debug"].nil?
473
+ end
474
+
475
+ def copyImagesToDir(dirname, copybase)
476
+ Dir.open(dirname) {|dir|
477
+ dir.each {|fname|
478
+ next if fname =~ /^\./
479
+ if FileTest.directory?("#{dirname}/#{fname}")
480
+ copyImagesToDir("#{dirname}/#{fname}", "#{copybase}/#{fname}")
481
+ else
482
+ if fname =~ /\.(png|gif|jpg|jpeg|svg)$/i
483
+ Dir.mkdir(copybase) unless File.exist?(copybase)
484
+ FileUtils.cp "#{dirname}/#{fname}", copybase
485
+ figid = getFigId(fname)
486
+ mime = nil
487
+ case fname.downcase.match(/\.(png|gif|jpg|jpeg|svg)$/)[1]
488
+ when "png": mime = "image/png"
489
+ when "gif": mime = "image/gif"
490
+ when "jpg", "jpeg": mime = "image/jpeg"
491
+ when "svg": mime = "image/svg+xml"
492
+ else
493
+ raise "unsupported type #{fname}"
494
+ end
495
+ @manifeststr << %Q(<item id="#{figid}" href="#{dirname}/#{fname}" media-type="#{mime}" />\n)
496
+ end
497
+ end
498
+ }
499
+ }
500
+ end
501
+
502
+ def getFigId(filename)
503
+ figid = filename.sub(/\.(png|gif|jpg|jpeg|svg)$/, '')
504
+ "fig-#{figid}"
505
+ end
506
+
507
+ def getTitle(filename)
508
+ File.open(filename) {|f|
509
+ return REXML::Document.new(f).elements["//html/head/title"].text
510
+ }
511
+ end
512
+
513
+ def getanchors(filename)
514
+ File.open(filename) {|f|
515
+ file = filename.sub(/.+\//, '')
516
+ f.each_line {|l|
517
+ if l =~ /\A<h(\d)><a id=\"(.+?)\" \/>(.+?)<\/h/
518
+ # level, ID, content
519
+ @tocdesc << [$1.to_i, file, $2, $3]
520
+ end
521
+ }
522
+ }
523
+ end
524
+
525
+ main