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,31 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ##
4
+ ## これはreviewstarterが用意したファイル。
5
+ ## ユーザ独自のRakeタスクをここに定義する。
6
+ ##
7
+
8
+
9
+ #
10
+ #def render_eruby_files(param) # 要 Ruby >= 2.2
11
+ # Dir.glob('**/*.eruby').each do |erubyfile|
12
+ # origfile = erubyfile.sub(/\.eruby$/, '')
13
+ # sh "erb -T 2 '#{param}' #{erubyfile} > #{origfile}"
14
+ # end
15
+ #end
16
+ #
17
+ #
18
+ #namespace :setup do
19
+ #
20
+ # desc "*印刷用に設定 (B5, 10pt, mono)"
21
+ # task :printing do
22
+ # render_eruby_files('buildmode=printing')
23
+ # end
24
+ #
25
+ # desc "*タブレット用に設定 (A5, 10pt, color)"
26
+ # task :tablet do
27
+ # render_eruby_files('buildmode=tablet')
28
+ # end
29
+ #
30
+ #end
31
+ #
@@ -0,0 +1,142 @@
1
+ require 'fileutils'
2
+ require 'rake/clean'
3
+
4
+ BOOK = 'book'
5
+ BOOK_PDF = BOOK + '.pdf'
6
+ BOOK_EPUB = BOOK + '.epub'
7
+ CONFIG_FILE = 'config.yml'
8
+ WEBROOT = 'webroot'
9
+
10
+ def build(mode, chapter)
11
+ sh "review-compile --target=#{mode} --footnotetext --stylesheet=style.css #{chapter} > tmp"
12
+ mode_ext = { 'html' => 'html', 'latex' => 'tex', 'idgxml' => 'xml' }
13
+ FileUtils.mv 'tmp', chapter.gsub(/re\z/, mode_ext[mode])
14
+ end
15
+
16
+ def build_all(mode)
17
+ sh "review-compile --target=#{mode} --footnotetext --stylesheet=style.css"
18
+ end
19
+
20
+ def config_file()
21
+ conf = ENV['config']
22
+ conf = CONFIG_FILE if conf.nil? || conf.empty?
23
+ return conf
24
+ end
25
+
26
+ task default: :html_all
27
+
28
+ desc 'build html (Usage: rake build re=target.re)'
29
+ task :html => :prepare do
30
+ if ENV['re'].nil?
31
+ puts 'Usage: rake build re=target.re'
32
+ exit
33
+ end
34
+ build('html', ENV['re'])
35
+ end
36
+
37
+ desc 'build all html'
38
+ task :html_all => :prepare do
39
+ build_all('html')
40
+ end
41
+
42
+ desc 'preproc all'
43
+ task :preproc => :prepare do
44
+ Dir.glob('*.re').each do |file|
45
+ sh "review-preproc --replace #{file}"
46
+ end
47
+ end
48
+
49
+ desc 'generate PDF and EPUB file'
50
+ task all: %i[pdf epub]
51
+
52
+ desc 'generate PDF file'
53
+ task :pdf => :prepare do
54
+ require 'review'
55
+ #require 'review/pdfmaker'
56
+ require './lib/ruby/review-pdfmaker'
57
+ #
58
+ FileUtils.rm_rf [BOOK_PDF, BOOK, BOOK + '-pdf']
59
+ begin
60
+ ReVIEW::PDFMaker.execute(config_file())
61
+ rescue RuntimeError => ex
62
+ if ex.message =~ /^failed to run command:/
63
+ abort "*\n* ERROR (review-pdfmaker):\n* #{ex.message}\n*"
64
+ else
65
+ raise
66
+ end
67
+ end
68
+ end
69
+
70
+ desc 'generate EPUB file'
71
+ task :epub => :prepare do
72
+ FileUtils.rm_rf [BOOK_EPUB, BOOK, BOOK + '-epub']
73
+ #sh "review-epubmaker #{config_file()}"
74
+ require 'review'
75
+ require './lib/ruby/review-epubmaker'
76
+ ReVIEW::EpubMaker.execute(config_file())
77
+ end
78
+
79
+ desc 'generate stagic HTML file for web'
80
+ task :web => :prepare do
81
+ FileUtils.rm_rf [WEBROOT]
82
+ #sh "review-webmaker #{config_file()}"
83
+ require 'review'
84
+ #require 'review/webmaker'
85
+ require './lib/ruby/review-webmaker'
86
+ ReVIEW::WEBMaker.execute(config_file())
87
+ end
88
+
89
+
90
+ #desc "+ copy *.re files under 'contents/' into here."
91
+ task :prepare do
92
+ require 'yaml'
93
+ config = YAML.load_file(config_file())
94
+ PREPARES.each do |x|
95
+ case x
96
+ when Proc; x.call(config)
97
+ else ; __send__(x, config)
98
+ end
99
+ end
100
+ end
101
+
102
+ ## 前処理を追加したい場合は、関数名またはProcオブジェクトをこの配列に追加する
103
+ PREPARES = [:copy_files_under_contentdir]
104
+
105
+ def copy_files_under_contentdir(config)
106
+ ## 設定ファイル (config.yml) の「contentdir:」の値を調べる
107
+ contdir = config['contentdir']
108
+ if contdir && contdir != '.' # '.' はカレントディレクトリを表すので対象外
109
+ ## もし contentdir が存在しなければエラー
110
+ unless File.directory?(contdir)
111
+ abort_with_message <<END
112
+ ERROR: Content directory '#{contdir}' not exist.
113
+ Please check 'contentdir:' value in config file,
114
+ or create '#{contdir}' directory and put *.re files into there.
115
+ END
116
+ end
117
+ ## カレントディレクトリに *.re ファイルがあればエラー
118
+ unless Dir.glob('*.re').empty?
119
+ re_file = Dir.glob('*.re').sort.first
120
+ abort_with_message <<END
121
+ ERROR: File '#{re_file}' found in current directory.
122
+ When 'contentdir:' specifieid in config file,
123
+ you must put all *.re files into that directory.
124
+ Please remove *.re files from current directory.
125
+ END
126
+ end
127
+ ## contentdir にある *.re ファイルをカレントディレクトリにコピー
128
+ cp Dir.glob(File.join(contdir, '*.re')), '.', :verbose=>false
129
+ ## コピーしたファイルをRakeプロセス終了時に削除
130
+ at_exit { rm_f Dir.glob('*.re'), :verbose=>false }
131
+ end
132
+ end
133
+
134
+ def abort_with_message(error_msg)
135
+ $stderr.puts "***"
136
+ $stderr.puts error_msg.gsub(/^/, '*** ')
137
+ $stderr.puts "***"
138
+ abort()
139
+ end
140
+
141
+
142
+ CLEAN.include([BOOK, BOOK_PDF, BOOK_EPUB, BOOK + '-pdf', BOOK + '-epub', WEBROOT, 'images/_review_math'])
@@ -0,0 +1,72 @@
1
+ require 'fileutils'
2
+ require 'rake/clean'
3
+
4
+ BOOK = 'book'
5
+ BOOK_PDF = BOOK + '.pdf'
6
+ BOOK_EPUB = BOOK + '.epub'
7
+ CONFIG_FILE = 'config.yml'
8
+ WEBROOT = 'webroot'
9
+
10
+ def build(mode, chapter)
11
+ sh "review-compile --target=#{mode} --footnotetext --stylesheet=style.css #{chapter} > tmp"
12
+ mode_ext = { 'html' => 'html', 'latex' => 'tex', 'idgxml' => 'xml' }
13
+ FileUtils.mv 'tmp', chapter.gsub(/re\z/, mode_ext[mode])
14
+ end
15
+
16
+ def build_all(mode)
17
+ sh "review-compile --target=#{mode} --footnotetext --stylesheet=style.css"
18
+ end
19
+
20
+ task default: :html_all
21
+
22
+ desc 'build html (Usage: rake build re=target.re)'
23
+ task :html do
24
+ if ENV['re'].nil?
25
+ puts 'Usage: rake build re=target.re'
26
+ exit
27
+ end
28
+ build('html', ENV['re'])
29
+ end
30
+
31
+ desc 'build all html'
32
+ task :html_all do
33
+ build_all('html')
34
+ end
35
+
36
+ desc 'preproc all'
37
+ task :preproc do
38
+ Dir.glob('*.re').each do |file|
39
+ sh "review-preproc --replace #{file}"
40
+ end
41
+ end
42
+
43
+ desc 'generate PDF and EPUB file'
44
+ task all: %i[pdf epub]
45
+
46
+ desc 'generate PDF file'
47
+ task pdf: BOOK_PDF
48
+
49
+ desc 'generate stagic HTML file for web'
50
+ task web: WEBROOT
51
+
52
+ desc 'generate EPUB file'
53
+ task epub: BOOK_EPUB
54
+
55
+ SRC = FileList['*.re'] + [CONFIG_FILE]
56
+
57
+ file BOOK_PDF => SRC do
58
+ FileUtils.rm_rf [BOOK_PDF, BOOK, BOOK + '-pdf']
59
+ sh "review-pdfmaker #{CONFIG_FILE}"
60
+ end
61
+
62
+ file BOOK_EPUB => SRC do
63
+ FileUtils.rm_rf [BOOK_EPUB, BOOK, BOOK + '-epub']
64
+ sh "review-epubmaker #{CONFIG_FILE}"
65
+ end
66
+
67
+ file WEBROOT => SRC do
68
+ FileUtils.rm_rf [WEBROOT]
69
+ sh "review-webmaker #{CONFIG_FILE}"
70
+ end
71
+
72
+ CLEAN.include([BOOK, BOOK_PDF, BOOK_EPUB, BOOK + '-pdf', BOOK + '-epub', WEBROOT, 'images/_review_math'])
@@ -0,0 +1,326 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ WEBSERVER_PORT = 9000
5
+
6
+
7
+ ##
8
+ ## 生成したファイルを削除
9
+ ##
10
+ task :clean do
11
+ require 'yaml'
12
+ config = File.open("config.yml", 'r', encoding: 'utf-8') {|f| YAML.load(f) }
13
+ x = config['bookname'] or abort("ERROR: missing 'bookname' in config.yml")
14
+ files = [x+".pdf", x+"-pdf", x+".epub", x+"-epub", "webroot"]
15
+ files = files.select {|x| File.exist?(x) }
16
+ rm_rf files unless files.empty?
17
+ end
18
+
19
+
20
+
21
+ ##
22
+ ## Docker用のタスク。
23
+ ##
24
+ namespace :docker do
25
+
26
+ docker_image = ENV['DOCKER_IMAGE']
27
+ docker_image = "kauplan/review2.5" if docker_image.to_s.empty?
28
+ docker_opts = "--rm -v $PWD:/work -w /work"
29
+ docker_opts += ENV.keys().grep(/^STARTER_/).map {|k| " -e #{k}" }.join()
30
+
31
+ desc "+ pull docker image for building PDF file"
32
+ task :setup do
33
+ sh "docker pull #{docker_image}"
34
+ end
35
+
36
+ docker_run = proc do |command, opt=nil|
37
+ sh "docker run #{docker_opts} #{opt} #{docker_image} #{command}"
38
+ end
39
+
40
+ desc "+ run 'rake pdf' on docker"
41
+ task :pdf do docker_run.call('rake pdf') end
42
+
43
+ desc "+ run 'rake pdf:nombre' on docker"
44
+ task :'pdf:nombre' do docker_run.call('rake pdf:nombre') end
45
+
46
+ desc "+ run 'rake epub' on docker"
47
+ task :epub do docker_run.call('rake epub') end
48
+
49
+ desc "+ run 'rake web' on docker"
50
+ task :web do docker_run.call('rake web') end
51
+
52
+ desc "+ run 'rake web:server' on docker"
53
+ task :'web:server' do
54
+ port = WEBSERVER_PORT
55
+ opt = "-it -p #{port}:#{port+1} -e WEBSERVER_PORT=#{port+1}"
56
+ docker_run.call('rake web:server', opt)
57
+ end
58
+
59
+ end
60
+
61
+
62
+ ##
63
+ ## PDFにノンブルを入れるためのRakeタスク。
64
+ ## ノンブルについては「ワンストップ!技術同人誌を書こう」第10章を参照のこと。
65
+ ## https://booth.pm/ja/items/708196
66
+ ##
67
+ namespace :pdf do
68
+
69
+ desc "+ add nombre (rake pdf:nombre [file=*.pdf] [out=*.pdf])"
70
+ task :nombre do
71
+ infile = ENV['file']; infile = nil if infile && infile.empty?
72
+ outfile = ENV['out'] ; outfile = nil if outfile && outfile.empty?
73
+ #
74
+ begin
75
+ require 'combine_pdf'
76
+ rescue LoadError
77
+ abort "ERROR: 'combine_pdf' gem not installed; please run `gem install combine_pdf` at first."
78
+ end
79
+ #
80
+ class CombinePDF::PDF
81
+ def add_nombre(options={})
82
+ start = options[:start] || 1
83
+ margin_h = options[:margin_h] || 5
84
+ margin_w = options[:margin_w] || 2
85
+ font = options[:font] || :Helvetica
86
+ size = options[:size] || 6
87
+ color = options[:color] || [0.5, 0.5, 0.5] # gray
88
+ binding = options[:binding] || :left
89
+ digit_w, digit_h = CombinePDF::Fonts.dimensions_of("9", font, size)
90
+ box_w = digit_w * 1.2
91
+ box_h = digit_h * 1.3
92
+ is_left = binding == :left
93
+ params = {
94
+ x: nil, y: nil, width: box_w, height: box_h,
95
+ font: font, font_size: size, font_color: color,
96
+ }
97
+ self.pages.each.with_index(start) do |page, page_num|
98
+ mediabox = page[:CropBox] || page[:MediaBox] or
99
+ abort "ERROR: failed to get page size of pdf"
100
+ page_w = mediabox[2]
101
+ page_h = mediabox[3]
102
+ top = page_h - margin_h
103
+ bottom = margin_h
104
+ left = margin_w
105
+ right = page_w - margin_w - box_w
106
+ params[:x] = is_left ? left : right
107
+ params[:y] = bottom
108
+ page_num.to_s.reverse.each_char do |c|
109
+ page.textbox(c, params)
110
+ params[:y] += box_h
111
+ end
112
+ is_left = ! is_left
113
+ end
114
+ self
115
+ end
116
+ end
117
+ #
118
+ if infile.nil?
119
+ require 'yaml'
120
+ config = File.open("config.yml", 'r', encoding: 'utf-8') {|f| YAML.load(f) }
121
+ bookname = config['bookname'] or abort("ERROR: missing 'bookname' in config.yml")
122
+ infile = bookname + ".pdf"
123
+ end
124
+ File.file?(infile) or abort("ERROR: #{infile}: pdf file not found.")
125
+ if outfile.nil?
126
+ #outfile = infile.sub(/\.pdf$/, '_nombre.pdf')
127
+ outfile = infile
128
+ end
129
+ #
130
+ #pdf = CombinePDF.parse(File.binread(infile))
131
+ #pdf.add_nombre()
132
+ #File.binwrite(outfile, pdf.to_pdf())
133
+ pdf = CombinePDF.load(infile)
134
+ pdf.add_nombre()
135
+ pdf.save(outfile)
136
+ end
137
+
138
+ end
139
+
140
+
141
+ ##
142
+ ## プレビュー用のサーバを起動する
143
+ ##
144
+ namespace :web do
145
+
146
+ invoke_web_task = proc do
147
+ #sh "rake web"
148
+ Process.fork { Rake::Task['web'].invoke() }
149
+ Process.wait()
150
+ end
151
+
152
+ helper_message_when_start_server = <<END
153
+ *
154
+ * Please open http://localhost:#{WEBSERVER_PORT}/ in browser.
155
+ *
156
+ END
157
+
158
+ helper_message_when_address_already_in_use = <<END
159
+ **
160
+ ** ERROR: Failed to start web server.
161
+ **
162
+ ** Other webserver process seems running. Kill it and restart webserver.
163
+ **
164
+ ** How to:
165
+ ** $ ps | head -1; ps | grep web[:]server
166
+ ** PID TTY TIME CMD
167
+ ** 12345 ttys002 0.00.35 /usr/bin/ruby /usr/bin/rake web:server
168
+ ** $ kill -KILL 12345
169
+ ** $ rake web:server
170
+ **
171
+ END
172
+
173
+ favicon_base64 = <<END
174
+ AAABAAEAEBACAAEAAQCwAAAAFgAAACgAAAAQAAAAIAAAAAEAAQAAAAAAgAAA
175
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAA
176
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/
177
+ /wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//
178
+ AAD//wAA//8AAP//AAD//wAA
179
+ END
180
+
181
+ desc "+ start web server for preview"
182
+ task :server do
183
+ rebuild_js = (ENV['STARTER_REBUILD_JS'] ||= '/rebuild.js')
184
+ invoke_web_task.call()
185
+ #
186
+ require 'yaml'
187
+ config = File.open('config.yml') {|f| YAML.load(f) }
188
+ contdir = config['contentdir']
189
+ #
190
+ require 'webrick'
191
+ root = 'webroot/'
192
+ host = '0.0.0.0' # don't use '127.0.0.1' to work on Docker
193
+ port = ENV['WEBSERVER_PORT'] ? ENV['WEBSERVER_PORT'].to_i : WEBSERVER_PORT
194
+ onstart = proc { puts helper_message_when_start_server }
195
+ srvconf = {DocumentRoot: root, BindAddress: host, Port: port, StartCallback: onstart}
196
+ begin
197
+ srv = WEBrick::HTTPServer.new(srvconf)
198
+ rescue Errno::EADDRINUSE => ex
199
+ at_exit { $stderr.puts helper_message_when_address_already_in_use }
200
+ raise
201
+ end
202
+ #
203
+ srv.mount_proc('/favicon.ico') do |req, res|
204
+ res.content_type = "image/x-icon"
205
+ res['Cache-Control'] = "public, max-age=#{60*60*24}"
206
+ res.body = favicon_base64.unpack("m")[0]
207
+ end
208
+ #
209
+ flag_quit = false
210
+ interval = 0.8 # seconds
211
+ srv.mount_proc(rebuild_js) do |req, res|
212
+ file = nil
213
+ if req.query_string =~ /\Afile=([^&]*)/
214
+ file = $1.strip
215
+ file = nil if file.empty?
216
+ end
217
+ #
218
+ errmsg = nil
219
+ if file.nil?
220
+ # pass
221
+ elsif file !~ /\A[-\w]+\.re\z/
222
+ errmsg = "invalid file"
223
+ else
224
+ file = File.join(contdir, file) if contdir
225
+ errmsg = "file not found." if ! File.exist?(file)
226
+ end
227
+ #
228
+ if errmsg
229
+ body = "alert('ERROR: #{errmsg}');"
230
+ else
231
+ body = "window.location.reload();"
232
+ ## sleep until '*.re' file modified
233
+ if file
234
+ mtime = File.mtime(file)
235
+ sleep interval while !flag_quit && mtime == File.mtime(file)
236
+ end
237
+ ## generate html file from '*.re'
238
+ invoke_web_task.call() unless flag_quit
239
+ end
240
+ res.content_type = "application/javascript"
241
+ msg = "Server seems down. Press OK to reload."
242
+ res.body = !flag_quit ? body : "alert('#{msg}');window.location.reload();"
243
+ end
244
+ #
245
+ trap(:INT) { flag_quit = true; srv.shutdown() }
246
+ srv.start()
247
+ end
248
+
249
+ end
250
+
251
+
252
+ ##
253
+ ## 高解像度用の画像から低解像度の画像を生成するRakeタスク。
254
+ ## 詳細は「ワンストップ!技術同人誌を書こう」第8章を参照のこと。
255
+ ## https://booth.pm/ja/items/708196
256
+ ##
257
+ desc "+ convert images (high resolution -> low resolution)"
258
+ task :images do
259
+ ## macOSならsipsコマンドを使い、それ以外ではImageMagickを使う
260
+ has_sips = File.exist?("/usr/bin/sips")
261
+ if ! has_sips
262
+ ok = system("convert --version >/dev/null")
263
+ if ! ok
264
+ abort "ERROR: 'convert' command not found; please install ImageMagick."
265
+ end
266
+ end
267
+ ## 高解像度の画像をもとに低解像度の画像を作成する
268
+ for src in Dir.glob("images_highres/**/*.{png,jpg,jpeg}")
269
+ ## 低解像度の画像を作成済みなら残りの処理をスキップ
270
+ dest = src.sub("images_highres/", "images_lowres/")
271
+ next if File.exist?(dest) && File.mtime(src) == File.mtime(dest)
272
+ ## 必要ならフォルダを作成
273
+ dir = File.dirname(dest)
274
+ mkdir_p dir if ! File.directory?(dir)
275
+ ## 高解像度の画像のDPIを変更(72dpi→360dpi)
276
+ if has_sips
277
+ sh "sips -s dpiHeight 360 -s dpiWidth 360 #{src}"
278
+ else
279
+ sh "convert -density 360 -units PixelsPerInch #{src} #{src}"
280
+ end
281
+ ## 低解像度の画像を作成(72dpi、横幅1/5)
282
+ if has_sips
283
+ `sips -g pixelWidth #{src}` =~ /pixelWidth: (\d+)/
284
+ option = "-s dpiHeight 72 -s dpiWidth 72 --resampleWidth #{$1.to_i / 5}"
285
+ sh "sips #{option} --out #{dest} #{src}"
286
+ else
287
+ sh "convert -density 72 -units PixelsPerInch -resize 20% #{src} #{dest}"
288
+ end
289
+ ## 低解像度の画像のタイムスタンプを、高解像度の画像と同じにする
290
+ ## (=画像のタイムスタンプが違ったら、画像が更新されたと見なす)
291
+ File.utime(File.atime(dest), File.mtime(src), dest)
292
+ end
293
+ ## 高解像度の画像が消されたら、低解像度の画像も消す
294
+ for dest in Dir.glob("images_lowres/**/*").sort().reverse()
295
+ src = dest.sub("images_lowres/", "images_highres/")
296
+ rm_r dest if ! File.exist?(src)
297
+ end
298
+ end
299
+
300
+
301
+ ##
302
+ ## 低解像度と高解像度を切り替えるRakeタスク。
303
+ ## 詳細は「ワンストップ!技術同人誌を書こう」第8章を参照のこと。
304
+ ## https://booth.pm/ja/items/708196
305
+ ##
306
+ namespace "images" do
307
+
308
+ desc "+ toggle image directories ('images_{lowres,highres}')"
309
+ task :toggle do
310
+ if ! File.symlink?("images")
311
+ msg = "ERROR: 'images' directory should be a symbolic link.\n"
312
+ msg << " rename it and create symbolic link, for example:\n"
313
+ msg << " $ mv images images_highres\n"
314
+ msg << " $ ln -s images_highres images\n"
315
+ abort msg
316
+ end
317
+ link = File.readlink("images")
318
+ rm "images"
319
+ if link == "images_lowres"
320
+ ln_s "images_highres", "images"
321
+ else
322
+ ln_s "images_lowres", "images"
323
+ end
324
+ end
325
+
326
+ end