6
6
__license__ = 'GPL v3'
7
7
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
9
10
from collections import defaultdict
10
11
from urlparse import urlparse
12
13
from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES
13
14
from calibre.ebooks.oeb.polish.container import OEB_FONTS
14
from calibre.ebooks.oeb.polish.utils import guess_type
15
from calibre.ebooks.oeb.polish.utils import guess_type, actual_case_for_name, corrected_case_for_name
15
16
from calibre.ebooks.oeb.polish.check.base import BaseError, WARN, INFO
17
18
class BadLink(BaseError):
20
21
' either fix, or remove the link.')
24
class CaseMismatch(BadLink):
26
def __init__(self, href, corrected_name, name, lnum, col):
27
BadLink.__init__(self, _('The linked to resource {0} does not exist').format(href), name, line=lnum, col=col)
28
self.HELP = _('The case of the link {0} and the case of the actual file it points to {1}'
29
' do not agree. You should change either the case of the link or rename the file.').format(
31
self.INDIVIDUAL_FIX = _('Change the case of the link to match the actual file')
32
self.corrected_name = corrected_name
35
def __call__(self, container):
36
frag = urlparse(self.href).fragment
37
nhref = container.name_to_href(self.corrected_name, self.name)
41
class LinkReplacer(object):
43
def __call__(self, url):
48
replacer = LinkReplacer()
49
container.replace_links(self.name, replacer)
50
return replacer.replaced
52
class BadDestinationType(BaseError):
56
def __init__(self, link_source, link_dest, link_elem):
57
BaseError.__init__(self, _('Link points to a file that is not a text document'), link_source, line=link_elem.sourceline)
58
self.HELP = _('The link "{0}" points to a file <i>{1}</i> that is not a text (HTML) document.'
59
' Many ebook readers will be unable to follow such a link. You should'
60
' either remove the link or change it to point to a text document.'
61
' For example, if it points to an image, you can create small wrapper'
62
' document that contains the image and change the link to point to that.').format(
63
link_elem.get('href'), link_dest)
64
self.bad_href = link_elem.get('href')
23
66
class FileLink(BadLink):
25
68
HELP = _('This link uses the file:// URL scheme. This does not work with many ebook readers.'
82
125
' The recommended mimetype for files with the extension "{2}" is {3}.'
83
126
' You should change either the file extension or the mimetype in the OPF.').format(
84
127
name, opf_mt, ext, ext_mt)
85
self.INDIVIDUAL_FIX = _('Change the mimetype for this file in the OPF to %s') % ext_mt
128
if opf_mt in OEB_DOCS and name in {n for n, l in container.spine_names}:
129
self.INDIVIDUAL_FIX = _('Change the file extension to .xhtml')
130
self.change_ext_to = 'xhtml'
132
self.INDIVIDUAL_FIX = _('Change the mimetype for this file in the OPF to %s') % ext_mt
133
self.change_ext_to = None
87
135
def __call__(self, container):
89
for item in container.opf_xpath('//opf:manifest/opf:item[@href and @media-type="%s"]' % self.opf_mt):
90
name = container.href_to_name(item.get('href'), container.opf_name)
91
if name == self.file_name:
93
item.set('media-type', self.ext_mt)
94
container.mime_map[name] = self.ext_mt
96
container.dirty(container.opf_name)
137
if self.change_ext_to is not None:
138
from calibre.ebooks.oeb.polish.replace import rename_files
139
new_name = self.file_name.rpartition('.')[0] + '.' + self.change_ext_to
141
while container.has_name(new_name):
143
new_name = self.file_name.rpartition('.')[0] + ('%d.' % c) + self.change_ext_to
144
rename_files(container, {self.file_name:new_name})
147
for item in container.opf_xpath('//opf:manifest/opf:item[@href and @media-type="%s"]' % self.opf_mt):
148
name = container.href_to_name(item.get('href'), container.opf_name)
149
if name == self.file_name:
151
item.set('media-type', self.ext_mt)
152
container.mime_map[name] = self.ext_mt
154
container.dirty(container.opf_name)
99
157
def check_mimetypes(container):
107
165
a(MimetypeMismatch(container, name, mt, gt))
168
def check_link_destinations(container):
170
for name, mt in container.mime_map.iteritems():
172
for a in container.parsed(name).xpath('//*[local-name()="a" and @href]'):
173
tname = container.href_to_name(a.get('href'), name)
174
if tname and tname in container.mime_map and container.mime_map[tname] not in OEB_DOCS:
175
errors.append(BadDestinationType(name, tname, a))
110
178
def check_links(container):
111
179
links_map = defaultdict(set)
112
180
xml_types = {guess_type('a.opf'), guess_type('a.ncx')}
128
196
if tname in container.mime_map:
129
197
links_map[name].add(tname)
131
a(BadLink(_('The linked resource %s is a directory') % fl(href), name, lnum, col))
199
# Filesystem says the file exists, but it is not in
200
# the mime_map, so either there is a case mismatch
201
# or the link is a directory
202
apath = container.name_to_abspath(tname)
203
if os.path.isdir(apath):
204
a(BadLink(_('The linked resource %s is a directory') % fl(href), name, lnum, col))
206
a(CaseMismatch(href, actual_case_for_name(container, tname), name, lnum, col))
133
a(BadLink(_('The linked resource %s does not exist') % fl(href), name, lnum, col))
208
cname = corrected_case_for_name(container, tname)
209
if cname is not None:
210
a(CaseMismatch(href, cname, name, lnum, col))
212
a(BadLink(_('The linked resource %s does not exist') % fl(href), name, lnum, col))
135
214
purl = urlparse(href)
136
215
if purl.scheme == 'file':