review 2.1.0 → 2.2.0

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