Sync to trunk.

This commit is contained in:
John Schember 2009-07-26 15:05:46 -04:00
commit bfbcd0fba5
17 changed files with 121 additions and 172 deletions

View File

@ -49,6 +49,7 @@ def freeze():
'/usr/lib/libMagickCore.so',
'/usr/lib/libgcrypt.so.11',
'/usr/lib/libgpg-error.so.0',
'/usr/lib/libphonon.so.4',
]
binary_includes += [os.path.join(QTDIR, 'lib%s.so.4'%x) for x in QTDLLS]

View File

@ -8,7 +8,7 @@ import os
import shutil
from itertools import cycle
from calibre import sanitize_file_name as sanitize
from calibre.utils.filenames import ascii_filename as sanitize
from calibre.devices.usbms.driver import USBMS
import calibre.devices.cybookg3.t2b as t2b
@ -98,7 +98,7 @@ class CYBOOKG3(USBMS):
self.report_progress(i / float(len(files)), _('Transferring books to device...'))
self.report_progress(1.0, _('Transferring books to device...'))
return zip(paths, cycle([on_card]))
def delete_books(self, paths, end_session=True):

View File

@ -8,7 +8,7 @@ import os, re, sys, shutil
from itertools import cycle
from calibre.devices.usbms.driver import USBMS
from calibre import sanitize_file_name as sanitize
from calibre.utils.filenames import ascii_filename as sanitize
from calibre.ebooks.metadata import string_to_authors
class JETBOOK(USBMS):

View File

