review 2.1.0 → 2.2.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +293 -6
  4. data/.rubocop_todo.yml +3 -608
  5. data/.travis.yml +6 -13
  6. data/README.md +5 -3
  7. data/Rakefile +6 -6
  8. data/bin/review-catalog-converter +2 -2
  9. data/bin/review-check +1 -1
  10. data/bin/review-compile +1 -2
  11. data/bin/review-init +6 -3
  12. data/bin/review-validate +3 -3
  13. data/bin/review-vol +2 -1
  14. data/doc/NEWS.ja.md +138 -25
  15. data/doc/NEWS.md +137 -25
  16. data/doc/config.yml.sample +2 -2
  17. data/doc/config.yml.sample-simple +1 -1
  18. data/doc/format.ja.md +86 -5
  19. data/doc/format.md +67 -2
  20. data/doc/makeindex.ja.md +95 -0
  21. data/doc/makeindex.md +97 -0
  22. data/doc/sample.css +214 -0
  23. data/lib/epubmaker.rb +6 -6
  24. data/lib/epubmaker/epubcommon.rb +19 -47
  25. data/lib/epubmaker/epubv2.rb +3 -1
  26. data/lib/epubmaker/epubv3.rb +4 -26
  27. data/lib/epubmaker/producer.rb +46 -46
  28. data/lib/epubmaker/zip_exporter.rb +86 -0
  29. data/lib/review/book/base.rb +13 -15
  30. data/lib/review/book/chapter.rb +2 -1
  31. data/lib/review/book/compilable.rb +9 -9
  32. data/lib/review/book/image_finder.rb +13 -13
  33. data/lib/review/book/index.rb +2 -2
  34. data/lib/review/book/volume.rb +2 -2
  35. data/lib/review/builder.rb +57 -1
  36. data/lib/review/catalog.rb +2 -2
  37. data/lib/review/compiler.rb +15 -7
  38. data/lib/review/configure.rb +11 -0
  39. data/lib/review/epubmaker.rb +403 -401
  40. data/lib/review/ewbbuilder.rb +16 -16
  41. data/lib/review/htmlbuilder.rb +42 -58
  42. data/lib/review/htmltoc.rb +1 -1
  43. data/lib/review/htmlutils.rb +50 -4
  44. data/lib/review/i18n.rb +2 -2
  45. data/lib/review/idgxmlbuilder.rb +30 -47
  46. data/lib/review/latexbuilder.rb +86 -41
  47. data/lib/review/latexutils.rb +19 -19
  48. data/lib/review/markdownbuilder.rb +16 -4
  49. data/lib/review/md2inaobuilder.rb +0 -9
  50. data/lib/review/pdfmaker.rb +91 -48
  51. data/lib/review/preprocessor.rb +1 -1
  52. data/lib/review/rstbuilder.rb +763 -0
  53. data/lib/review/sec_counter.rb +7 -9
  54. data/lib/review/tocparser.rb +3 -3
  55. data/lib/review/tocprinter.rb +5 -5
  56. data/lib/review/topbuilder.rb +48 -56
  57. data/lib/review/version.rb +1 -1
  58. data/lib/review/webmaker.rb +6 -7
  59. data/review.gemspec +1 -0
  60. data/templates/latex/layout.tex.erb +27 -2
  61. data/test/assets/test_template.tex +10 -1
  62. data/test/book_test_helper.rb +1 -2
  63. data/test/run_test.rb +10 -0
  64. data/test/sample-book/src/style.css +215 -0
  65. data/test/sample-book/src/vendor/jumoline/lppl.txt +416 -0
  66. data/test/test_book.rb +0 -1
  67. data/test/test_catalog.rb +1 -0
  68. data/test/test_converter.rb +1 -1
  69. data/test/test_epub3maker.rb +44 -51
  70. data/test/test_epubmaker.rb +82 -38
  71. data/test/test_epubmaker_cmd.rb +1 -1
  72. data/test/test_extentions_hash.rb +8 -1
  73. data/test/test_htmlbuilder.rb +411 -18
  74. data/test/test_i18n.rb +17 -0
  75. data/test/test_idgxmlbuilder.rb +88 -3
  76. data/test/test_image_finder.rb +18 -0
  77. data/test/test_index.rb +2 -0
  78. data/test/test_latexbuilder.rb +96 -8
  79. data/test/test_makerhelper.rb +2 -2
  80. data/test/test_markdownbuilder.rb +22 -1
  81. data/test/test_md2inaobuilder.rb +0 -5
  82. data/test/test_pdfmaker.rb +54 -36
  83. data/test/test_pdfmaker_cmd.rb +1 -1
  84. data/test/test_rstbuilder.rb +356 -0
  85. data/test/test_textutils.rb +14 -4
  86. data/test/test_topbuilder.rb +23 -4
  87. data/test/test_zip_exporter.rb +113 -0
  88. metadata +28 -2
