review-retrovert 0.2.2 → 0.9.1

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/retrovert.yml +39 -0
  3. data/.gitignore +2 -2
  4. data/.ruby-version +1 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +68 -2
  7. data/README.md +6 -3
  8. data/lib/review/retrovert/cli.rb +2 -0
  9. data/lib/review/retrovert/converter.rb +255 -16
  10. data/lib/review/retrovert/version.rb +1 -1
  11. data/review-retrovert.gemspec +1 -1
  12. data/testdata/mybook/.gitignore +7 -0
  13. data/testdata/mybook/README.md +43 -0
  14. data/testdata/mybook/Rakefile +16 -0
  15. data/testdata/mybook/catalog.yml +33 -0
  16. data/testdata/mybook/config-starter.yml +139 -0
  17. data/testdata/mybook/config.yml +375 -0
  18. data/testdata/mybook/contents/00-preface.re +83 -0
  19. data/testdata/mybook/contents/01-install.re +272 -0
  20. data/testdata/mybook/contents/02-tutorial.re +1008 -0
  21. data/testdata/mybook/contents/03-syntax.re +2613 -0
  22. data/testdata/mybook/contents/04-customize.re +728 -0
  23. data/testdata/mybook/contents/05-faq.re +328 -0
  24. data/testdata/mybook/contents/06-bestpractice.re +971 -0
  25. data/testdata/mybook/contents/91-compare.re +18 -0
  26. data/testdata/mybook/contents/92-filelist.re +119 -0
  27. data/testdata/mybook/contents/93-background.re +267 -0
  28. data/testdata/mybook/contents/99-postface.re +38 -0
  29. data/testdata/mybook/css/normalize.css +349 -0
  30. data/testdata/mybook/css/webstyle.css +514 -0
  31. data/testdata/mybook/images/03-syntax/favicon-16x16.png +0 -0
  32. data/testdata/mybook/images/03-syntax/figure_heretop.png +0 -0
  33. data/testdata/mybook/images/03-syntax/tw-icon1.jpg +0 -0
  34. data/testdata/mybook/images/03-syntax/tw-icon2.jpg +0 -0
  35. data/testdata/mybook/images/03-syntax/tw-icon3.jpg +0 -0
  36. data/testdata/mybook/images/03-syntax/tw-icon4.jpg +0 -0
  37. data/testdata/mybook/images/04-customize/caption_pagebreak.png +0 -0
  38. data/testdata/mybook/images/04-customize/chaptitlepage_sample.png +0 -0
  39. data/testdata/mybook/images/05-faq/codeblock_rpadding1.png +0 -0
  40. data/testdata/mybook/images/05-faq/codeblock_rpadding2.png +0 -0
  41. data/testdata/mybook/images/06-bestpractice/figure_heretop.png +0 -0
  42. data/testdata/mybook/images/06-bestpractice/font_beramono.png +0 -0
  43. data/testdata/mybook/images/06-bestpractice/heading_design1.png +0 -0
  44. data/testdata/mybook/images/06-bestpractice/heading_design2.png +0 -0
  45. data/testdata/mybook/images/06-bestpractice/margin_book.png +0 -0
  46. data/testdata/mybook/images/06-bestpractice/multiline-title.png +0 -0
  47. data/testdata/mybook/images/06-bestpractice/preface_numbered.png +0 -0
  48. data/testdata/mybook/images/06-bestpractice/program_border.png +0 -0
  49. data/testdata/mybook/images/06-bestpractice/sechead_design_4.png +0 -0
  50. data/testdata/mybook/images/06-bestpractice/titlepage-samples.png +0 -0
  51. data/testdata/mybook/images/93-background/bug913.png +0 -0
  52. data/testdata/mybook/images/93-background/slide2.png +0 -0
  53. data/testdata/mybook/images/cover_a5.pdf +0 -0
  54. data/testdata/mybook/images/cover_b5.pdf +0 -0
  55. data/testdata/mybook/images/tw-icon.jpg +0 -0
  56. data/testdata/mybook/layouts/layout.epub.erb +29 -0
  57. data/testdata/mybook/layouts/layout.html5.erb +106 -0
  58. data/testdata/mybook/layouts/layout.tex.erb +546 -0
  59. data/testdata/mybook/lib/hooks/beforetexcompile.rb +55 -0
  60. data/testdata/mybook/lib/ruby/review-builder.rb +503 -0
  61. data/testdata/mybook/lib/ruby/review-cli.rb +58 -0
  62. data/testdata/mybook/lib/ruby/review-compiler.rb +523 -0
  63. data/testdata/mybook/lib/ruby/review-epubmaker.rb +606 -0
  64. data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +661 -0
  65. data/testdata/mybook/lib/ruby/review-latexbuilder.rb +782 -0
  66. data/testdata/mybook/lib/ruby/review-maker.rb +235 -0
  67. data/testdata/mybook/lib/ruby/review-monkeypatch.rb +91 -0
  68. data/testdata/mybook/lib/ruby/review-pdfmaker.rb +468 -0
  69. data/testdata/mybook/lib/ruby/review-textbuilder.rb +36 -0
  70. data/testdata/mybook/lib/ruby/review-tocparser.rb +285 -0
  71. data/testdata/mybook/lib/ruby/review-webmaker.rb +433 -0
  72. data/testdata/mybook/lib/tasks/mytasks.rake +31 -0
  73. data/testdata/mybook/lib/tasks/review.rake +142 -0
  74. data/testdata/mybook/lib/tasks/review.rake.orig +72 -0
  75. data/testdata/mybook/lib/tasks/starter.rake +326 -0
  76. data/testdata/mybook/locale.yml +6 -0
  77. data/testdata/mybook/review-ext.rb +206 -0
  78. data/testdata/mybook/sty/jumoline.sty +310 -0
  79. data/testdata/mybook/sty/mycolophon.sty +81 -0
  80. data/testdata/mybook/sty/mystyle.sty +8 -0
  81. data/testdata/mybook/sty/mytextsize.sty +61 -0
  82. data/testdata/mybook/sty/mytitlepage.sty +103 -0
  83. data/testdata/mybook/sty/reviewmacro.sty +60 -0
  84. data/testdata/mybook/sty/starter-codeblock.sty +332 -0
  85. data/testdata/mybook/sty/starter-color.sty +79 -0
  86. data/testdata/mybook/sty/starter-font.sty +112 -0
  87. data/testdata/mybook/sty/starter-heading.sty +514 -0
  88. data/testdata/mybook/sty/starter-note.sty +127 -0
  89. data/testdata/mybook/sty/starter-section.sty +262 -0
  90. data/testdata/mybook/sty/starter-toc.sty +72 -0
  91. data/testdata/mybook/sty/starter.sty +554 -0
  92. data/testdata/mybook/style.css +597 -0
  93. metadata +100 -3
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### ReVIEW::TEXTBuilderクラスを拡張する
5
+ ###
6
+
7
+ require 'review/textbuilder'
8
+
9
+
10
+ module ReVIEW
11
+
12
+ defined?(TEXTBuilder) or raise "internal error: TEXTBuilder not found."
13
+
14
+
15
+ class TEXTBuilder
16
+
17
+ ## ファイル名
18
+ def inline_file(str)
19
+ on_inline_file { escape(str) }
20
+ end
21
+ def on_inline_file
22
+ yield
23
+ end
24
+
25
+ ## ユーザ入力
26
+ def inline_userinput(str)
27
+ on_inline_input { escape(str) }
28
+ end
29
+ def on_inline_userinput
30
+ yield
31
+ end
32
+
33
+ end
34
+
35
+
36
+ end
@@ -0,0 +1,285 @@
1
+ #
2
+ # Copyright (c) 2008-2017 Minero Aoki, Kenshi Muto
3
+ # 2002-2007 Minero Aoki
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 'review/preprocessor'
12
+ require 'review/book'
13
+ require 'review/textbuilder'
14
+
15
+ module ReVIEW
16
+
17
+
18
+ class TOCParser
19
+
20
+ def self.parse(chap)
21
+ f = StringIO.new(chap.content, 'r:BOM|utf-8')
22
+ stream = Preprocessor::Strip.new(f)
23
+ parser = self.new
24
+ return parser.parse(stream, chap).map {|root|
25
+ root.number = chap.number
26
+ root
27
+ }
28
+ end
29
+
30
+ def self.chapter_node(chap)
31
+ toc = self.parse(chap)
32
+ if toc.size != 1
33
+ $stderr.puts "warning: chapter #{toc.join} contains more than 1 chapter"
34
+ end
35
+ toc.first
36
+ end
37
+
38
+ def parse(stream, chap)
39
+ f = stream
40
+ roots = [] # list of chapters
41
+ node_stack = []
42
+ filename = chap.path
43
+ while (line = f.gets)
44
+ case line
45
+ when /\A\#@\+\+\+$/
46
+ beg = f.lineno
47
+ line = f.gets() while line && line !~ /\A\#@---$/
48
+ line or
49
+ error! filename, beg, 'unterminated list'
50
+ next
51
+ when /\A\#@/
52
+ # do nothing
53
+ next
54
+ when /\A\s*\z/
55
+ # do nothing
56
+ next
57
+ when /\A(={2,})[\[\s\{]/
58
+ lev = $1.size
59
+ lev <= 6 or
60
+ error! filename, f.lineno, "section level too deep: #{lev}"
61
+ label = get_label(line)
62
+ if node_stack.empty?
63
+ # missing chapter label
64
+ dummy_chapter = Chapter.new(label, chap)
65
+ node_stack.push dummy_chapter
66
+ roots.push dummy_chapter
67
+ end
68
+ next if label.start_with?('[/') # ex) "[/column]"
69
+ sec = Section.new(lev, label.gsub(/\A\{.*?\}\s?/, ''))
70
+ node_stack.pop until node_stack.last.level < sec.level
71
+ node_stack.last.add_child sec
72
+ node_stack.push sec
73
+
74
+ when /\A=[^=]/
75
+ label = get_label(line)
76
+ node_stack.clear
77
+ new_chapter = Chapter.new(label, chap)
78
+ node_stack.push new_chapter
79
+ roots.push new_chapter
80
+
81
+ when %r{\A//\w+(?:\[.*?\])*\{\s*\z}
82
+ ! node_stack.empty? or
83
+ error! filename, f.lineno, 'list found before section label'
84
+ node_stack.last.add_child(list = List.new)
85
+ beg = f.lineno
86
+ list.add line
87
+ while (line = f.gets) && ! line.start_with?('//}')
88
+ list.add line
89
+ end
90
+ line or
91
+ error! filename, beg, 'unterminated list'
92
+
93
+ when /\A\/\/\w/
94
+ # do nothing
95
+ next
96
+ else
97
+ # if node_stack.empty?
98
+ # error! filename, f.lineno, 'text found before section label'
99
+ # end
100
+ next if node_stack.empty?
101
+ node_stack.last.add_child(par = Paragraph.new(chap))
102
+ par.add line
103
+ while (line = f.gets) && line !~ /\A\s*\z/
104
+ par.add line
105
+ end
106
+ end
107
+ end
108
+
109
+ roots
110
+ end
111
+
112
+ def get_label(line)
113
+ line = line.strip.sub(/\A=+\s*/, '')
114
+ compile_label(line)
115
+ end
116
+
117
+ def compile_label(line)
118
+ b = ReVIEW::TEXTBuilder.new
119
+ dummy_book = ReVIEW::Book::Base.load
120
+ dummy_chapter = ReVIEW::Book::Chapter.new(dummy_book, 1, '-', nil, StringIO.new)
121
+ dummy_loc = Location.new('', StringIO.new)
122
+ b.bind(ReVIEW::Compiler.new(b), dummy_chapter, dummy_loc)
123
+ b.compile_inline(line)
124
+ end
125
+
126
+ def error!(filename, lineno, msg)
127
+ raise "#{filename}:#{lineno}: #{msg}"
128
+ end
129
+
130
+ # class Node
131
+ # def initialize(children = [])
132
+ # @children = children
133
+ # end
134
+ #
135
+ # attr_reader :children
136
+ #
137
+ # def add_child(c)
138
+ # @children.push c
139
+ # end
140
+ #
141
+ # def each_node(&block)
142
+ # @children.each do |c|
143
+ # yield c
144
+ # c.each(&block)
145
+ # end
146
+ # end
147
+ #
148
+ # def each_child(&block)
149
+ # @children.each(&block)
150
+ # end
151
+ #
152
+ # def chapter?
153
+ # false
154
+ # end
155
+ #
156
+ # def each_section(&block)
157
+ # @children.each { |n| n.yield_section(&block) }
158
+ # end
159
+ #
160
+ # def each_section_with_index
161
+ # i = 0
162
+ # each_section do |n|
163
+ # yield n, i
164
+ # i += 1
165
+ # end
166
+ # end
167
+ #
168
+ # def section_size
169
+ # cnt = 0
170
+ # @children.each { |n| n.yield_section { cnt += 1 } }
171
+ # cnt
172
+ # end
173
+ # end
174
+ #
175
+ # class Section < Node
176
+ # def initialize(level, label, path = nil)
177
+ # super()
178
+ # @level = level
179
+ # @label = label
180
+ # @filename = path ? real_filename(path) : nil
181
+ # end
182
+ #
183
+ # def real_filename(path)
184
+ # if FileTest.symlink?(path)
185
+ # File.basename(File.readlink(path))
186
+ # else
187
+ # File.basename(path)
188
+ # end
189
+ # end
190
+ # private :real_filename
191
+ #
192
+ # attr_reader :level
193
+ # attr_reader :label
194
+ #
195
+ # def estimated_lines
196
+ # @children.inject(0) { |sum, n| sum + n.estimated_lines }
197
+ # end
198
+ #
199
+ # def yield_section
200
+ # yield self
201
+ # end
202
+ #
203
+ # def inspect
204
+ # "#<#{self.class} level=#{@level} #{@label}>"
205
+ # end
206
+ # end
207
+ #
208
+ # class Chapter < Section
209
+ # def initialize(label, chap)
210
+ # super 1, label, chap.path
211
+ # @chapter = chap
212
+ # @chapter_id = chap.id
213
+ # @path = chap.path
214
+ # @page_metric = chap.book.page_metric
215
+ # @volume = nil
216
+ # @number = nil
217
+ # end
218
+ #
219
+ # attr_accessor :number
220
+ #
221
+ # def chapter?
222
+ # true
223
+ # end
224
+ #
225
+ # attr_reader :chapter_id
226
+ #
227
+ # def volume
228
+ # return @volume if @volume
229
+ # @volume = @chapter.volume
230
+ # @volume.lines = estimated_lines
231
+ # @volume
232
+ # end
233
+ #
234
+ # def inspect
235
+ # "#<#{self.class} #{@filename}>"
236
+ # end
237
+ # end
238
+ #
239
+ # class Paragraph < Node
240
+ # def initialize(chap)
241
+ # @bytes = 0
242
+ # @page_metric = chap.book.page_metric
243
+ # end
244
+ #
245
+ # def inspect
246
+ # "#<#{self.class}>"
247
+ # end
248
+ #
249
+ # def add(line)
250
+ # @bytes += line.strip.bytesize
251
+ # end
252
+ #
253
+ # def estimated_lines
254
+ # (@bytes + 2) / @page_metric.text.n_columns + 1
255
+ # end
256
+ #
257
+ # def yield_section
258
+ # end
259
+ # end
260
+ #
261
+ # class List < Node
262
+ # def initialize
263
+ # @lines = 0
264
+ # end
265
+ #
266
+ # def inspect
267
+ # "#<#{self.class}>"
268
+ # end
269
+ #
270
+ # def add(_line)
271
+ # @lines += 1
272
+ # end
273
+ #
274
+ # def estimated_lines
275
+ # @lines + 2
276
+ # end
277
+ #
278
+ # def yield_section
279
+ # end
280
+ # end
281
+
282
+ end
283
+
284
+
285
+ end
@@ -0,0 +1,433 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### ReVIEW::WEBMakerクラスを書き直す
5
+ ### (でも元がひどすぎるので書き直すにも限界がある)
6
+ ###
7
+
8
+ require 'pathname'
9
+ require 'fileutils'
10
+ require 'erb'
11
+ require 'review/converter'
12
+ require 'review/htmlbuilder'
13
+ require 'review/template'
14
+ require 'review/webmaker'
15
+ require_relative './review-maker'
16
+
17
+
18
+ module ReVIEW
19
+
20
+ remove_const :WEBMaker
21
+
22
+
23
+ class WEBMaker < Maker
24
+
25
+ SCRIPT_NAME = "review-webmaker"
26
+
27
+ def initialize(*args)
28
+ super
29
+ @config['htmlext'] = 'html'
30
+ @docroot = @config['docroot'] || 'webroot'
31
+ end
32
+
33
+ def generate()
34
+ remove_old_files()
35
+ FileUtils.mkdir_p(@docroot)
36
+ #
37
+ @book = ReVIEW::Book.load(@basedir)
38
+ @book.config = @config
39
+ #
40
+ copy_stylesheet()
41
+ copy_frontmatter()
42
+ build_body()
43
+ copy_backmatter()
44
+ #
45
+ imagedir = @config['imagedir']
46
+ destdir = "#{@docroot}/#{imagedir}"
47
+ copy_images(imagedir, destdir)
48
+ #
49
+ copy_resources('covers', destdir)
50
+ copy_resources('adv', destdir)
51
+ copy_resources(@config['fontdir'], "#{@docroot}/fonts", @config['font_ext'])
52
+ end
53
+
54
+ def build_body()
55
+ base_path = Pathname.new(@basedir)
56
+ @converter = ReVIEW::Converter.new(@book, new_builder())
57
+ @book.parts.each do |part|
58
+ if part.name.present?
59
+ if part.file?
60
+ build_chap(part, base_path, true)
61
+ else
62
+ htmlfile = "part_#{part.number}.#{@config['htmlext']}"
63
+ build_part(part, htmlfile)
64
+ # title = ReVIEW::I18n.t('part', part.number)
65
+ # title += ReVIEW::I18n.t('chapter_postfix') + part.name.strip unless part.name.strip.empty?
66
+ end
67
+ end
68
+ part.chapters.each do |chap|
69
+ build_chap(chap, base_path, false)
70
+ end
71
+ end
72
+ end
73
+
74
+ def new_builder()
75
+ builder = ReVIEW::WEBBuilder.new()
76
+ builder.starter_config = @starter_config
77
+ return builder
78
+ end
79
+
80
+ def new_renderer()
81
+ return WEBRenderer.new(@config, @book, @basedir, @starter_config)
82
+ end
83
+
84
+ def build_part(part, htmlfile)
85
+ new_renderer().render_part(part, File.join(@docroot, htmlfile))
86
+ end
87
+
88
+ def build_chap(chap, base_path, ispart)
89
+ filename = \
90
+ if ispart.present?
91
+ chap.path
92
+ else
93
+ Pathname.new(chap.path).relative_path_from(base_path).to_s
94
+ end
95
+ chapter_id = filename.sub(/\.re\Z/, '')
96
+ htmlfile = "#{chapter_id}.#{@config['htmlext']}"
97
+ #
98
+ if @config['params'].present?
99
+ warn "'params:' in config.yml is obsoleted."
100
+ end
101
+ #
102
+ begin
103
+ @converter.convert(filename, File.join(@docroot, htmlfile))
104
+ rescue => ex
105
+ warn "compile error in #{filename} (#{ex.class})"
106
+ warn ex.message
107
+ generate_errorpage(ex, filename, htmlfile)
108
+ end
109
+ end
110
+
111
+ def remove_old_files()
112
+ math_dir = "./#{@config['imagedir']}/_review_math"
113
+ FileUtils.rm_rf(math_dir) if @config['imgmath'] && Dir.exist?(math_dir)
114
+ FileUtils.rm_rf(@docroot)
115
+ end
116
+
117
+ def copy_images(srcdir, destdir)
118
+ copy_resources(srcdir, destdir, nil)
119
+ end
120
+
121
+ def copy_resources(srcdir, destdir, allow_exts=nil)
122
+ if srcdir && File.exist?(srcdir)
123
+ FileUtils.mkdir_p(destdir)
124
+ allow_exts ||= @config['image_ext']
125
+ ok = allow_exts && ! allow_exts.empty?
126
+ ext_rexp = ok ? /\.(#{allow_exts.join('|')})\Z/i : nil
127
+ _recursive_copy_files(srcdir, destdir, ext_rexp)
128
+ end
129
+ end
130
+
131
+ def _recursive_copy_files(srcdir, destdir, ext_rexp)
132
+ Dir.open(srcdir) do |dir|
133
+ dir.each do |fname|
134
+ next if fname.start_with?('.')
135
+ srcpath = "#{srcdir}/#{fname}"
136
+ if File.directory?(srcpath)
137
+ destpath = "#{destdir}/#{fname}"
138
+ _recursive_copy_files(srcpath, destpath, ext_rexp)
139
+ elsif ext_rexp.nil? || fname =~ ext_rexp
140
+ FileUtils.mkdir_p(destdir)
141
+ FileUtils.cp(srcpath, destdir)
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def copy_stylesheet()
148
+ cssdir = File.join(@docroot, "css")
149
+ Dir.mkdir(cssdir) unless File.directory?(cssdir)
150
+ [@config['stylesheet']].flatten.compact.each do |x|
151
+ FileUtils.cp("css/#{x}", cssdir)
152
+ end
153
+ end
154
+
155
+ def copy_frontmatter()
156
+ generate_indexpage('index.html')
157
+ #
158
+ if @config['titlepage']
159
+ destfile = "titlepage.#{@config['htmlext']}"
160
+ if @config['titlefile']
161
+ FileUtils.cp(@config['titlefile'], "#{@docroot}/#{destfile}")
162
+ else
163
+ generate_titlepage(destfile)
164
+ end
165
+ end
166
+ #
167
+ _copy_file(@config['creditfile'])
168
+ _copy_file(@config['originaltitlefile'])
169
+ end
170
+
171
+ def generate_errorpage(ex, filename, htmlfile)
172
+ s = new_renderer().render_exception(ex, filename)
173
+ File.write(File.join(@docroot, htmlfile), s)
174
+ end
175
+
176
+ def generate_indexpage(htmlfile)
177
+ s = new_renderer().render_indexpage()
178
+ File.write(File.join(@docroot, htmlfile), s)
179
+ rescue => ex
180
+ generate_errorpage(ex, nil, htmlfile)
181
+ end
182
+
183
+ def generate_titlepage(htmlfile)
184
+ s = new_renderer().render_titlepage()
185
+ File.write(File.join(@docroot, htmlfile), s)
186
+ end
187
+
188
+ def copy_backmatter()
189
+ conf = @config
190
+ ext = conf['htmlext']
191
+ _copy_file(conf['profile'])
192
+ _copy_file(conf['advfile'])
193
+ _copy_file(conf['colophon'], "colophon.#{ext}") if conf['colophon'].is_a?(String)
194
+ _copy_file(conf['backcover'])
195
+ end
196
+
197
+ def _copy_file(srcfile, destfile=nil)
198
+ if srcfile && File.exist?(srcfile)
199
+ destfile ||= File.basename(srcfile)
200
+ FileUtils.cp(srcfile, File.join(@docroot, destfile))
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+
207
+ class WEBBuilder < HTMLBuilder
208
+
209
+ def layoutfile
210
+ "layouts/layout.html5.erb"
211
+ end
212
+
213
+ end
214
+
215
+
216
+ class WEBRenderer < BaseRenderer
217
+
218
+ def initialize(*args)
219
+ super
220
+ @language = @config['language']
221
+ @stylesheets = @config['stylesheet']
222
+ end
223
+
224
+ def layout_template_name()
225
+ #if @config['htmlversion'].to_i == 5
226
+ # 'web/html/layout-html5.html.erb'
227
+ #else
228
+ # 'web/html/layout-xhtml1.html.erb'
229
+ #end
230
+ "layout.html5.erb"
231
+ end
232
+
233
+ def render_exception(error, textfile)
234
+ @error = error
235
+ @textfile = textfile
236
+ #
237
+ return render()
238
+ end
239
+
240
+ def render_part(part)
241
+ part_name = part.name.strip
242
+ #
243
+ sb = ""
244
+ sb << "<div class=\"part\">\n"
245
+ sb << "<h1 class=\"part-number\">#{i18n('part', part.number)}</h1>\n"
246
+ sb << "<h2 class=\"part-title\">#{part_name}</h2>\n" if part_name.present?
247
+ sb << "</div>\n"
248
+ @body = sb
249
+ #
250
+ return render()
251
+ end
252
+
253
+ def render_indexpage()
254
+ if @config['coverimage']
255
+ imgfile = File.join(@config['imagedir'], @config['coverimage'])
256
+ else
257
+ imgfile = nil
258
+ end
259
+ #
260
+ sb = ""
261
+ if imgfile
262
+ sb << " <div id=\"cover-image\" class=\"cover-image\">\n"
263
+ sb << " <img src=\"#{imgfile}\" class=\"max\"/>\n"
264
+ sb << " </div>\n"
265
+ end
266
+ @body = sb
267
+ @toc = ReVIEW::WEBTOCPrinter.book_to_html(@book, nil)
268
+ @next = @book.chapters[0]
269
+ @next_title = @next ? @next.title : ''
270
+ #
271
+ return render()
272
+ end
273
+
274
+ def render_titlepage()
275
+ author = @config['aut'] ? _join_names(@config.names_of('aut')) : nil
276
+ publisher = @config['pbl'] ? _join_names(@config.names_of('pbl')) : nil
277
+ #
278
+ sb = ""
279
+ sb << "<div class=\"titlepage\">\n"
280
+ sb << "<h1 class=\"tp-title\">#{h @config.name_of('booktitle')}</h1>\n"
281
+ sb << "<h2 class=\"tp-author\">#{author}</h2>\n" if author
282
+ sb << "<h3 class=\"tp-publisher\">#{publisher}</h3>\n" if publisher
283
+ sb << "</div>\n"
284
+ @body = sb
285
+ #
286
+ return render()
287
+ end
288
+
289
+ private
290
+
291
+ def _join_names(names)
292
+ sep = i18n('names_splitter')
293
+ return [names].flatten.join(sep)
294
+ end
295
+
296
+ end
297
+
298
+
299
+ class WEBTOCPrinter
300
+
301
+ def self.book_to_html(book, current_chapter)
302
+ printer = self.new(nil, nil, nil)
303
+ arr = printer.handle_book(book, current_chapter)
304
+ html = printer.render_html(arr)
305
+ return html
306
+ end
307
+
308
+ def render_html(arr)
309
+ tag, attr, children = arr
310
+ tag == "book" or raise "internal error: tag=#{tag.inspect}"
311
+ buf = "<ul class=\"toc toc-1\">\n"
312
+ children.each do |child|
313
+ _render_li(child, buf, 1)
314
+ end
315
+ buf << "</ul>\n"
316
+ return buf
317
+ end
318
+
319
+ def parse_inline(str)
320
+ builder = HTMLBuilder.new
321
+ builder.instance_variable_set('@book', @_book)
322
+ @compiler ||= Compiler.new(builder)
323
+ return @compiler.text(str)
324
+ end
325
+
326
+ def _render_li(arr, buf, n)
327
+ tag, attr, children = arr
328
+ case tag
329
+ when "part"
330
+ buf << "<li class=\"toc-part\">#{parse_inline(attr[:label])}\n"
331
+ buf << " <ul class=\"toc toc-#{n+1}\">\n"
332
+ children.each {|child| _render_li(child, buf, n+1) }
333
+ buf << " </ul>\n"
334
+ buf << "</li>\n"
335
+ when "chapter"
336
+ buf << " <li class=\"toc-chapter\"><a href=\"#{h attr[:path]}\">#{parse_inline(attr[:label])}</a>"
337
+ if children && !children.empty?
338
+ buf << "\n <ul class=\"toc toc-#{n+1}\">\n"
339
+ children.each {|child| _render_li(child, buf, n+1) }
340
+ buf << " </ul>\n"
341
+ buf << " </li>\n"
342
+ else
343
+ buf << "</li>\n"
344
+ end
345
+ when "section"
346
+ buf << " <li class=\"toc-section\"><a href=\"##{attr[:anchor]}\">#{parse_inline(attr[:label])}</a></li>\n"
347
+ end
348
+ buf
349
+ end
350
+
351
+ def handle_book(book, current_chapter)
352
+ @_book = book
353
+ children = []
354
+ book.each_part do |part|
355
+ if part.number
356
+ children << handle_part(part, current_chapter)
357
+ else
358
+ part.each_chapter do |chap|
359
+ children << handle_chapter(chap, current_chapter)
360
+ end
361
+ end
362
+ end
363
+ #
364
+ attrs = {
365
+ title: book.config['booktitle'],
366
+ subtitle: book.config['subtitle'],
367
+ }
368
+ return ["book", attrs, children]
369
+ end
370
+
371
+ def handle_part(part, current_chapter)
372
+ children = []
373
+ part.each_chapter do |chap|
374
+ children << handle_chapter(chap, current_chapter)
375
+ end
376
+ #
377
+ attrs = {
378
+ number: part.number,
379
+ title: part.title,
380
+ #label: "#{I18n.t('part_short', part.number)} #{part.title}",
381
+ label: "#{I18n.t('part', part.number)} #{part.title}",
382
+ }
383
+ return ["part", attrs, children]
384
+ end
385
+
386
+ def handle_chapter(chap, current_chapter)
387
+ children = []
388
+ if current_chapter.nil? || chap == current_chapter
389
+ chap.headline_index.each do |sec|
390
+ next if sec.number.present? && sec.number.length >= 2
391
+ children << handle_section(sec, chap)
392
+ end
393
+ end
394
+ #
395
+ chap_node = TOCParser.chapter_node(chap)
396
+ ext = chap.book.config['htmlext'] || 'html'
397
+ path = chap.path.sub(/\.re\z/, ".#{ext}")
398
+ label = if chap_node.number && chap.on_chaps?
399
+ "#{I18n.t('chapter_short', chap.number)} #{chap.title}"
400
+ else
401
+ chap.title
402
+ end
403
+ #
404
+ attrs = {
405
+ number: chap_node.number,
406
+ title: chap.title,
407
+ label: label,
408
+ path: path,
409
+ }
410
+ return ["chapter", attrs, children]
411
+ end
412
+
413
+ def handle_section(sec, chap)
414
+ if chap.number && sec.number.length > 0
415
+ number = [chap.number] + sec.number
416
+ label = "#{number.join('.')} #{sec.caption}"
417
+ else
418
+ number = nil
419
+ label = sec.caption
420
+ end
421
+ attrs = {
422
+ number: number,
423
+ title: sec.caption,
424
+ label: label,
425
+ anchor: "h#{number ? number.join('-') : ''}",
426
+ }
427
+ return ["section", attrs, []]
428
+ end
429
+
430
+ end
431
+
432
+
433
+ end