@ -15,7 +15,7 @@ from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError
from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre import iswindows, islinux, isosx, __appname__
from calibre import sanitize_file_name as sanitize
from calibre.utils.filenames import ascii_filename as sanitize
class Device(DeviceConfig, DevicePlugin):
'''

View File

@ -253,7 +253,6 @@ def process_pages(pages, opts, update, tdir):
for job in jobs:
if job.failed:
raw_input()
raise Exception(_('Failed to process comic: \n\n%s')%
job.log_file.read())
pages, failures_ = job.result

View File

@ -18,38 +18,38 @@ from calibre.ebooks.lrf.pylrs.pylrs import Book, PageStyle, TextStyle, \
from calibre.ebooks.chardet import xml_to_unicode
class LrsParser(object):
SELF_CLOSING_TAGS = [i.lower() for i in ['CR', 'Plot', 'NoBR', 'Space',
'PutObj', 'RuledLine',
SELF_CLOSING_TAGS = [i.lower() for i in ['CR', 'Plot', 'NoBR', 'Space',
'PutObj', 'RuledLine',
'Plot', 'SetDefault', 'BookSetting', 'RegistFont',
'PageStyle', 'TextStyle', 'BlockStyle', 'JumpTo',
'ImageStream', 'Image']]
def __init__(self, stream, logger):
self.logger = logger
src = stream.read()
self.soup = BeautifulStoneSoup(xml_to_unicode(src)[0],
convertEntities=BeautifulStoneSoup.XML_ENTITIES,
convertEntities=BeautifulStoneSoup.XML_ENTITIES,
selfClosingTags=self.SELF_CLOSING_TAGS)
self.objects = {}
for obj in self.soup.findAll(objid=True):
self.objects[obj['objid']] = obj
self.parsed_objects = {}
self.first_pass()
self.second_pass()
self.third_pass()
self.fourth_pass()
self.fifth_pass()
def fifth_pass(self):
for tag in self.soup.findAll(['canvas', 'header', 'footer']):
canvas = self.parsed_objects[tag.get('objid')]
for po in tag.findAll('putobj'):
canvas.put_object(self.parsed_objects[po.get('refobj')],
po.get('x1'), po.get('y1'))
@classmethod
def attrs_to_dict(cls, tag, exclude=('objid',)):
result = {}
@ -58,7 +58,7 @@ class LrsParser(object):
continue
result[str(key)] = val
return result
def text_tag_to_element(self, tag):
map = {
'span' : Span,
@ -77,7 +77,7 @@ class LrsParser(object):
settings = self.attrs_to_dict(tag)
settings.pop('spanstyle', '')
return map[tag.name](**settings)
def process_text_element(self, tag, elem):
for item in tag.contents:
if isinstance(item, NavigableString):
@ -86,8 +86,8 @@ class LrsParser(object):
subelem = self.text_tag_to_element(item)
elem.append(subelem)
self.process_text_element(item, subelem)
def process_paragraph(self, tag):
p = Paragraph()
contents = [i for i in tag.contents]
@ -104,7 +104,7 @@ class LrsParser(object):
p.append(elem)
self.process_text_element(item, elem)
return p
def process_text_block(self, tag):
tb = self.parsed_objects[tag.get('objid')]
for item in tag.contents:
@ -119,25 +119,25 @@ class LrsParser(object):
elem = self.text_tag_to_element(item)
self.process_text_element(item, elem)
p.append(elem)
def fourth_pass(self):
for tag in self.soup.findAll('page'):
page = self.parsed_objects[tag.get('objid')]
self.book.append(page)
for block_tag in tag.findAll(['canvas', 'imageblock', 'textblock',
for block_tag in tag.findAll(['canvas', 'imageblock', 'textblock',
'ruledline', 'simpletextblock']):
if block_tag.name == 'ruledline':
page.append(RuledLine(**self.attrs_to_dict(block_tag)))
else:
page.append(self.parsed_objects[block_tag.get('objid')])
for tag in self.soup.find('objects').findAll('button'):
jt = tag.find('jumpto')
tb = self.parsed_objects[jt.get('refobj')]
jb = JumpButton(tb)
self.book.append(jb)
self.parsed_objects[tag.get('objid')] = jb
for tag in self.soup.findAll(['textblock', 'simpletextblock']):
self.process_text_block(tag)
toc = self.soup.find('toc')
@ -145,11 +145,11 @@ class LrsParser(object):
for tag in toc.findAll('toclabel'):
label = self.tag_to_string(tag)
self.book.addTocEntry(label, self.parsed_objects[tag.get('refobj')])
def third_pass(self):
map = {
'page' : (Page, ['pagestyle', 'evenfooterid',
'page' : (Page, ['pagestyle', 'evenfooterid',
'oddfooterid', 'evenheaderid', 'oddheaderid']),
'textblock' : (TextBlock, ['textstyle', 'blockstyle']),
'simpletextblock' : (TextBlock, ['textstyle', 'blockstyle']),
@ -167,7 +167,8 @@ class LrsParser(object):
settings = self.attrs_to_dict(tag, map[tag.name][1]+['objid', 'objlabel'])
for a in ('pagestyle', 'blockstyle', 'textstyle'):
label = tag.get(a, False)
if label:
if label and \
(label in self._style_labels or label in self.parsed_objects):
_obj = self.parsed_objects[label] if \
self.parsed_objects.has_key(label) else \
self._style_labels[label]
@ -181,9 +182,9 @@ class LrsParser(object):
if tag.has_key('canvaswidth'):
args += [tag.get('canvaswidth'), tag.get('canvasheight')]
self.parsed_objects[id] = map[tag.name][0](*args, **settings)
def second_pass(self):
map = {
'pagestyle' : (PageStyle, ['stylelabel', 'evenheaderid', 'oddheaderid', 'evenfooterid', 'oddfooterid']),
@ -207,8 +208,8 @@ class LrsParser(object):
self._style_labels[x] = self.parsed_objects[id]
if tag.name == 'registfont':
self.book.append(self.parsed_objects[id])
@classmethod
def tag_to_string(cls, tag):
'''
@ -226,20 +227,20 @@ class LrsParser(object):
res = cls.tag_to_string(item)
if res:
strings.append(res)
return u''.join(strings)
return u''.join(strings)
def first_pass(self):
info = self.soup.find('bbebxylog').find('bookinformation').find('info')
bookinfo = info.find('bookinfo')
docinfo = info.find('docinfo')
def me(base, tagname):
tag = base.find(tagname.lower())
if tag is None:
return ('', '', '')
tag = (self.tag_to_string(tag), tag.get('reading') if tag.has_key('reading') else '')
return tag
title = me(bookinfo, 'Title')
author = me(bookinfo, 'Author')
publisher = me(bookinfo, 'Publisher')
@ -250,12 +251,12 @@ class LrsParser(object):
creator = me(docinfo, 'Creator')[0]
producer = me(docinfo, 'Producer')[0]
bookid = me(bookinfo, 'BookID')[0]
sd = self.soup.find('setdefault')
sd = StyleDefault(**self.attrs_to_dict(sd, ['page_tree_id', 'rubyalignandadjust']))
bs = self.soup.find('booksetting')
bs = BookSetting(**self.attrs_to_dict(bs, []))
settings = {}
thumbnail = self.soup.find('cthumbnail')
if thumbnail is not None:
@ -264,23 +265,23 @@ class LrsParser(object):
settings['thumbnail'] = f
else:
print _('Could not read from thumbnail file:'), f
self.book = Book(title=title, author=author, publisher=publisher,
category=category, classification=classification,
freetext=freetext, language=language, creator=creator,
producer=producer, bookid=bookid, setdefault=sd,
booksetting=bs, **settings)
for hdr in self.soup.findAll(['header', 'footer']):
elem = Header if hdr.name == 'header' else Footer
self.parsed_objects[hdr.get('objid')] = elem(**self.attrs_to_dict(hdr))
self.parsed_objects[hdr.get('objid')] = elem(**self.attrs_to_dict(hdr))
def render(self, file, to_lrs=False):
if to_lrs:
self.book.renderLrs(file, 'utf-8')
else:
self.book.renderLrf(file)
def option_parser():
parser = OptionParser(usage=_('%prog [options] file.lrs\nCompile an LRS file into an LRF file.'))
@ -299,7 +300,7 @@ def main(args=sys.argv, logger=None):
level = logging.DEBUG if opts.verbose else logging.INFO
logger = logging.getLogger('lrs2lrf')
setup_cli_handlers(logger, level)
if len(args) != 2:
parser.print_help()
return 1
@ -310,7 +311,7 @@ def main(args=sys.argv, logger=None):
if opts.verbose:
import warnings
warnings.defaultaction = 'error'
logger.info('Parsing LRS file...')
converter = LrsParser(open(args[1], 'rb'), logger)
logger.info('Writing to output file...')
@ -320,4 +321,4 @@ def main(args=sys.argv, logger=None):
if __name__ == '__main__':
sys.exit(main())
sys.exit(main())

