review-retrovert 0.9.7 → 0.9.8

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +9 -0
  3. data/.editorconfig +3 -0
  4. data/.github/workflows/retrovert.yml +20 -5
  5. data/.gitignore +1 -0
  6. data/.vscode/launch.json +48 -0
  7. data/Gemfile.lock +13 -4
  8. data/Makefile +24 -0
  9. data/docker/review.Dockerfile +10 -0
  10. data/lib/review/retrovert/converter.rb +374 -111
  11. data/lib/review/retrovert/reviewcompat.rb +57 -0
  12. data/lib/review/retrovert/reviewdef.rb +70 -0
  13. data/lib/review/retrovert/sty/{ird.sty → ird.sty.erb} +9 -1
  14. data/lib/review/retrovert/sty/review-retrovert-custom.sty.erb +6 -0
  15. data/lib/review/retrovert/utils.rb +35 -0
  16. data/lib/review/retrovert/version.rb +1 -1
  17. data/lib/review/retrovert/yamlconfig.rb +0 -1
  18. data/package-lock.json +6 -0
  19. data/package.json +1 -0
  20. data/review-retrovert.gemspec +5 -1
  21. data/testdata/mybook/.gitignore +3 -1
  22. data/testdata/mybook/.textlintrc +11 -0
  23. data/testdata/mybook/Rakefile +8 -0
  24. data/testdata/mybook/catalog.yml +36 -10
  25. data/testdata/mybook/config-starter.yml +134 -9
  26. data/testdata/mybook/config.yml +44 -15
  27. data/testdata/mybook/contents/00-preface.re +48 -2
  28. data/testdata/mybook/contents/01-install.re +38 -5
  29. data/testdata/mybook/contents/02-tutorial.re +333 -113
  30. data/testdata/mybook/contents/03-syntax.re +2370 -373
  31. data/testdata/mybook/contents/04-customize.re +424 -88
  32. data/testdata/mybook/contents/05-faq.re +288 -10
  33. data/testdata/mybook/contents/06-bestpractice.re +431 -59
  34. data/testdata/mybook/contents/91-compare.re +402 -2
  35. data/testdata/mybook/contents/92-filelist.re +14 -8
  36. data/testdata/mybook/contents/93-background.re +10 -10
  37. data/testdata/mybook/contents/99-postface.re +2 -1
  38. data/testdata/mybook/contents/r0-root.re +1 -1
  39. data/testdata/mybook/css/webstyle.css +180 -2
  40. data/testdata/mybook/data/terms.txt +3 -0
  41. data/testdata/mybook/data/words.txt +15 -0
  42. data/testdata/mybook/images/03-syntax/index-page.png +0 -0
  43. data/testdata/mybook/images/03-syntax/order-detail.png +0 -0
  44. data/testdata/mybook/images/04-customize/section_decoration_samples.png +0 -0
  45. data/testdata/mybook/images/05-faq/dummy-image.png +0 -0
  46. data/testdata/mybook/images/06-bestpractice/section_title_wlines.png +0 -0
  47. data/testdata/mybook/images/avatar-b.png +0 -0
  48. data/testdata/mybook/images/avatar-g.png +0 -0
  49. data/testdata/mybook/images/avatar-r.png +0 -0
  50. data/testdata/mybook/images/caution-icon.png +0 -0
  51. data/testdata/mybook/images/info-icon.png +0 -0
  52. data/testdata/mybook/images/warning-icon.png +0 -0
  53. data/testdata/mybook/layouts/layout.html5.erb +3 -1
  54. data/testdata/mybook/layouts/layout.tex.erb +265 -379
  55. data/testdata/mybook/layouts/layout.tex.erb.orig +386 -0
  56. data/testdata/mybook/lib/ruby/review-book.rb +64 -0
  57. data/testdata/mybook/lib/ruby/review-builder.rb +902 -63
  58. data/testdata/mybook/lib/ruby/review-compiler.rb +675 -22
  59. data/testdata/mybook/lib/ruby/review-epubbuilder.rb +33 -0
  60. data/testdata/mybook/lib/ruby/review-epubmaker.rb +10 -7
  61. data/testdata/mybook/lib/ruby/review-htmlbuilder.rb +354 -66
  62. data/testdata/mybook/lib/ruby/review-latexbuilder.rb +429 -146
  63. data/testdata/mybook/lib/ruby/review-maker.rb +117 -6
  64. data/testdata/mybook/lib/ruby/review-markdownbuilder.rb +945 -0
  65. data/testdata/mybook/lib/ruby/review-markdownmaker.rb +91 -0
  66. data/testdata/mybook/lib/ruby/review-monkeypatch.rb +2 -0
  67. data/testdata/mybook/lib/ruby/review-pdfmaker.rb +160 -82
  68. data/testdata/mybook/lib/ruby/review-webmaker.rb +20 -5
  69. data/testdata/mybook/lib/tasks/review.rake +14 -0
  70. data/testdata/mybook/lib/tasks/starter.rake +148 -4
  71. data/testdata/mybook/sty/indexstyle.ist +25 -0
  72. data/testdata/mybook/sty/mytextsize.sty +29 -0
  73. data/testdata/mybook/sty/mytitlepage.sty +34 -11
  74. data/testdata/mybook/sty/review-base.sty +276 -0
  75. data/testdata/mybook/sty/starter-codeblock.sty +237 -106
  76. data/testdata/mybook/sty/starter-color.sty +72 -17
  77. data/testdata/mybook/sty/starter-heading.sty +60 -13
  78. data/testdata/mybook/sty/starter-misc.sty +894 -0
  79. data/testdata/mybook/sty/starter-note.sty +67 -14
  80. data/testdata/mybook/sty/starter-talklist.sty +105 -0
  81. data/testdata/mybook/sty/starter-util.sty +35 -0
  82. data/testdata/mybook/sty/starter.sty +8 -526
  83. metadata +80 -4
