softcover 1.1.22 → 1.1.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/softcover/article_template/latex_gitignore +2 -0
- data/lib/softcover/article_template/markdown_gitignore +2 -0
- data/lib/softcover/book_manifest.rb +2 -2
- data/lib/softcover/book_template/latex_gitignore +2 -0
- data/lib/softcover/book_template/markdown_gitignore +1 -0
- data/lib/softcover/builders/epub.rb +35 -14
- data/lib/softcover/builders/html.rb +5 -3
- data/lib/softcover/commands/check.rb +3 -3
- data/lib/softcover/utils.rb +14 -4
- data/lib/softcover/version.rb +1 -1
- data/spec/builders/epub_spec.rb +9 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0525db14fac2a0c8d3e36fe1730e11aadea0f260
|
4
|
+
data.tar.gz: 6ac3cade0c04ae7cb4c970a4fe13012d4fb70b29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c575083b8d00e0262bec7b18d37d9d7d079618409a20bd745081c922bf2fdaef6a402cfc4b0bac1085869be71e665e60fad265c34fb5e56f7680fd4d2d01d247
|
7
|
+
data.tar.gz: c0aa8dc1442c3f738fba7aa99534a7701f146dfd0c538c38a9363fc8aa7bbe305072414620bd4c5cb1b2619367082645845bb342bb2890f18df9403f1c116732
|
@@ -31,7 +31,7 @@ class Softcover::BookManifest < OpenStruct
|
|
31
31
|
include Softcover::Utils
|
32
32
|
|
33
33
|
def fragment_name
|
34
|
-
"#{slug}_fragment
|
34
|
+
"#{slug}_fragment.#{html_extension}"
|
35
35
|
end
|
36
36
|
|
37
37
|
def fragment_path
|
@@ -281,7 +281,7 @@ class Softcover::BookManifest < OpenStruct
|
|
281
281
|
|
282
282
|
# Returns the name of the HTML file containing the full book.
|
283
283
|
def full_html_file
|
284
|
-
path("html/#{slug}
|
284
|
+
path("html/#{slug}.#{html_extension}")
|
285
285
|
end
|
286
286
|
|
287
287
|
# Returns chapters for the PDF.
|
@@ -25,6 +25,15 @@ module Softcover
|
|
25
25
|
!options[:amazon] && cover_img
|
26
26
|
end
|
27
27
|
|
28
|
+
def cover_filename
|
29
|
+
xhtml("cover.#{html_extension}")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Transforms foo.html to foo.xhtml
|
33
|
+
def xhtml(filename)
|
34
|
+
filename.sub('.html', '.xhtml')
|
35
|
+
end
|
36
|
+
|
28
37
|
def cover_img_path
|
29
38
|
path("#{images_dir}/#{cover_img}")
|
30
39
|
end
|
@@ -33,6 +42,11 @@ module Softcover
|
|
33
42
|
path('epub/OEBPS/images')
|
34
43
|
end
|
35
44
|
|
45
|
+
def nav_filename
|
46
|
+
xhtml("nav.#{html_extension}")
|
47
|
+
end
|
48
|
+
|
49
|
+
|
36
50
|
def escape(string)
|
37
51
|
CGI.escape_html(string)
|
38
52
|
end
|
@@ -42,7 +56,7 @@ module Softcover
|
|
42
56
|
toc_chapters, manifest_chapters, images)
|
43
57
|
if cover_id
|
44
58
|
cover_meta = %(<meta name="cover" content="#{cover_id}"/>)
|
45
|
-
cover_html =
|
59
|
+
cover_html = %(<item id="cover" href="#{cover_filename}" media-type="application/xhtml+xml"/>)
|
46
60
|
cover_ref = '<itemref idref="cover" linear="no" />'
|
47
61
|
else
|
48
62
|
cover_meta = cover_html = cover_ref = ''
|
@@ -61,7 +75,7 @@ module Softcover
|
|
61
75
|
#{cover_meta}
|
62
76
|
</metadata>
|
63
77
|
<manifest>
|
64
|
-
<item href="
|
78
|
+
<item href="#{nav_filename}" id="nav" media-type="application/xhtml+xml" properties="nav"/>
|
65
79
|
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
|
66
80
|
<item id="page-template.xpgt" href="styles/page-template.xpgt" media-type="application/vnd.adobe-page-template+xml"/>
|
67
81
|
<item id="pygments.css" href="styles/pygments.css" media-type="text/css"/>
|
@@ -160,6 +174,7 @@ module Softcover
|
|
160
174
|
# All the HTML is generated, so this clears out any unused files.
|
161
175
|
def remove_html
|
162
176
|
FileUtils.rm(Dir.glob(path('epub/OEBPS/*.html')))
|
177
|
+
FileUtils.rm(Dir.glob(path('epub/OEBPS/*.xhtml')))
|
163
178
|
end
|
164
179
|
|
165
180
|
# Removes images in case they are stale.
|
@@ -215,14 +230,18 @@ module Softcover
|
|
215
230
|
mkdir images_dir
|
216
231
|
mkdir texmath_dir
|
217
232
|
|
218
|
-
File.write(path(
|
233
|
+
File.write(path("epub/OEBPS/#{cover_filename}"), cover_page) if cover?(options)
|
219
234
|
|
220
235
|
pngs = []
|
221
236
|
chapters.each_with_index do |chapter, i|
|
222
|
-
target_filename = path("epub/OEBPS/#{chapter.fragment_name}")
|
237
|
+
target_filename = path("epub/OEBPS/#{xhtml(chapter.fragment_name)}")
|
223
238
|
File.open(target_filename, 'w') do |f|
|
224
239
|
content = File.read(path("html/#{chapter.fragment_name}"))
|
225
240
|
doc = strip_attributes(Nokogiri::HTML(content))
|
241
|
+
# Use xhtml in references.
|
242
|
+
doc.css('a.hyperref').each do |ref_node|
|
243
|
+
ref_node['href'] = ref_node['href'].sub('.html', xhtml('.html'))
|
244
|
+
end
|
226
245
|
body = doc.at_css('body')
|
227
246
|
if body.nil?
|
228
247
|
$stderr.puts "\nError: Document not built due to empty chapter"
|
@@ -262,9 +281,10 @@ module Softcover
|
|
262
281
|
# SHAs of their contents, which arranges both for unique filenames
|
263
282
|
# and for automatic disk caching.
|
264
283
|
def html_with_math(chapter, images_dir, texmath_dir, pngs, options={})
|
265
|
-
content = File.read(File.join("html",
|
284
|
+
content = File.read(File.join("html",
|
285
|
+
"#{chapter.slug}.#{html_extension}"))
|
266
286
|
pagejs = "#{File.dirname(__FILE__)}/utils/page.js"
|
267
|
-
url = "file://#{Dir.pwd}/html/#{chapter.slug}
|
287
|
+
url = "file://#{Dir.pwd}/html/#{chapter.slug}.#{html_extension}"
|
268
288
|
cmd = "#{phantomjs} #{pagejs} #{url}"
|
269
289
|
silence { silence_stream(STDERR) { system cmd } }
|
270
290
|
# Sometimes in tests the phantomjs_source.html file is missing.
|
@@ -323,7 +343,8 @@ module Softcover
|
|
323
343
|
end
|
324
344
|
# Make references relative.
|
325
345
|
source.css('a.hyperref').each do |ref_node|
|
326
|
-
ref_node['href'] = ref_node['href'].sub('.html',
|
346
|
+
ref_node['href'] = ref_node['href'].sub('.html',
|
347
|
+
xhtml('_fragment.html'))
|
327
348
|
end
|
328
349
|
source.at_css('div#book').children.to_xhtml
|
329
350
|
end
|
@@ -446,7 +467,7 @@ module Softcover
|
|
446
467
|
# Writes the navigation file.
|
447
468
|
# This is required by the EPUB standard.
|
448
469
|
def write_nav
|
449
|
-
File.write(
|
470
|
+
File.write("epub/OEBPS/#{nav_filename}", nav_html)
|
450
471
|
end
|
451
472
|
|
452
473
|
def container_xml
|
@@ -470,7 +491,7 @@ module Softcover
|
|
470
491
|
# Returns the content configuration file.
|
471
492
|
def content_opf(options={})
|
472
493
|
man_ch = chapters.map do |chapter|
|
473
|
-
%(<item id="#{chapter.slug}" href="#{chapter.fragment_name}" media-type="application/xhtml+xml"/>)
|
494
|
+
%(<item id="#{chapter.slug}" href="#{xhtml(chapter.fragment_name)}" media-type="application/xhtml+xml"/>)
|
474
495
|
end
|
475
496
|
toc_ch = chapters.map do |chapter|
|
476
497
|
%(<itemref idref="#{chapter.slug}"/>)
|
@@ -523,14 +544,14 @@ module Softcover
|
|
523
544
|
section_names_and_ids(article).each_with_index do |(name, id), n|
|
524
545
|
chapter_nav << %(<navPoint id="#{id}" playOrder="#{n+1}">)
|
525
546
|
chapter_nav << %( <navLabel><text>#{escape(name)}</text></navLabel>)
|
526
|
-
chapter_nav << %( <content src="#{article.fragment_name}##{id}"/>)
|
547
|
+
chapter_nav << %( <content src="#{xhtml(article.fragment_name)}##{id}"/>)
|
527
548
|
chapter_nav << %(</navPoint>)
|
528
549
|
end
|
529
550
|
else
|
530
551
|
chapters.each_with_index do |chapter, n|
|
531
552
|
chapter_nav << %(<navPoint id="#{chapter.slug}" playOrder="#{n+1}">)
|
532
553
|
chapter_nav << %( <navLabel><text>#{chapter_name(n)}</text></navLabel>)
|
533
|
-
chapter_nav << %( <content src="#{chapter.fragment_name}"/>)
|
554
|
+
chapter_nav << %( <content src="#{xhtml(chapter.fragment_name)}"/>)
|
534
555
|
chapter_nav << %(</navPoint>)
|
535
556
|
end
|
536
557
|
end
|
@@ -547,7 +568,7 @@ module Softcover
|
|
547
568
|
if article?
|
548
569
|
article = chapters.first
|
549
570
|
nav_list = section_names_and_ids(article).map do |name, id|
|
550
|
-
%(<li> <a href="#{article.fragment_name}##{id}">#{name}</a></li>)
|
571
|
+
%(<li> <a href="#{xhtml(article.fragment_name)}##{id}">#{name}</a></li>)
|
551
572
|
end
|
552
573
|
else
|
553
574
|
nav_list = manifest.chapters.map do |chapter|
|
@@ -560,14 +581,14 @@ module Softcover
|
|
560
581
|
|
561
582
|
# Returns a navigation link for the chapter.
|
562
583
|
def nav_link(chapter)
|
563
|
-
%(<a href="#{chapter.fragment_name}">#{chapter.html_title}</a>)
|
584
|
+
%(<a href="#{xhtml(chapter.fragment_name)}">#{chapter.html_title}</a>)
|
564
585
|
end
|
565
586
|
|
566
587
|
# Returns a list of the section names and CSS ids.
|
567
588
|
# Form is [['Beginning', 'sec-beginning'], ['Next', 'sec-next']]
|
568
589
|
def section_names_and_ids(article)
|
569
590
|
# Grab section names and ids from the article.
|
570
|
-
filename = File.join('epub', 'OEBPS', article.fragment_name)
|
591
|
+
filename = File.join('epub', 'OEBPS', xhtml(article.fragment_name))
|
571
592
|
doc = Nokogiri::HTML(File.read(filename))
|
572
593
|
names = doc.css('div.section>h2').map do |s|
|
573
594
|
s.children.children.last.content
|
@@ -127,7 +127,7 @@ module Softcover
|
|
127
127
|
# The resulting file is a self-contained HTML document suitable
|
128
128
|
# for viewing in isolation.
|
129
129
|
def write_full_html_file(basename, file_content, options)
|
130
|
-
html_filename = File.join('html', basename
|
130
|
+
html_filename = File.join('html', "#{basename}.#{html_extension}")
|
131
131
|
File.open(html_filename, 'w') do |f|
|
132
132
|
f.write(file_content)
|
133
133
|
end
|
@@ -221,7 +221,8 @@ module Softcover
|
|
221
221
|
|
222
222
|
# Writes the chapter fragment HTML (omitting, e.g., <html> tags, etc.)
|
223
223
|
def write_fragment_file(chapter)
|
224
|
-
html_filename = File.join('html',
|
224
|
+
html_filename = File.join('html',
|
225
|
+
"#{chapter.slug}_fragment.#{html_extension}")
|
225
226
|
File.open(html_filename, 'w') do |f|
|
226
227
|
chapter.nodes.each do |node|
|
227
228
|
f.write(node.to_xhtml)
|
@@ -232,7 +233,7 @@ module Softcover
|
|
232
233
|
|
233
234
|
# Writes the chapter as a complete, self-contained HTML document.
|
234
235
|
def write_complete_file(chapter, erb_file, n)
|
235
|
-
html_filename = File.join('html', chapter.slug
|
236
|
+
html_filename = File.join('html', "#{chapter.slug}.#{html_extension}")
|
236
237
|
# Make references absolute.
|
237
238
|
chapter.nodes.each do |node|
|
238
239
|
node.css('a.hyperref').each do |ref_node|
|
@@ -255,6 +256,7 @@ module Softcover
|
|
255
256
|
# This also arranges to clear out unused HTML files, as happens when,
|
256
257
|
# e.g., the name of a LaTeX chapter file changes.
|
257
258
|
FileUtils.rm(Dir.glob(path('html/*.html')))
|
259
|
+
FileUtils.rm(Dir.glob(path('html/*.xhtml')))
|
258
260
|
end
|
259
261
|
end
|
260
262
|
end
|
@@ -87,9 +87,9 @@ module Softcover
|
|
87
87
|
"Install zip (e.g., apt-get install zip)"
|
88
88
|
when :epubcheck
|
89
89
|
url = 'https://github.com/IDPF/epubcheck/releases/'
|
90
|
-
url += 'download/
|
91
|
-
message = "EpubCheck
|
92
|
-
message += " ∟ Unzip and place epubcheck-
|
90
|
+
url += 'download/v4.0.1/epubcheck-4.0.1.zip'
|
91
|
+
message = "EpubCheck 4.0.1 (#{url})\n"
|
92
|
+
message += " ∟ Unzip and place epubcheck-4.0.1/ in a directory on your path"
|
93
93
|
when :inkscape
|
94
94
|
message = "Inkscape (http://inkscape.org/)"
|
95
95
|
else
|
data/lib/softcover/utils.rb
CHANGED
@@ -41,6 +41,10 @@ module Softcover::Utils
|
|
41
41
|
Softcover::Config['api_key'].present?
|
42
42
|
end
|
43
43
|
|
44
|
+
def html_extension
|
45
|
+
'html'
|
46
|
+
end
|
47
|
+
|
44
48
|
UNITS = %W(B KB MB GB TB).freeze
|
45
49
|
|
46
50
|
def as_size(number)
|
@@ -238,6 +242,13 @@ module Softcover::Utils
|
|
238
242
|
lines.reject { |line| line =~ skip }.join("\n")
|
239
243
|
end
|
240
244
|
|
245
|
+
# Returns first location on the path for a given file.
|
246
|
+
def first_path(file)
|
247
|
+
possible_paths = ENV['PATH'].split(File::PATH_SEPARATOR).
|
248
|
+
collect { |x| File.join(x, file) }
|
249
|
+
possible_paths.find { |f| File.file?(f) }
|
250
|
+
end
|
251
|
+
|
241
252
|
# Returns the filename of a dependency given a label.
|
242
253
|
def dependency_filename(label)
|
243
254
|
case label
|
@@ -249,10 +260,9 @@ module Softcover::Utils
|
|
249
260
|
get_filename(:'ebook-convert')
|
250
261
|
when :epubcheck
|
251
262
|
# Finds EpubCheck anywhere on the path.
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
possible_paths.select { |f| File.file?(f) }.first || ""
|
263
|
+
version_3 = path('epubcheck-3.0/epubcheck-3.0.jar')
|
264
|
+
version_4 = path('epubcheck-4.0.1/epubcheck.jar')
|
265
|
+
first_path(version_4) || first_path(version_3) || ""
|
256
266
|
when :inkscape
|
257
267
|
default = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
|
258
268
|
filename_or_default(:inkscape, default)
|
data/lib/softcover/version.rb
CHANGED
data/spec/builders/epub_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Softcover::Builders::Epub do
|
4
4
|
before(:all) do
|
5
5
|
generate_book
|
6
|
-
@file_to_be_removed = path('html/should_be_removed.
|
6
|
+
@file_to_be_removed = path('html/should_be_removed.xhtml')
|
7
7
|
File.write(@file_to_be_removed, '')
|
8
8
|
silence { `softcover build:epub` }
|
9
9
|
@builder = Softcover::Builders::Epub.new
|
@@ -118,9 +118,9 @@ describe Softcover::Builders::Epub do
|
|
118
118
|
it { should exist }
|
119
119
|
|
120
120
|
it "should contain the right filenames in the right order" do
|
121
|
-
filenames = %w[frontmatter_fragment.
|
122
|
-
another_chapter_fragment.
|
123
|
-
yet_another_chapter_fragment.
|
121
|
+
filenames = %w[frontmatter_fragment.xhtml a_chapter_fragment.xhtml
|
122
|
+
another_chapter_fragment.xhtml
|
123
|
+
yet_another_chapter_fragment.xhtml]
|
124
124
|
source_files = doc.css('content').map { |node| node['src'] }
|
125
125
|
expect(source_files).to eql(filenames)
|
126
126
|
end
|
@@ -150,11 +150,11 @@ describe Softcover::Builders::Epub do
|
|
150
150
|
|
151
151
|
it "should create the HTML files" do
|
152
152
|
has_math = false
|
153
|
-
expect(path('epub/OEBPS/cover.
|
153
|
+
expect(path('epub/OEBPS/cover.xhtml')).to exist
|
154
154
|
builder.manifest.chapters.each_with_index do |chapter, i|
|
155
155
|
content = File.read(path("html/#{chapter.slug}_fragment.html"))
|
156
156
|
has_math ||= builder.math?(content)
|
157
|
-
fragment = path("epub/OEBPS/#{chapter.slug}_fragment.
|
157
|
+
fragment = path("epub/OEBPS/#{chapter.slug}_fragment.xhtml")
|
158
158
|
expect(fragment).to exist
|
159
159
|
end
|
160
160
|
# Make sure at least one template file has math.
|
@@ -162,7 +162,7 @@ describe Softcover::Builders::Epub do
|
|
162
162
|
end
|
163
163
|
|
164
164
|
describe "cover file" do
|
165
|
-
subject(:cover_file) { File.read(path('epub/OEBPS/cover.
|
165
|
+
subject(:cover_file) { File.read(path('epub/OEBPS/cover.xhtml')) }
|
166
166
|
it "should have the right cover image" do
|
167
167
|
expect(cover_file).to include 'cover.jpg'
|
168
168
|
end
|
@@ -263,7 +263,7 @@ describe Softcover::EpubUtils do
|
|
263
263
|
end
|
264
264
|
end
|
265
265
|
|
266
|
-
context "nav.
|
266
|
+
context "nav.xhtml template" do
|
267
267
|
let(:nav_list) { [] }
|
268
268
|
let(:template) do
|
269
269
|
dummy_class.new.nav_html_template(title, nav_list)
|
@@ -278,7 +278,7 @@ end
|
|
278
278
|
describe "article validation" do
|
279
279
|
before(:all) do
|
280
280
|
generate_book(markdown: true, article: true)
|
281
|
-
@file_to_be_removed = path('html/should_be_removed.
|
281
|
+
@file_to_be_removed = path('html/should_be_removed.xhtml')
|
282
282
|
File.write(@file_to_be_removed, '')
|
283
283
|
silence { `softcover build:epub` }
|
284
284
|
@builder = Softcover::Builders::Epub.new
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: softcover
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Hartl
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-01-
|
12
|
+
date: 2016-01-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: polytexnic
|