View File

@ -21,7 +21,8 @@ except ImportError:
from lxml import html, etree
from calibre import entity_to_unicode, sanitize_file_name
from calibre import entity_to_unicode
from calibre.utils.filenames import ascii_filename
from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks import DRMError
from calibre.ebooks.chardet import ENCODING_PATS
@ -374,7 +375,7 @@ class MobiReader(object):
fname = self.name.encode('ascii', 'replace')
fname = re.sub(r'[\x08\x15\0]+', '', fname)
htmlfile = os.path.join(output_dir,
sanitize_file_name(fname) + '.html')
ascii_filename(fname) + '.html')
try:
for ref in guide.xpath('descendant::reference'):
if ref.attrib.has_key('href'):

View File

@ -65,9 +65,9 @@ class Jacket(object):
if not comments.strip():
comments = ''
comments = comments.replace('\r\n', '\n').replace('\n\n', '<br/><br/>')
series = '<b>Series: </b>' + mi.series if mi.series else ''
series = '<b>Series: </b>' + escape(mi.series if mi.series else '')
if series and mi.series_index is not None:
series += ' [%s]'%mi.format_series_index()
series += escape(' [%s]'%mi.format_series_index())
tags = mi.tags
if not tags:
try:
@ -75,7 +75,7 @@ class Jacket(object):
except:
tags = []
if tags:
tags = '<b>Tags: </b>' + self.opts.dest.tags_to_string(tags)
tags = '<b>Tags: </b>' + escape(self.opts.dest.tags_to_string(tags))
else:
tags = ''
try:
@ -84,8 +84,8 @@ class Jacket(object):
title = _('Unknown')
html = self.JACKET_TEMPLATE%dict(xmlns=XPNSMAP['h'],
title=escape(title), comments=escape(comments),
jacket=escape(_('Book Jacket')), series=escape(series),
tags=escape(tags))
jacket=escape(_('Book Jacket')), series=series,
tags=tags)
id, href = self.oeb.manifest.generate('jacket', 'jacket.xhtml')
root = etree.fromstring(html)
item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root)

View File