@@ -0,0 +1,91 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'pathname'
4
+
5
+ require_relative './review-cli'
6
+ require_relative './review-maker'
7
+ require_relative './review-markdownbuilder'
8
+
9
+
10
+ module ReVIEW
11
+
12
+
13
+ class MarkdownMaker < Maker
14
+
15
+ SCRIPT_NAME = "review-markdownmaker"
16
+
17
+ def generate()
18
+ @build_dir = make_build_dir()
19
+ begin
20
+ book = load_book() # also loads 'review-ext.rb'
21
+ converter = ReVIEW::Converter.new(book, new_builder())
22
+ errors = create_input_files(converter, book)
23
+ if errors && !errors.empty?
24
+ handle_compile_errors(errors)
25
+ end
26
+ return @build_dir
27
+ ensure
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def new_builder()
34
+ return ReVIEW::MarkdownBuilder.new
35
+ end
36
+
37
+ def make_build_dir()
38
+ dir = "#{@config['bookname']}-md"
39
+ if !File.directory?(dir)
40
+ Dir.mkdir(dir)
41
+ else
42
+ Dir.glob("#{dir}/*").each {|x| FileUtils.rm_rf(x) }
43
+ end
44
+ return dir
45
+ end
46
+
47
+ def create_input_files(converter, book)
48
+ errors = []
49
+ book.each_input_file do |part, chap|
50
+ if part
51
+ err = output_chaps(converter, part.name) if part.file?
52
+ elsif chap
53
+ filename = File.basename(chap.path, '.*')
54
+ err = output_chaps(converter, filename)
55
+ end
56
+ errors << err if err
57
+ end
58
+ return errors
59
+ end
60
+
61
+ def output_chaps(converter, filename)
62
+ contdir = @config['contentdir']
63
+ infile = "#{filename}.re"
64
+ infile = File.join(contdir, infile) if contdir.present?
65
+ outfile = "#{filename}.md"
66
+ $stderr.puts "compiling #{outfile}"
67
+ begin
68
+ converter.convert(infile, File.join(@build_dir, outfile))
69
+ nil
70
+ ## 文法エラーだけキャッチし、それ以外のエラーはキャッチしないよう変更
71
+ ## (LATEXBuilderで起こったエラーのスタックトレースを表示する)
72
+ rescue ApplicationError => ex
73
+ warn "compile error in #{outfile} (#{ex.class})"
74
+ warn colored_errmsg(ex.message)
75
+ ex.message
76
+ end
77
+ end
78
+
79
+ def handle_compile_errors(errors)
80
+ return if errors.nil? || errors.empty?
81
+ if @config['ignore-errors']
82
+ $stderr.puts 'compile error, but try to generate PDF file'
83
+ else
84
+ error 'compile error, No PDF file output.'
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+
91
+ end
@@ -4,12 +4,14 @@
4
4
  ## change ReVIEW source code
