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/libMagickCore.so',
'/usr/lib/libgcrypt.so.11', '/usr/lib/libgcrypt.so.11',
'/usr/lib/libgpg-error.so.0', '/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] 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 import shutil
from itertools import cycle 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 from calibre.devices.usbms.driver import USBMS
import calibre.devices.cybookg3.t2b as t2b 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(i / float(len(files)), _('Transferring books to device...'))
self.report_progress(1.0, _('Transferring books to device...')) self.report_progress(1.0, _('Transferring books to device...'))
return zip(paths, cycle([on_card])) return zip(paths, cycle([on_card]))
def delete_books(self, paths, end_session=True): def delete_books(self, paths, end_session=True):

View File

@ -8,7 +8,7 @@ import os, re, sys, shutil
from itertools import cycle from itertools import cycle
from calibre.devices.usbms.driver import USBMS 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 from calibre.ebooks.metadata import string_to_authors
class JETBOOK(USBMS): class JETBOOK(USBMS):

View File

@ -15,7 +15,7 @@ from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError from calibre.devices.errors import DeviceError
from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre import iswindows, islinux, isosx, __appname__ 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): class Device(DeviceConfig, DevicePlugin):
''' '''

View File

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

View File

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

View File

@ -65,9 +65,9 @@ class Jacket(object):
if not comments.strip(): if not comments.strip():
comments = '' comments = ''
comments = comments.replace('\r\n', '\n').replace('\n\n', '<br/><br/>') 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: 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 tags = mi.tags
if not tags: if not tags:
try: try:
@ -75,7 +75,7 @@ class Jacket(object):
except: except:
tags = [] tags = []
if 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: else:
tags = '' tags = ''
try: try:
@ -84,8 +84,8 @@ class Jacket(object):
title = _('Unknown') title = _('Unknown')
html = self.JACKET_TEMPLATE%dict(xmlns=XPNSMAP['h'], html = self.JACKET_TEMPLATE%dict(xmlns=XPNSMAP['h'],
title=escape(title), comments=escape(comments), title=escape(title), comments=escape(comments),
jacket=escape(_('Book Jacket')), series=escape(series), jacket=escape(_('Book Jacket')), series=series,
tags=escape(tags)) tags=tags)
id, href = self.oeb.manifest.generate('jacket', 'jacket.xhtml') id, href = self.oeb.manifest.generate('jacket', 'jacket.xhtml')
root = etree.fromstring(html) root = etree.fromstring(html)
item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root) 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 import re
from calibre.ebooks.unidecode.unicodepoints import CODEPOINTS from calibre.ebooks.unidecode.unicodepoints import CODEPOINTS
from calibre.constants import preferred_encoding
class Unidecoder(object): class Unidecoder(object):
@ -70,7 +71,10 @@ class Unidecoder(object):
try: try:
text = unicode(text) text = unicode(text)
except: 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. # Replace characters larger than 127 with their ASCII equivelent.
return re.sub('[^\x00-\x7f]', lambda x: self.replace_point(x.group()), return re.sub('[^\x00-\x7f]', lambda x: self.replace_point(x.group()),
text) text)
@ -80,7 +84,7 @@ class Unidecoder(object):
Returns the replacement character or ? if none can be found. Returns the replacement character or ? if none can be found.
''' '''
try: 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 # 0xAB represents the group within CODEPOINTS to query and 0xCD
# represents the position in the list of characters for the group. # represents the position in the list of characters for the group.
return CODEPOINTS[self.code_group(codepoint)][self.grouped_point( return CODEPOINTS[self.code_group(codepoint)][self.grouped_point(

View File

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

View File

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

View File

@ -526,3 +526,11 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
QDialog.accept(self) QDialog.accept(self)
if callable(self.accepted_callback): if callable(self.accepted_callback):
self.accepted_callback(self.id) 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 QMessageBox, QStackedLayout
from PyQt4.QtSvg import QSvgRenderer from PyQt4.QtSvg import QSvgRenderer
from calibre import __version__, __appname__, sanitize_file_name, \ from calibre import __version__, __appname__, \
iswindows, isosx, prints, patheq iswindows, isosx, prints, patheq
from calibre.utils.filenames import ascii_filename
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import prefs, dynamic from calibre.utils.config import prefs, dynamic
from calibre.utils.ipc.server import Server 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): def _files_added(self, paths=[], names=[], infos=[], on_card=None):
if paths: if paths:
self.upload_books(paths, self.upload_books(paths,
list(map(sanitize_file_name, names)), list(map(ascii_filename, names)),
infos, on_card=on_card) infos, on_card=on_card)
self.status_bar.showMessage( self.status_bar.showMessage(
_('Uploading books to device.'), 2000) _('Uploading books to device.'), 2000)
@ -888,7 +889,17 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
'removed from your computer. Are you sure?') 'removed from your computer. Are you sure?')
+'</p>', 'library_delete_books', self): +'</p>', 'library_delete_books', self):
return return
ci = view.currentIndex()
row = None
if ci.isValid():
row = ci.row()
view.model().delete_books(rows) 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: else:
if self.stack.currentIndex() == 1: if self.stack.currentIndex() == 1:
view = self.memory_view 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.ptempfile import PersistentTemporaryFile
from calibre.customize.ui import run_plugins_on_import 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 from calibre.ebooks import BOOK_EXTENSIONS
if iswindows: if iswindows:
@ -652,8 +652,8 @@ class LibraryDatabase2(LibraryDatabase):
authors = self.authors(id, index_is_id=True) authors = self.authors(id, index_is_id=True)
if not authors: if not authors:
authors = _('Unknown') authors = _('Unknown')
author = sanitize_file_name(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore') author = ascii_filename(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') title = ascii_filename(self.title(id, index_is_id=True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'ignore')
path = author + '/' + title + ' (%d)'%id path = author + '/' + title + ' (%d)'%id
return path return path
@ -664,8 +664,8 @@ class LibraryDatabase2(LibraryDatabase):
authors = self.authors(id, index_is_id=True) authors = self.authors(id, index_is_id=True)
if not authors: if not authors:
authors = _('Unknown') authors = _('Unknown')
author = sanitize_file_name(authors.split(',')[0][:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace') author = ascii_filename(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') title = ascii_filename(self.title(id, index_is_id=True)[:self.PATH_LIMIT]).decode(filesystem_encoding, 'replace')
name = title + ' - ' + author name = title + ' - ' + author
while name.endswith('.'): while name.endswith('.'):
name = name[:-1] name = name[:-1]
@ -1520,12 +1520,12 @@ class LibraryDatabase2(LibraryDatabase):
x['cover'] = os.path.join(path, 'cover.jpg') x['cover'] = os.path.join(path, 'cover.jpg')
if not self.has_cover(x['id'], index_is_id=True): if not self.has_cover(x['id'], index_is_id=True):
x['cover'] = None 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) formats = self.formats(record[FIELD_MAP['id']], index_is_id=True)
if formats: if formats:
for fmt in formats.split(','): for fmt in formats.split(','):
x['formats'].append(path%fmt.lower()) path = self.format_abspath(x['id'], fmt, index_is_id=True)
x['fmt_'+fmt.lower()] = path%fmt.lower() x['formats'].append(path)
x['fmt_'+fmt.lower()] = path
x['available_formats'] = [i.upper() for i in formats.split(',')] x['available_formats'] = [i.upper() for i in formats.split(',')]
return data return data
@ -1602,12 +1602,12 @@ books_series_link feeds
by_author[au] = [] by_author[au] = []
by_author[au].append(index) by_author[au].append(index)
for au in by_author.keys(): 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): if not single_dir and not os.path.exists(apath):
os.mkdir(apath) os.mkdir(apath)
for idx in by_author[au]: for idx in by_author[au]:
title = re.sub(r'\s', ' ', self.title(idx, index_is_id=index_is_id)) 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 = idx if index_is_id else self.id(idx)
id = str(id) id = str(id)
if not single_dir and not os.path.exists(tpath): if not single_dir and not os.path.exists(tpath):
@ -1621,10 +1621,10 @@ books_series_link feeds
mi.authors = [_('Unknown')] mi.authors = [_('Unknown')]
cdata = self.cover(int(id), index_is_id=True) cdata = self.cover(int(id), index_is_id=True)
if cdata is not None: 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) open(os.path.join(base, cname), 'wb').write(cdata)
mi.cover = cname 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: 'wb') as f:
f.write(metadata_to_opf(mi)) f.write(metadata_to_opf(mi))
@ -1636,7 +1636,7 @@ books_series_link feeds
if not data: if not data:
continue continue
fname = name +'.'+fmt.lower() fname = name +'.'+fmt.lower()
fname = sanitize_file_name(fname) fname = ascii_filename(fname)
f = open(os.path.join(base, fname), 'w+b') f = open(os.path.join(base, fname), 'w+b')
f.write(data) f.write(data)
f.flush() f.flush()
@ -1671,7 +1671,7 @@ books_series_link feeds
if not au: if not au:
au = _('Unknown') au = _('Unknown')
fname = '%s - %s.%s'%(title, au, format.lower()) fname = '%s - %s.%s'%(title, au, format.lower())
fname = sanitize_file_name(fname) fname = ascii_filename(fname)
if not os.path.exists(dir): if not os.path.exists(dir):
os.makedirs(dir) os.makedirs(dir)
f = open(os.path.join(dir, fname), 'w+b') 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> </pre>
<h4>Note</h4> <h4>Note</h4>
<ul> <ul>
<li>On some linux distributions, you have to install the
libphonon (may be called libphonon4) package for calibre
to work.
</li>
<li> <li>
When running the command line utilities, When running the command line utilities,
they will segfault after completion. This can 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. 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 = { def ascii_text(orig):
u"" : "'", try:
u"" : "'", ascii = udc.decode(orig)
u"«" : '"', except:
u"»" : '"', if isinstance(orig, unicode):
u"" : "...", ascii = orig.encode('ascii', 'replace')
u"" : "#", ascii = orig.decode(preferred_encoding,
u"Щ" : "Shh", 'replace').encode('ascii', 'replace')
u"Ё" : "Jo", return ascii
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
for c in string.whitespace:
MAP[c] = ' '
PAT = re.compile('['+u''.join(MAP.keys())+']')
def ascii_filename(orig): def ascii_filename(orig):
orig = PAT.sub(lambda m:MAP[m.group()], orig) return sanitize_file_name(ascii_text(orig).replace('?', '_'))
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')

View File

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