@ -57,6 +57,7 @@ it under the same terms as Perl itself.
import re
from calibre.ebooks.unidecode.unicodepoints import CODEPOINTS
from calibre.constants import preferred_encoding
class Unidecoder(object):
@ -70,7 +71,10 @@ class Unidecoder(object):
try:
text = unicode(text)
except:
text = text.decode('utf-8', 'ignore')
try:
text = text.decode(preferred_encoding)
except:
text = text.decode('utf-8', 'replace')
# Replace characters larger than 127 with their ASCII equivelent.
return re.sub('[^\x00-\x7f]', lambda x: self.replace_point(x.group()),
text)
@ -80,7 +84,7 @@ class Unidecoder(object):
Returns the replacement character or ? if none can be found.
'''
try:
# Splite the unicode character xABCD into parts 0xAB and 0xCD.
# Split the unicode character xABCD into parts 0xAB and 0xCD.
# 0xAB represents the group within CODEPOINTS to query and 0xCD
# represents the position in the list of characters for the group.
return CODEPOINTS[self.code_group(codepoint)][self.grouped_point(

View File

@ -342,6 +342,8 @@ class FileDialog(QObject):
ftext += '%s (%s);;'%(text, ' '.join(extensions))
if add_all_files_filter or not ftext:
ftext += 'All files (*)'
if ftext.endswith(';;'):
ftext = ftext[:-2]
self.dialog_name = name if name else 'dialog_' + title
self.selected_files = None

View File

@ -21,7 +21,7 @@ from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
pixmap_to_data, warning_dialog, \
question_dialog
from calibre.ebooks.metadata import authors_to_string
from calibre import sanitize_file_name, preferred_encoding
from calibre import preferred_encoding
from calibre.utils.filenames import ascii_filename
from calibre.devices.errors import FreeSpaceError
from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \
@ -542,7 +542,7 @@ class DeviceGUI(object):
'\n\n' + t + '\n\t' + _('by') + ' ' + a + '\n\n' + \
_('in the %s format.') %
os.path.splitext(f)[1][1:].upper())
prefix = sanitize_file_name(t+' - '+a)
prefix = ascii_filename(t+' - '+a)
if not isinstance(prefix, unicode):
prefix = prefix.decode(preferred_encoding, 'replace')
attachment_names.append(prefix + os.path.splitext(f)[1])
@ -693,7 +693,7 @@ class DeviceGUI(object):
rows_are_ids=True)
names = []
for mi in metadata:
prefix = sanitize_file_name(mi['title'])
prefix = ascii_filename(mi['title'])
if not isinstance(prefix, unicode):
prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix)
@ -758,7 +758,7 @@ class DeviceGUI(object):
a = mi['authors']
if not a:
a = _('Unknown')
prefix = sanitize_file_name(t+' - '+a)
prefix = ascii_filename(t+' - '+a)
if not isinstance(prefix, unicode):
prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix)

View File

@ -526,3 +526,11 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
QDialog.accept(self)
if callable(self.accepted_callback):
self.accepted_callback(self.id)
def reject(self, *args):
cf = getattr(self, 'cover_fetcher', None)
if cf is not None and hasattr(cf, 'terminate'):
cf.terminate()
cf.wait()
QDialog.reject(self, *args)

View File

@ -14,8 +14,9 @@ from PyQt4.Qt import Qt, SIGNAL, QObject, QCoreApplication, QUrl, QTimer, \
QMessageBox, QStackedLayout
from PyQt4.QtSvg import QSvgRenderer
from calibre import __version__, __appname__, sanitize_file_name, \
from calibre import __version__, __appname__, \
iswindows, isosx, prints, patheq
from calibre.utils.filenames import ascii_filename
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import prefs, dynamic
from calibre.utils.ipc.server import Server
@ -852,7 +853,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
def _files_added(self, paths=[], names=[], infos=[], on_card=None):
if paths:
self.upload_books(paths,
list(map(sanitize_file_name, names)),
list(map(ascii_filename, names)),
infos, on_card=on_card)
self.status_bar.showMessage(
_('Uploading books to device.'), 2000)
@ -888,7 +889,17 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
'removed from your computer. Are you sure?')
+'</p>', 'library_delete_books', self):
return
ci = view.currentIndex()
row = None
if ci.isValid():
row = ci.row()
view.model().delete_books(rows)
if row is not None:
ci = view.model().index(row, 0)
if ci.isValid():
view.setCurrentIndex(ci)
sm = view.selectionModel()
sm.select(ci, sm.Select)
else:
if self.stack.currentIndex() == 1:
view = self.memory_view

View File

@ -34,7 +34,7 @@ from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_e
from calibre.ptempfile import PersistentTemporaryFile
from calibre.customize.ui import run_plugins_on_import
from calibre import sanitize_file_name
from calibre.utils.filenames import ascii_filename
from calibre.ebooks import BOOK_EXTENSIONS
if iswindows:
@ -652,8 +652,8 @@ class LibraryDatabase2(LibraryDatabase):
authors = self.authors(id, index_is_id=True)
if not authors:
authors = _('Unknown')
author = sanitize_file_name(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
title = sanitize_file_name(self.title(id, index_is_id=True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
author = ascii_filename(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
title = ascii_filename(self.title(id, index_is_id=True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
path = author + '/' + title + ' (%d)'%id
return path
@ -664,8 +664,8 @@ class LibraryDatabase2(LibraryDatabase):
authors = self.authors(id, index_is_id=True)
if not authors:
authors = _('Unknown')
author = sanitize_file_name(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
title = sanitize_file_name(self.title(id, index_is_id=True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
author = ascii_filename(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
title = ascii_filename(self.title(id, index_is_id=True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
name = title + ' - ' + author
while name.endswith('.'):
name = name[:-1]
@ -1520,12 +1520,12 @@ class LibraryDatabase2(LibraryDatabase):
x['cover'] = os.path.join(path, 'cover.jpg')
if not self.has_cover(x['id'], index_is_id=True):
x['cover'] = None
path += os.sep + self.construct_file_name(record[FIELD_MAP['id']]) + '.%s'
formats = self.formats(record[FIELD_MAP['id']], index_is_id=True)
if formats:
for fmt in formats.split(','):
x['formats'].append(path%fmt.lower())
x['fmt_'+fmt.lower()] = path%fmt.lower()
path = self.format_abspath(x['id'], fmt, index_is_id=True)
x['formats'].append(path)
x['fmt_'+fmt.lower()] = path
x['available_formats'] = [i.upper() for i in formats.split(',')]
return data
@ -1602,12 +1602,12 @@ books_series_link feeds
by_author[au] = []
by_author[au].append(index)
for au in by_author.keys():
apath = os.path.join(dir, sanitize_file_name(au))
apath = os.path.join(dir, ascii_filename(au))
if not single_dir and not os.path.exists(apath):
os.mkdir(apath)
for idx in by_author[au]:
title = re.sub(r'\s', ' ', self.title(idx, index_is_id=index_is_id))
tpath = os.path.join(apath, sanitize_file_name(title))
tpath = os.path.join(apath, ascii_filename(title))
id = idx if index_is_id else self.id(idx)
id = str(id)
if not single_dir and not os.path.exists(tpath):
@ -1621,10 +1621,10 @@ books_series_link feeds
mi.authors = [_('Unknown')]
cdata = self.cover(int(id), index_is_id=True)
if cdata is not None:
cname = sanitize_file_name(name)+'.jpg'
cname = ascii_filename(name)+'.jpg'
open(os.path.join(base, cname), 'wb').write(cdata)
mi.cover = cname
with open(os.path.join(base, sanitize_file_name(name)+'.opf'),
with open(os.path.join(base, ascii_filename(name)+'.opf'),
'wb') as f:
f.write(metadata_to_opf(mi))
@ -1636,7 +1636,7 @@ books_series_link feeds
if not data:
continue
fname = name +'.'+fmt.lower()
fname = sanitize_file_name(fname)
fname = ascii_filename(fname)
f = open(os.path.join(base, fname), 'w+b')
f.write(data)
f.flush()
@ -1671,7 +1671,7 @@ books_series_link feeds
if not au:
au = _('Unknown')
fname = '%s - %s.%s'%(title, au, format.lower())
fname = sanitize_file_name(fname)
fname = ascii_filename(fname)
if not os.path.exists(dir):
os.makedirs(dir)
f = open(os.path.join(dir, fname), 'w+b')

View File

@ -82,10 +82,6 @@ sudo python -c "import urllib2; exec urllib2.urlopen('http://calibre.kovidgoyal.
</pre>
<h4>Note</h4>
<ul>
<li>On some linux distributions, you have to install the
libphonon (may be called libphonon4) package for calibre
to work.
</li>
<li>
When running the command line utilities,
they will segfault after completion. This can

View File

@ -1,97 +1,23 @@
# -*- coding: utf-8 -*-
'''
Make strings safe for use as ASCII filenames, while trying to preserve as much
Make strings safe for use as ASCII filenames, while trying to preserve as much
meaning as possible.
'''
import re, string
from calibre.ebooks.unidecode.unidecoder import Unidecoder
from calibre import sanitize_file_name
from calibre.constants import preferred_encoding
udc = Unidecoder()
MAP = {
u"" : "'",
u"" : "'",
u"«" : '"',
u"»" : '"',
u"" : "...",
u"" : "#",
u"Щ" : "Shh",
u"Ё" : "Jo",
u"Ж" : "Zh",
u"Ц" : "C",
u"Ч" : "Ch",
u"Ш" : "Sh",
u"Ы" : "Y",
u"Ю" : "Ju",
u"Я" : "Ja",
u"Б" : "B",
u"Г" : "G",
u"Д" : "D",
u"И" : "I",
u"Й" : "J",
u"К" : "K",
u"Л" : "L",
u"П" : "P",
u"Ф" : "F",
u"Э" : "E",
u"Ъ" : "`",
u"Ь" : "'",
u"щ" : "shh",
u"ё" : "jo",
u"ж" : "zh",
u"ц" : "c",
u"ч" : "ch",
u"ш" : "sh",
u"ы" : "y",
u"ю" : "ju",
u"я" : "ja",
u"б" : "b",
u"в" : "v",
u"г" : "g",
u"д" : "d",
u"з" : "z",
u"и" : "i",
u"й" : "j",
u"к" : "k",
u"л" : "l",
u"м" : "m",
u"н" : "n",
u"о" : "o",
u"п" : "p",
u"т" : "t",
u"ф" : "f",
u"э" : "e",
u"ъ" : "`",
u"ь" : "'",
u"А" : "A",
u"В" : "V",
u"Е" : "Je",
u"З" : "Z",
u"М" : "M",
u"Н" : "N",
u"О" : "O",
u"Р" : "R",
u"С" : "S",
u"Т" : "T",
u"У" : "U",
u"Х" : "Kh",
u"Є" : "Je",
u"Ї" : "Ji",
u"а" : "a",
u"е" : "je",
u"р" : "r",
u"с" : "s",
u"у" : "u",
u"х" : "kh",
u"є" : "je",
} #: Translation table
def ascii_text(orig):
try:
ascii = udc.decode(orig)
except:
if isinstance(orig, unicode):
ascii = orig.encode('ascii', 'replace')
ascii = orig.decode(preferred_encoding,
'replace').encode('ascii', 'replace')
return ascii
for c in string.whitespace:
MAP[c] = ' '
PAT = re.compile('['+u''.join(MAP.keys())+']')
def ascii_filename(orig):
orig = PAT.sub(lambda m:MAP[m.group()], orig)
buf = []
for i in range(len(orig)):
val = ord(orig[i])
buf.append('_' if val < 33 or val > 126 else orig[i])
return (''.join(buf)).encode('ascii')
return sanitize_file_name(ascii_text(orig).replace('?', '_'))

View File

@ -14,8 +14,8 @@ from httplib import responses
from PIL import Image
from cStringIO import StringIO
from calibre import browser, sanitize_file_name, \
relpath, unicode_path
from calibre import browser, relpath, unicode_path
from calibre.utils.filenames import ascii_filename
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
from calibre.ebooks.chardet import xml_to_unicode
from calibre.utils.config import OptionParser
@ -313,7 +313,7 @@ class RecursiveFetcher(object):
self.log.exception('Could not fetch image %s'% iurl)
continue
c += 1
fname = sanitize_file_name('img'+str(c)+ext)
fname = ascii_filename('img'+str(c)+ext)
if isinstance(fname, unicode):
fname = fname.encode('ascii', 'replace')
imgpath = os.path.join(diskpath, fname+'.jpg')
@ -416,7 +416,7 @@ class RecursiveFetcher(object):
if not isinstance(_fname, unicode):
_fname.decode('latin1', 'replace')
_fname = _fname.encode('ascii', 'replace').replace('%', '').replace(os.sep, '')
_fname = sanitize_file_name(_fname)
_fname = ascii_filename(_fname)
_fname = os.path.splitext(_fname)[0]+'.xhtml'
res = os.path.join(linkdiskpath, _fname)
self.downloaded_paths.append(res)