5
5
  ##
6
6
 
7
+ require_relative './review-book' ## Book::Baseクラスを修正
7
8
  require_relative './review-compiler' ## Compilerクラスを修正
8
9
  require_relative './review-builder' ## Builderクラスを修正
9
10
  require_relative './review-latexbuilder' ## LaTeX用の機能を修正
10
11
  require_relative './review-htmlbuilder' ## HTML用の機能を修正
11
12
  require_relative './review-textbuilder' ## テキスト用の機能を修正
12
13
  #require_relative './review-pdfmaker' ## PDF用の機能を修正 (for Rake task)
14
+ require_relative './review-epubbuilder' ## テキスト用の機能を修正
13
15
  require_relative './review-webmaker' ## Webページ用の機能を修正
14
16
  require_relative './review-tocparser' ## Web用目次作成機能を修正
15
17
 
@@ -38,15 +38,14 @@ module ReVIEW
38
38
  converter = ReVIEW::Converter.new(book, new_builder())
39
39
  errors = create_input_files(converter, book)
40
40
  if errors && !errors.empty?
41
- FileUtils.rm_f pdf_filepath() ###
41
+ #FileUtils.rm_f pdf_filepath() ###
42
42
  handle_compile_errors(errors)
43
43
  end
44
44
  #
45
45
  prepare_files()
46
- tmp_file = build_pdf(book)
47
- copy_outfile(tmp_file, pdf_filepath())
46
+ build_pdf(book)
48
47
  ensure
49
- remove_entry_secure(@build_dir) unless @config['debug']
48
+ FileUtils.remove_entry_secure(@build_dir) unless @config['debug']
50
49
  end
51
50
  end
52
51
 
@@ -56,8 +55,12 @@ module ReVIEW
56
55
  return ReVIEW::LATEXBuilder.new
57
56
  end
58
57
 
59
- def pdf_filepath
60
- return File.join(@basedir, "#{@config['bookname']}.pdf")
58
+ def pdf_filepath()
59
+ return File.join(@basedir, pdf_filename())
60
+ end
61
+
62
+ def pdf_filename()
63
+ return "#{@config['bookname']}.pdf"
61
64
  end
62
65
 
63
66
  def remove_old_file
@@ -69,9 +72,9 @@ module ReVIEW
69
72
  if !File.directory?(dir)
70
73
  Dir.mkdir(dir)
71
74
  else
72
- ## 目次と相互参照に関するファイルだけを残し、あとは削除
75
+ ## 目次と相互参照と索引に関するファイルだけを残し、あとは削除
73
76
  Pathname.new(dir).children.each do |x|
74
- next if x.basename.to_s =~ /\.(aux|toc|out|mtc\d*|maf)\z/
77
+ next if x.basename.to_s =~ /\.(aux|toc|out|idx|ind|mtc\d*|maf)\z/
75
78
  x.rmtree()
76
79
  end
77
80
  end
@@ -105,7 +108,7 @@ module ReVIEW
105
108
  ## (LATEXBuilderで起こったエラーのスタックトレースを表示する)
106
109
  rescue ApplicationError => ex
107
110
  warn "compile error in #{outfile} (#{ex.class})"
108
- warn ex.message
111
+ warn colored_errmsg(ex.message)
109
112
  ex.message
110
113
  end
111
114
  end
@@ -140,8 +143,9 @@ module ReVIEW
140
143
 
141
144
  def absolute_path(filename)
142
145
  return nil unless filename.present?
143
- filepath = File.absolute_path(filename, @basedir)
144
- return File.exist?(fielpath) ? filepath : nil
146
+ #filepath = File.absolute_path(filename, @basedir)
147
+ filepath = File.absolute_path(filename, '..')
148
+ return File.exist?(filepath) ? filepath : nil
145
149
  end
146
150
 
147
151
  def compile_latex_files(base, config)
@@ -161,36 +165,60 @@ module ReVIEW
161
165
  mkidx << " -s #{mkidx_sty}" if mkidx_sty
162
166
  mkidx << " -d #{mkidx_dic}" if mkidx_dic
163
167
  #