data/doc/sample.css CHANGED
@@ -107,6 +107,220 @@ p.flushright {
107
107
  text-align: right;
108
108
  }
109
109
 
110
+ /**
111
+ * from Rouge
112
+ */
113
+ .highlight table td { padding: 5px; }
114
+ .highlight table pre { margin: 0; }
115
+ .highlight .cm {
116
+ color: #999988;
117
+ font-style: italic;
118
+ }
119
+ .highlight .cp {
120
+ color: #999999;
121
+ font-weight: bold;
122
+ }
123
+ .highlight .c1 {
124
+ color: #999988;
125
+ font-style: italic;
126
+ }
127
+ .highlight .cs {
128
+ color: #999999;
129
+ font-weight: bold;
130
+ font-style: italic;
131
+ }
132
+ .highlight .c, .highlight .cd {
133
+ color: #999988;
134
+ font-style: italic;
135
+ }
136
+ .highlight .err {
137
+ color: #a61717;
138
+ background-color: #e3d2d2;
139
+ }
140
+ .highlight .gd {
141
+ color: #000000;
142
+ background-color: #ffdddd;
143
+ }
144
+ .highlight .ge {
145
+ color: #000000;
146
+ font-style: italic;
147
+ }
148
+ .highlight .gr {
149
+ color: #aa0000;
150
+ }
151
+ .highlight .gh {
152
+ color: #999999;
153
+ }
154
+ .highlight .gi {
155
+ color: #000000;
156
+ background-color: #ddffdd;
157
+ }
158
+ .highlight .go {
159
+ color: #888888;
160
+ }
161
+ .highlight .gp {
162
+ color: #555555;
163
+ }
164
+ .highlight .gs {
165
+ font-weight: bold;
166
+ }
167
+ .highlight .gu {
168
+ color: #aaaaaa;
169
+ }
170
+ .highlight .gt {
171
+ color: #aa0000;
172
+ }
173
+ .highlight .kc {
174
+ color: #000000;
175
+ font-weight: bold;
176
+ }
177
+ .highlight .kd {
178
+ color: #000000;
179
+ font-weight: bold;
180
+ }
181
+ .highlight .kn {
182
+ color: #000000;
183
+ font-weight: bold;
184
+ }
185
+ .highlight .kp {
186
+ color: #000000;
187
+ font-weight: bold;
188
+ }
189
+ .highlight .kr {
190
+ color: #000000;
191
+ font-weight: bold;
192
+ }
193
+ .highlight .kt {
194
+ color: #445588;
195
+ font-weight: bold;
196
+ }
197
+ .highlight .k, .highlight .kv {
198
+ color: #000000;
199
+ font-weight: bold;
200
+ }
201
+ .highlight .mf {
202
+ color: #009999;
203
+ }
204
+ .highlight .mh {
205
+ color: #009999;
206
+ }
207
+ .highlight .il {
208
+ color: #009999;
209
+ }
210
+ .highlight .mi {
211
+ color: #009999;
212
+ }
213
+ .highlight .mo {
214
+ color: #009999;
215
+ }
216
+ .highlight .m, .highlight .mb, .highlight .mx {
217
+ color: #009999;
218
+ }
219
+ .highlight .sb {
220
+ color: #d14;
221
+ }
222
+ .highlight .sc {
223
+ color: #d14;
224
+ }
225
+ .highlight .sd {
226
+ color: #d14;
227
+ }
228
+ .highlight .s2 {
229
+ color: #d14;
230
+ }
231
+ .highlight .se {
232
+ color: #d14;
233
+ }
234
+ .highlight .sh {
235
+ color: #d14;
236
+ }
237
+ .highlight .si {
238
+ color: #d14;
239
+ }
240
+ .highlight .sx {
241
+ color: #d14;
242
+ }
243
+ .highlight .sr {
244
+ color: #009926;
245
+ }
246
+ .highlight .s1 {
247
+ color: #d14;
248
+ }
249
+ .highlight .ss {
250
+ color: #990073;
251
+ }
252
+ .highlight .s {
253
+ color: #d14;
254
+ }
255
+ .highlight .na {
256
+ color: #008080;
257
+ }
258
+ .highlight .bp {
259
+ color: #999999;
260
+ }
261
+ .highlight .nb {
262
+ color: #0086B3;
263
+ }
264
+ .highlight .nc {
265
+ color: #445588;
266
+ font-weight: bold;
267
+ }
268
+ .highlight .no {
269
+ color: #008080;
270
+ }
271
+ .highlight .nd {
272
+ color: #3c5d5d;
273
+ font-weight: bold;
274
+ }
275
+ .highlight .ni {
276
+ color: #800080;
277
+ }
278
+ .highlight .ne {
279
+ color: #990000;
280
+ font-weight: bold;
281
+ }
282
+ .highlight .nf {
283
+ color: #990000;
284
+ font-weight: bold;
285
+ }
286
+ .highlight .nl {
287
+ color: #990000;
288
+ font-weight: bold;
289
+ }
290
+ .highlight .nn {
291
+ color: #555555;
292
+ }
293
+ .highlight .nt {
294
+ color: #000080;
295
+ }
296
+ .highlight .vc {
297
+ color: #008080;
298
+ }
299
+ .highlight .vg {
300
+ color: #008080;
301
+ }
302
+ .highlight .vi {
303
+ color: #008080;
304
+ }
305
+ .highlight .nv {
306
+ color: #008080;
307
+ }
308
+ .highlight .ow {
309
+ color: #000000;
310
+ font-weight: bold;
311
+ }
312
+ .highlight .o {
313
+ color: #000000;
314
+ font-weight: bold;
315
+ }
316
+ .highlight .w {
317
+ color: #bbbbbb;
318
+ }
319
+ .highlight {
320
+ background-color: #f8f8f8;
321
+ }
322
+ .rouge-table { border-spacing: 0 }
323
+ .rouge-gutter { text-align: right }
110
324
 
