~ubuntu-branches/debian/sid/calibre/sid

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/oeb/polish/check/links.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2014-02-27 07:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 74.
  • Revision ID: package-import@ubuntu.com-20140227074806-64wdebb3ptosxhhx
Tags: upstream-1.25.0+dfsg
ImportĀ upstreamĀ versionĀ 1.25.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
__license__ = 'GPL v3'
7
7
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
8
8
 
 
9
import os
9
10
from collections import defaultdict
10
11
from urlparse import urlparse
11
12
 
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
16
17
 
17
18
class BadLink(BaseError):
20
21
             ' either fix, or remove the link.')
21
22
    level = WARN
22
23
 
 
24
class CaseMismatch(BadLink):
 
25
 
 
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(
 
30
                          href, corrected_name)
 
31
        self.INDIVIDUAL_FIX = _('Change the case of the link to match the actual file')
 
32
        self.corrected_name = corrected_name
 
33
        self.href = href
 
34
 
 
35
    def __call__(self, container):
 
36
        frag = urlparse(self.href).fragment
 
37
        nhref = container.name_to_href(self.corrected_name, self.name)
 
38
        if frag:
 
39
            nhref += '#' + frag
 
40
        orig_href = self.href
 
41
        class LinkReplacer(object):
 
42
            replaced = False
 
43
            def __call__(self, url):
 
44
                if url != orig_href:
 
45
                    return url
 
46
                self.replaced = True
 
47
                return nhref
 
48
        replacer = LinkReplacer()
 
49
        container.replace_links(self.name, replacer)
 
50
        return replacer.replaced
 
51
 
 
52
class BadDestinationType(BaseError):
 
53
 
 
54
    level = WARN
 
55
 
 
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')
 
65
 
23
66
class FileLink(BadLink):
24
67
 
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'
 
131
        else:
 
132
            self.INDIVIDUAL_FIX = _('Change the mimetype for this file in the OPF to %s') % ext_mt
 
133
            self.change_ext_to = None
86
134
 
87
135
    def __call__(self, container):
88
136
        changed = False
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:
92
 
                changed = True
93
 
                item.set('media-type', self.ext_mt)
94
 
                container.mime_map[name] = self.ext_mt
95
 
        if changed:
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
 
140
            c = 0
 
141
            while container.has_name(new_name):
 
142
                c += 1
 
143
                new_name = self.file_name.rpartition('.')[0] + ('%d.' % c) + self.change_ext_to
 
144
            rename_files(container, {self.file_name:new_name})
 
145
            changed = True
 
146
        else:
 
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:
 
150
                    changed = True
 
151
                    item.set('media-type', self.ext_mt)
 
152
                    container.mime_map[name] = self.ext_mt
 
153
            if changed:
 
154
                container.dirty(container.opf_name)
97
155
        return changed
98
156
 
99
157
def check_mimetypes(container):
107
165
            a(MimetypeMismatch(container, name, mt, gt))
108
166
    return errors
109
167
 
 
168
def check_link_destinations(container):
 
169
    errors = []
 
170
    for name, mt in container.mime_map.iteritems():
 
171
        if mt in OEB_DOCS:
 
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))
 
176
    return errors
 
177
 
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)
130
198
                        else:
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))
 
205
                            else:
 
206
                                a(CaseMismatch(href, actual_case_for_name(container, tname), name, lnum, col))
132
207
                    else:
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))
 
211
                        else:
 
212
                            a(BadLink(_('The linked resource %s does not exist') % fl(href), name, lnum, col))
134
213
                else:
135
214
                    purl = urlparse(href)
136
215
                    if purl.scheme == 'file':