168
+ texfile = "#{base}.tex"
169
+ idxfile = "#{base}.idx"
170
+ dvifile = "#{base}.dvi"
171
+ pdffile = pdf_filename()
172
+ #
164
173
  call_hook('hook_beforetexcompile')
165
- changed = run_latex!(latex, "#{base}.tex")
166
- changed = run_latex!(latex, "#{base}.tex") if changed
167
- changed = run_latex!(latex, "#{base}.tex") if changed
174
+ #
175
+ print_latex_version(latex_cmd)
176
+ if (v = ENV['STARTER_COMPILETIMES']).present?
177
+ compile_ntimes = v.to_i
178
+ elsif ENV['STARTER_CHAPTER'].present?
179
+ compile_ntimes = 1
180
+ else
181
+ compile_ntimes = nil
182
+ end
183
+ run_latex(latex, texfile, compile_ntimes)
184
+ #
168
185
  if mkidx_p # 索引を作る場合
169
- call_hook('hook_beforemakeindex')
170
- run_cmd!("#{mkidx} #{base}") if File.exist?("#{base}.idx")
171
- call_hook('hook_aftermakeindex')
172
- run_latex!(latex, "#{base}.tex")
186
+ changed = _files_changed?('*.ind') do
187
+ call_hook('hook_beforemakeindex')
188
+ usr_bin_time { run_cmd!("#{mkidx} #{base}") } if File.exist?(idxfile)
189
+ call_hook('hook_aftermakeindex')
190
+ end
191
+ if changed
192
+ run_latex(latex, texfile)
193
+ else
194
+ puts "[pdfmaker]### (info): index file not changed. skip latex compilation."
195
+ end
173
196
  end
174
197
  call_hook('hook_aftertexcompile')
175
- return unless File.exist?("#{base}.dvi")
176
- run_cmd!("#{dvipdf} #{base}.dvi")
198
+ return unless File.exist?(dvifile)
199
+ usr_bin_time { run_cmd!("#{dvipdf} -o ../#{pdffile} #{dvifile}") }
177
200
  call_hook('hook_afterdvipdf')
178
201
  end
179
202
 
180
203
  ## コンパイルメッセージを減らすために、uplatexコマンドをバッチモードで起動する。
181
204
  ## エラーがあったら、バッチモードにせずに再コンパイルしてエラーメッセージを出す。
182
- def run_latex!(latex, file)
183
- ## *.auxファイルと*.tocファイルのハッシュ値を計算する。
184
- ## (「//list[?]」が生成するラベルはランダムなのでそれを削除してハッシュ値を計算する。)
185
- delete_rexp = /^\\newlabel\{_\d+\}/
186
- auxfile = file.sub(/\.tex\z/, '.aux')
187
- tocfile = file.sub(/\.tex\z/, '.toc')
188
- auxhash_old = _filehash(auxfile, delete_rexp)
189
- tochash_old = _filehash(tocfile)
190
- ## invoke latex command with batchmode option in order to suppress
191
- ## compilation message (in other words, to be quiet mode).
192
- ok = run_cmd("#{latex} -interaction=batchmode #{file}")
193
- if ! ok
205
+ def run_latex(latex, file, max_ntimes=nil)
206
+ max_ntimes ||= 3
207
+ ok = true
208
+ cmd = "#{latex} -interaction=batchmode #{file}"
209
+ hash1 = _hash_auxfiles()
210
+ max_ntimes.times do
211
+ ## invoke latex command with batchmode option in order to suppress
212
+ ## compilation message (in other words, to be quiet mode).
213
+ #ok = run_cmd("#{latex} -interaction=batchmode #{file}")
214
+ _output, ok = usr_bin_time { run_cmd_capturing_output(cmd, " > /dev/null") }
215
+ break unless ok
216
+ hash2 = _hash_auxfiles()
217
+ auxfiles_changed = hash1 != hash2
218
+ break unless auxfiles_changed
219
+ hash1 = hash2
220
+ end
221
+ unless ok
194
222
  ## latex command with batchmode option doesn't show any error,
195
223
  ## therefore we must invoke latex command again without batchmode option
196
224
  ## in order to show error.
@@ -199,48 +227,37 @@ module ReVIEW
199
227
  $stderr.puts "*"
200
228
  run_cmd!("#{latex} #{file}")
201
229
  end
