review 0.6.0

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