111
325
  /**
112
326
  * from EBPAJ EPUB 3 File Creation Guide sample style
data/lib/epubmaker.rb CHANGED
@@ -10,13 +10,13 @@
10
10
  #
11
11
  # == Quick usage
12
12
  # require 'epubmaker'
13
- # epub = EPUBMaker::Producer.new
14
- # params = epub.load("config.yml")
15
- # epub.contents.push(EPUBMaker::Content.new({"file" => "ch01.xhtml"}))
16
- # epub.contents.push(EPUBMaker::Content.new({"file" => "ch02.xhtml"}))
13
+ # producer = EPUBMaker::Producer.new
14
+ # params = producer.load("config.yml")
15
+ # producer.contents.push(EPUBMaker::Content.new({"file" => "ch01.xhtml"}))
16
+ # producer.contents.push(EPUBMaker::Content.new({"file" => "ch02.xhtml"}))
17
17
  # ...
18
- # epub.import_imageinfo("images")
19
- # epub.produce
18
+ # producer.import_imageinfo("images")
19
+ # producer.produce
20
20
 
21
21
  require 'epubmaker/producer'
22
22
  require 'epubmaker/content'
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # = epubcommon.rb -- super class for EPUBv2 and EPUBv3
3
3
  #
4
- # Copyright (c) 2010-2016 Kenshi Muto and Masayoshi Takahashi
4
+ # Copyright (c) 2010-2017 Kenshi Muto and Masayoshi Takahashi
5
5
  #
6
6
  # This program is free software.
7
7
  # You can distribute or modify this program under the terms of
@@ -12,12 +12,6 @@
12
12
  require 'review/i18n'
13
13
  require 'review/template'
14
14
  require 'cgi'
15
- require 'shellwords'
16
- begin
17
- require 'zip'
18
- rescue LoadError
19
- ## I cannot find rubyzip library, so I use external zip command.
20
- end
21
15
 
22
16
  module EPUBMaker
23
17
 
@@ -144,6 +138,11 @@ EOT
144
138
  @body = <<-EOT
145
139
  <h1 class="cover-title">#{CGI.escapeHTML(@producer.params.name_of("title"))}</h1>
146
140
  EOT
141
+ if @producer.params["subtitle"]
142
+ @body << <<-EOT
143
+ <h2 class="cover-subtitle">#{CGI.escapeHTML(@producer.params.name_of("subtitle"))}</h2>
144
+ EOT
145
+ end
147
146
  end
148
147
 
149
148
  @title = CGI.escapeHTML(@producer.params.name_of("title"))
@@ -159,12 +158,21 @@ EOT
159
158
  end
160
159
 
161
160
  # Return title (copying) content.
161
+ # NOTE: this method is not used yet.
162
+ # see lib/review/epubmaker.rb#build_titlepage
162
163
  def titlepage
163
164
  @title = CGI.escapeHTML(@producer.params.name_of("title"))
164
165
 
165
166
  @body = <<EOT
166
167
  <h1 class="tp-title">#{@title}</h1>
167
168
  EOT
169
+
170
+ if @producer.params["subtitle"]
171
+ @body << <<EOT
172
+ <h2 class="tp-subtitle">#{CGI.escapeHTML(@producer.params.name_of("subtitle"))}</h2>
173
+ EOT
174
+ end
175
+
168
176
  if @producer.params["aut"]
169
177
  @body << <<EOT
170
178
  <p>
@@ -259,11 +267,14 @@ EOT
259
267
  revstr = ReVIEW::I18n.t("nth_impression", "#{rev+1}")
260
268
  if item =~ /\A\d+\-\d+\-\d+\Z/
261
269
  buf << %Q[ <p>#{ReVIEW::I18n.t("published_by1", [date_to_s(item), editstr+revstr])}</p>\n]
262
- else
270
+ elsif item =~ /\A(\d+\-\d+\-\d+)[\s ](.+)/
263
271
  # custom date with string
264
272
  item.match(/\A(\d+\-\d+\-\d+)[\s ](.+)/) do |m|
265
273
  buf << %Q[ <p>#{ReVIEW::I18n.t("published_by3", [date_to_s(m[1]), m[2]])}</p>\n]
266
274
  end
275
+ else
276
+ # free format
277
+ buf << %Q[ <p>#{item}</p>\n]
267
278
  end
268
279
  end
269
280
  end
@@ -405,45 +416,6 @@ EOT
405
416
  end
406
417
  end
407
418
 
408
- def export_zip(tmpdir, epubfile)
409
- if defined?(Zip)
410
- export_zip_rubyzip(tmpdir, epubfile)
411
- else
412
- export_zip_extcmd(tmpdir, epubfile)
413
- end
414
- end
415
-
416
- def export_zip_extcmd(tmpdir, epubfile)
417
- Dir.chdir(tmpdir) {|d| `#{@producer.params["epubmaker"]["zip_stage1"]} #{epubfile.shellescape} mimetype` }
418
- Dir.chdir(tmpdir) {|d| `#{@producer.params["epubmaker"]["zip_stage2"]} #{epubfile.shellescape} META-INF OEBPS #{@producer.params["epubmaker"]["zip_addpath"]}` }
419
- end
420
-
421
- def export_zip_rubyzip(tmpdir, epubfile)
422
- Dir.chdir(tmpdir) do |d|
423
- Zip::OutputStream.open(epubfile) do |epub|
424
- root_pathname = Pathname.new(tmpdir)
425
- # relpath = Pathname.new(File.join(tmpdir,'mimetype')).relative_path_from(root_pathname)
426
- epub.put_next_entry('mimetype', nil, nil, Zip::Entry::STORED)
427
- epub << "application/epub+zip"
428
-
429
- export_zip_rubyzip_addpath(epub, File.join(tmpdir,'META-INF'), root_pathname)
430
- export_zip_rubyzip_addpath(epub, File.join(tmpdir,'OEBPS'), root_pathname)
431
- if @producer.params["zip_addpath"].present?
432
- export_zip_rubyzip_addpath(epub, File.join(tmpdir,@producer.params["zip_addpath"]), root_pathname)
433
- end
434
- end
435
- end
436
- end
437
-
438
- def export_zip_rubyzip_addpath(epub, dirname, rootdir)
439
- Dir[File.join(dirname,'**','**')].each do |path|
440
- next if File.directory?(path)
441
- relpath = Pathname.new(path).relative_path_from(rootdir)
442
- epub.put_next_entry(relpath)
443
- epub << File.binread(path)
444
- end
445
- end
446
-
447
419
  def legacy_cover_and_title_file(loadfile, writefile)
448
420
  @title = @producer.params["booktitle"]
449
421
  s = ""
@@ -11,6 +11,7 @@
11
11
 
12
12
  require 'epubmaker/epubcommon'
13
13
  require 'cgi'
14
+ require 'epubmaker/zip_exporter'
14
15
 
15
16
  module EPUBMaker
16
17
 
@@ -132,7 +133,8 @@ EOT
132
133
  File.open("#{tmpdir}/OEBPS/#{@producer.params["bookname"]}-toc.#{@producer.params["htmlext"]}", "w") {|f| @producer.mytoc(f) } unless @producer.params["mytoc"].nil?
133
134
 
134
135
  @producer.call_hook(@producer.params["epubmaker"]["hook_prepack"], tmpdir)
135
- export_zip(tmpdir, epubfile)
136
+ expoter = EPUBMaker::ZipExporter.new(tmpdir, @producer.params)
137
+ expoter.export_zip(epubfile)
136
138
  end
137
139
 
138
140
  end
@@ -10,6 +10,7 @@
10
10
  #
11
11
 
12
12
  require 'epubmaker/epubcommon'
13
+ require 'epubmaker/zip_exporter'
13
14
 
14
15
  module EPUBMaker
15
16
 
@@ -18,7 +19,7 @@ module EPUBMaker
18
19
  # Construct object with parameter hash +params+ and message resource hash +res+.
19
20
  def initialize(producer)
20
21
  super
21
- @opf_prefix = Hash.new
22
+ @opf_prefix = {}
22
23
  if @producer.params["opf_prefix"].present?
23
24
  @producer.params["opf_prefix"].each do |k, v|
24
25
  @opf_prefix[k] = v
@@ -225,31 +226,8 @@ EOT
225
226
  File.open("#{tmpdir}/OEBPS/#{@producer.params["bookname"]}-toc.#{@producer.params["htmlext"]}", "w") {|f| @producer.ncx(f, @producer.params["epubmaker"]["ncxindent"]) }
226
227
 
227
228
  @producer.call_hook(@producer.params["epubmaker"]["hook_prepack"], tmpdir)
228
- export_zip(tmpdir, epubfile)
229
+ expoter = EPUBMaker::ZipExporter.new(tmpdir, @producer.params)
230
+ expoter.export_zip(epubfile)
229
231
  end
230
-
231
- private
232
-
233
- # Return cover pointer for opf file
234
- def cover_in_opf
235
- s = ""
236
-
237
- if @producer.params["coverimage"]
238
- @producer.contents.each do |item|
239
- if item.media.start_with?('image') && item.file =~ /#{@producer.params["coverimage"]}\Z/
240
- s << <<EOT
241
- <item id="#{item.id}" href="#{item.file}" media-type="#{item.media}"/>
242
- EOT
243
- break
244
- end
245
- end
246
- end
247
-
248
- s << <<EOT
249
- <item id="#{@producer.params["bookname"]}" href="#{@producer.params["cover"]}" media-type="application/xhtml+xml"/>
250
- EOT
251
- s
252
- end
253
-
254
232
  end
255
233
  end
@@ -170,16 +170,16 @@ module EPUBMaker
170
170
  current = Dir.pwd
171
171
  basedir = current if basedir.nil?
172
172
 
173
- _tmpdir = tmpdir.nil? ? Dir.mktmpdir : tmpdir
173
+ new_tmpdir = tmpdir.nil? ? Dir.mktmpdir : tmpdir
174
174
  epubfile = "#{current}/#{epubfile}" if epubfile !~ /\A\// # /
175
175
 
176
176
  # FIXME: error check
177
177
  File.unlink(epubfile) if File.exist?(epubfile)
178
178
 
179
179
  begin
180
- @epub.produce(epubfile, basedir, _tmpdir)
180
+ @epub.produce(epubfile, basedir, new_tmpdir)
181
181
  ensure
182
- FileUtils.rm_r(_tmpdir) if tmpdir.nil?
182
+ FileUtils.rm_r(new_tmpdir) if tmpdir.nil?
183
183
  end
184
184
  end
185
185
 
@@ -211,49 +211,49 @@ module EPUBMaker
211
211
  def complement
212
212
  @params["htmlext"] = "html" if @params["htmlext"].nil?
213
213
  defaults = ReVIEW::Configure.new.merge({
214
- "language" => "ja",
215
- "date" => Time.now.strftime("%Y-%m-%d"),
216
- "modified" => Time.now.strftime("%Y-%02m-%02dT%02H:%02M:%02SZ"),
217
- "isbn" => nil,
218
- "toclevel" => 2,
219
- "stylesheet" => [],
220
- "epubversion" => 3,
221
- "htmlversion" => 5,
222
- "secnolevel" => 2,
223
- "pre_secnolevel" => 0,
224
- "post_secnolevel" => 1,
225
- "part_secnolevel" => 1,
226
- "titlepage" => nil,
227
- "titlefile" => nil,
228
- "originaltitlefile" => nil,
229
- "profile" => nil,
230
- "colophon" => nil,
231
- "colophon_order" => %w[aut csl trl dsr ill edt pbl prt pht],
232
- "direction" => "ltr",
233
- "epubmaker" => {
234
- "flattoc" => nil,
235
- "flattocindent" => true,
236
- "ncx_indent" => [],
237
- "zip_stage1" => "zip -0Xq",
238
- "zip_stage2" => "zip -Xr9Dq",
239
- "zip_addpath" => nil,
240
- "hook_beforeprocess" => nil,
241
- "hook_afterfrontmatter" => nil,
242
- "hook_afterbody" => nil,
243
- "hook_afterbackmatter" => nil,
244
- "hook_aftercopyimage" => nil,
245
- "hook_prepack" => nil,
246
- "rename_for_legacy" => nil,
247
- "verify_target_images" => nil,
248
- "force_include_images" => [],
249
- "cover_linear" => nil,
250
- },
251
- "externallink" => true,
252
- "imagedir" => "images",
253
- "fontdir" => "fonts",
254
- "image_ext" => %w(png gif jpg jpeg svg ttf woff otf),
255
- "font_ext" => %w(ttf woff otf),
256
- })
214
+ "language" => "ja",
215
+ "date" => Time.now.strftime("%Y-%m-%d"),
216
+ "modified" => Time.now.strftime("%Y-%02m-%02dT%02H:%02M:%02SZ"),
217
+ "isbn" => nil,
218
+ "toclevel" => 2,
219
+ "stylesheet" => [],
220
+ "epubversion" => 3,
221
+ "htmlversion" => 5,
222
+ "secnolevel" => 2,
223
+ "pre_secnolevel" => 0,
224
+ "post_secnolevel" => 1,
225
+ "part_secnolevel" => 1,
226
+ "titlepage" => nil,
227
+ "titlefile" => nil,
228
+ "originaltitlefile" => nil,
229
+ "profile" => nil,
230
+ "colophon" => nil,
231
+ "colophon_order" => %w[aut csl trl dsr ill edt pbl prt pht],
232
+ "direction" => "ltr",
233
+ "epubmaker" => {
234
+ "flattoc" => nil,
235
+ "flattocindent" => true,
236
+ "ncx_indent" => [],
237
+ "zip_stage1" => "zip -0Xq",
238
+ "zip_stage2" => "zip -Xr9Dq",
239
+ "zip_addpath" => nil,
240
+ "hook_beforeprocess" => nil,
241
+ "hook_afterfrontmatter" => nil,
242
+ "hook_afterbody" => nil,
243
+ "hook_afterbackmatter" => nil,
244
+ "hook_aftercopyimage" => nil,
245
+ "hook_prepack" => nil,
246
+ "rename_for_legacy" => nil,
247
+ "verify_target_images" => nil,
248
+ "force_include_images" => [],
249
+ "cover_linear" => nil,
250
+ },
251
+ "externallink" => true,
252
+ "imagedir" => "images",
253
+ "fontdir" => "fonts",
254
+ "image_ext" => %w(png gif jpg jpeg svg ttf woff otf),
255
+ "font_ext" => %w(ttf woff otf),
256
+ })
257
257
 
258
258
  @params = defaults.deep_merge(@params)
259
259
  @params["title"] = @params["booktitle"] unless @params["title"]