202
- ## *.tocファイルのハッシュ値を計算し、コンパイル前と変わっていたらtrueを返す。
203
- ## (つまりもう一度コンパイルが必要ならtrueを返す。)
204
- auxhash_new = _filehash(auxfile, delete_rexp)
205
- tochash_new = _filehash(tocfile)
206
- return auxhash_old.nil? || tochash_old.nil? \
207
- || auxhash_old != auxhash_new || tochash_old != tochash_new
208
- ## LaTeXのログファイルに「ラベルが変更された」と出ていたらtrueを返す。
209
- ## (つまりもう一度コンパイルが必要ならtrueを返す。)
210
- ## → この方法は、ページ番号が変わったことは検出できても、
211
- ## 章や節のタイトルが変わったことを検出できない。
212
- #logfile = file.sub(/\.tex\z/, '.log')
213
- #begin
214
- # lines = File.open(logfile, 'r') {|f|
215
- # f.grep(/^LaTeX Warning: Label\(s\) may have changed./)
216
- # }
217
- # return !lines.empty?
218
- #rescue IOError
219
- # return true
220
- #end
221
230
  end
222
231
 
223
- def _filehash(filepath, delete_rexp=nil)
224
- return nil unless File.exist?(filepath)
225
- binary = File.open(filepath, 'rb') {|f| f.read() }
226
- binary = binary.gsub(delete_rexp, '') if delete_rexp
227
- return Digest::MD5.hexdigest(binary)
232
+ private
233
+
234
+ def _hash_auxfiles()
235
+ return _hash_files(Dir.glob('*.{aux,toc,idx}'))
228
236
  end
229
- private :_filehash
230
-
231
- ## 開発用。LaTeXコンパイル回数を環境変数で指定する。
232
- if ENV['STARTER_COMPILETIMES']
233
- alias __run_latex! run_latex!
234
- def run_latex!(latex, file)
235
- n = ENV['STARTER_COMPILETIMES']
236
- @_done ||= {}
237
- k = "#{latex} #{file}"
238
- return if (@_done[k] ||= 0) >= n.to_i
239
- @_done[k] += 1
240
- __run_latex!(latex, file)
237
+
238
+ def _files_changed?(filepat)
239
+ before = _hash_files(Dir.glob(filepat))
240
+ yield
241
+ after = _hash_files(Dir.glob(filepat))
242
+ return before != after
243
+ end
244
+
245
+ def _hash_files(filenames)
246
+ return filenames.each_with_object({}) do |filename, dict|
247
+ s = File.read(filename)
248
+ dict[filename] = Digest::SHA1.hexdigest(s)
241
249
  end
242
250
  end
243
251
 
252
+ protected
253
+
254
+ def print_latex_version(latex_cmd)
255
+ output, ok = run_cmd_capturing_output("#{latex_cmd} --version", " | head -1")
256
+ ok or
257
+ raise BuildError.new("latex command seemds not exist.")
258
+ puts output.each_line.first
259
+ end
260
+
244
261
  def prepare_files()
245
262
  ## copy image files
246
263
  copy_images(@config['imagedir'], File.join(@build_dir, @config['imagedir']))
@@ -252,14 +269,6 @@ module ReVIEW
252
269
  copy_sty(Dir.pwd, @build_dir, 'tex')
253
270
  end
254
271
 
255
- def copy_outfile(tmp_pdffile, out_pdffile)
256
- if File.exist?(tmp_pdffile)
257
- FileUtils.cp(tmp_pdffile, out_pdffile)
258
- elsif File.exist?(out_pdffile)
259
- FileUtils.rm(out_pdffile)
260
- end
261
- end
262
-
263
272
  def copy_images(from, to)
264
273
  return unless File.exist?(from)
265
274
  FileUtils.mkdir_p(to)
@@ -305,6 +314,33 @@ module ReVIEW
305
314
  class LATEXRenderer < BaseRenderer
306
315
  include ReVIEW::LaTeXUtils
307
316
 
317
+ ## LaTeXUtils#escape() だと、'-' を '{-}' に変換する。
318
+ ## そのため、たとえば「x-small」が「x{-}small」になってしまう。
319
+ ## これは設定値にとって都合が悪いので、書き直す。
320
+ def escape(v)
321
+ case v
322
+ when nil ; return ''
323
+ #when true ; return 'Y'
324
+ #when false ; return ''
325
+ when Numeric ; return v.to_s
326
+ else
327
+ v.to_s.gsub(/[#$%&{}_^~\\]/, LATEX_ESCAPE_CHARS)
328
+ end
329
+ end
330
+
331
+ LATEX_ESCAPE_CHARS = {
332
+ '%' => '\%',
333
+ '#' => '\#',
334
+ '$' => '\$',
335
+ '&' => '\&',
336
+ '_' => '\_',
337
+ '{' => '\{',
338
+ '}' => '\}',
339
+ '\\' => '{\textbackslash}',
340
+ '^' => '{\textasciicircum}',
341
+ '~' => '{\textasciitilde}',
342
+ }
343
+
308
344
  def layout_template_name
309
345
  return './latex/layout.tex.erb'
310
346
  end
@@ -323,7 +359,12 @@ module ReVIEW
323
359
 
324
360
  @okuduke = make_colophon()
325
361
  @authors = make_authors()
326
-
362
+ #
363
+ sep = I18n.t('names_splitter')
364
+ @book_author = @config.names_of('aut').join(sep)
365
+ @book_supervisor = @config.names_of('csl').join(sep)
366
+ @book_translator = @config.names_of('trl').join(sep)
367
+ #
327
368
  @custom_titlepage = make_custom_page(@config['cover']) || make_custom_page(@config['coverfile'])
328
369
  @custom_originaltitlepage = make_custom_page(@config['originaltitlefile'])
329
370
  @custom_creditpage = make_custom_page(@config['creditfile'])
@@ -336,7 +377,7 @@ module ReVIEW
336
377
  if @config['pubhistory']
337
378
  warn 'pubhistory is oboleted. use history.'
338
379
  else
339
- @config['pubhistory'] = make_history_list.join("\n")
380
+ @config['pubhistory'] = make_history_list().join("\n")
340
381
  end
341
382
 
342
383
  @coverimageoption =
@@ -400,17 +441,54 @@ module ReVIEW
400
441
  end
401
442
 
402
443
  def make_colophon_role(role)
403
- return "" unless @config[role].present?
444
+ return nil unless @config[role].present?
404
445
  initialize_metachars(@config['texcommand'])
405
446
  names = @config.names_of(role)
406
447
  sep = i18n('names_splitter')
407
448
  str = [names].flatten.join(sep)
408
- #return "#{i18n(role)} & #{escape_latex(str)} \\\\\n"
409
- return "\\startercolophonrow{#{i18n(role)}}{#{escape_latex(str)}}\n"
449
+ return i18n(role), escape(str)
410
450
  end
411
451
 
412
452
  def make_colophon()
413
- return @config['colophon_order'].map {|role| make_colophon_role(role) }.join
453
+ kv_list = []
454
+ @config['colophon_order'].each do |role|
455
+ if role == 'prt' # 印刷所の前に追加情報を入れる
456
+ _each_additional_kvs() {|k, v| kv_list << [k, v] }
457
+ end
458
+ kv = make_colophon_role(role)
459
+ kv_list << kv if kv
460
+ end
461
+ #
462
+ return kv_list.map {|k, v|
463
+ #"#{i18n(role)} & #{escape_latex(str)} \\\\\n"
464
+ "\\startercolophonrow{#{k}}{#{v}}\n"
465
+ }.join()
466
+ end
467
+
468
+ def _each_additional_kvs(&b)
469
+ dicts = @config['additional']
470
+ dicts.each do |d|
471
+ key = d['key']; val = d['value']
472
+ next unless key.present? && val.present?
473
+ k = escape(key.to_s)
474
+ [val].flatten.compact.each do |v|
475
+ case v
476
+ when /\A\@[-\w]+\z/
477
+ url = "https://twitter.com/#{escape(v[1..-1])}"
478
+ s = "{#{escape(v)}} (\\starterurl{#{url}}{#{url}})"
479
+ #s = "\textsf{#{escape(v)}} (\\starterurl{#{url}}{#{url}})"
480
+ #s = "\\starterurl{#{url}}{#{url}}"
481
+ yield k, s
482
+ when /\Ahttps?:\/\//
483
+ url = escape(v)
484
+ s = "\\starterurl{#{url}}{#{url}}"
485
+ yield k, s
486
+ else
487
+ yield k, escape(v.to_s)
488
+ end
489
+ k = nil
490
+ end
491
+ end if dicts
414
492
  end
415
493
 
416
494
  def make_authors