This commit is contained in:
Kovid Goyal 2013-05-29 08:01:36 +05:30
parent 3d699bb357
commit 826d5ab725
8 changed files with 173 additions and 172 deletions

View File

@ -22,7 +22,7 @@ class WeeklyLWN(BasicNewsRecipe):
remove_javascript = True remove_javascript = True
cover_url = site_url + '/images/lcorner.png' cover_url = site_url + '/images/lcorner.png'
#masthead_url = 'http://lwn.net/images/lcorner.png' # masthead_url = 'http://lwn.net/images/lcorner.png'
publication_type = 'magazine' publication_type = 'magazine'
remove_tags_before = dict(attrs={'class':'PageHeadline'}) remove_tags_before = dict(attrs={'class':'PageHeadline'})
@ -83,9 +83,9 @@ class WeeklyLWN(BasicNewsRecipe):
subsection = None subsection = None
while True: while True:
curr = curr.findNext(attrs = {'class': ['SummaryHL', 'Cat1HL', 'Cat2HL'] }) curr = curr.findNext(attrs={'class': ['SummaryHL', 'Cat1HL', 'Cat2HL']})
if curr == None: if curr is None:
break break
text = curr.contents[0].string text = curr.contents[0].string
@ -106,7 +106,7 @@ class WeeklyLWN(BasicNewsRecipe):
section_title = section section_title = section
# Most articles have anchors in their titles, *except* the security vulnerabilities # Most articles have anchors in their titles, *except* the security vulnerabilities
article_anchor = curr.findNext(name = 'a', attrs = { 'href': re.compile('^/Articles/') } ) article_anchor = curr.findNext(name='a', attrs={'href': re.compile('^/Articles/')})
if article_anchor: if article_anchor:
article_url = article_anchor.get('href') article_url = article_anchor.get('href')
@ -134,10 +134,11 @@ class WeeklyLWN(BasicNewsRecipe):
else: else:
print >>sys.stderr, "lwn_weekly.recipe: something bad happened; should not be able to reach this" print >>sys.stderr, "lwn_weekly.recipe: something bad happened; should not be able to reach this"
ans = [(section, articles[section]) for section in ans if section in articles] ans = [(section2, articles[section2]) for section2 in ans if section2 in articles]
#from pprint import pprint # from pprint import pprint
#pprint(ans) # pprint(ans)
return ans return ans
# vim: expandtab:ts=4:sw=4 # vim: expandtab:ts=4:sw=4

View File

@ -1,42 +1,43 @@
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
## History: import re
## 1: Base Version # History:
## 2: Added rules for wdr.de, ndr.de, br-online.de # 1: Base Version
## 3: Added rules for rbb-online.de, boerse.ard.de, sportschau.de # 2: Added rules for wdr.de, ndr.de, br-online.de
## 4: New design of tagesschau.de implemented. Simplyfied. # 3: Added rules for rbb-online.de, boerse.ard.de, sportschau.de
## 5: Taken out the pictures. # 4: New design of tagesschau.de implemented. Simplyfied.
# 5: Taken out the pictures.
class Tagesschau(BasicNewsRecipe): class Tagesschau(BasicNewsRecipe):
title = 'Tagesschau' title = 'Tagesschau'
description = 'Nachrichten der ARD' description = 'Nachrichten der ARD'
publisher = 'ARD' publisher = 'ARD'
language = 'de' language = 'de'
version = 5 version = 5
__author__ = 'Florian Andreas Pfaff, a.peter' __author__ = 'Florian Andreas Pfaff, a.peter'
oldest_article = 7 oldest_article = 7
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = True no_stylesheets = True
remove_javascript = True remove_javascript = True
feeds = [('Tagesschau', 'http://www.tagesschau.de/xml/rss2')] feeds = [('Tagesschau', 'http://www.tagesschau.de/xml/rss2')]
remove_tags = [ remove_tags = [
dict(name='div', attrs={'class':['inline']}), dict(name='div', attrs={'class':['inline']}),
dict(name='div', attrs={'class':re.compile(r'.*linklist.*')}), dict(name='div', attrs={'class':re.compile(r'.*linklist.*')}),
dict(name='div', attrs={'class':re.compile(r'.*media(Left|Right|Top|Bottom|Info).*')}), dict(name='div', attrs={'class':re.compile(r'.*media(Left|Right|Top|Bottom|Info).*')}),
dict(name='div', attrs={'class':re.compile(r'.*mod(Socialbar|ConComments).*')}), dict(name='div', attrs={'class':re.compile(r'.*mod(Socialbar|ConComments).*')}),
dict(name='div', attrs={'class':re.compile(r'.*infokasten.*')}), dict(name='div', attrs={'class':re.compile(r'.*infokasten.*')}),
dict(name='div', attrs={'class':re.compile(r'.*articlePictureA.*')}), dict(name='div', attrs={'class':re.compile(r'.*articlePictureA.*')}),
dict(name='div', attrs={'id': re.compile(r'.*zoomTeaser.*')}), dict(name='div', attrs={'id': re.compile(r'.*zoomTeaser.*')}),
dict(name='h2', attrs={'class':re.compile(r'.*conHeadline.*')}), dict(name='h2', attrs={'class':re.compile(r'.*conHeadline.*')}),
dict(name='ul', attrs={'class':['iconList']}) dict(name='ul', attrs={'class':['iconList']})
] ]
keep_only_tags = [ keep_only_tags = [
dict(name='div', attrs={'class':['section sectionZ sectionArticle']}), dict(name='div', attrs={'class':['section sectionZ sectionArticle']}),
dict(name='div', attrs={'class':re.compile(r'.*containerArticle.*')}) dict(name='div', attrs={'class':re.compile(r'.*containerArticle.*')})
] ]
def get_masthead_url(self): def get_masthead_url(self):
return 'http://intern.tagesschau.de/html/img/image.jpg' return 'http://intern.tagesschau.de/html/img/image.jpg'

View File

@ -114,7 +114,7 @@ class FB2Input(InputFormatPlugin):
break break
opf = OPFCreator(os.getcwdu(), mi) opf = OPFCreator(os.getcwdu(), mi)
entries = [(f, guess_type(f)[0]) for f in os.listdir(u'.')] entries = [(f2, guess_type(f)[0]) for f2 in os.listdir(u'.')]
opf.create_manifest(entries) opf.create_manifest(entries)
opf.create_spine([u'index.xhtml']) opf.create_spine([u'index.xhtml'])
if cpath: if cpath:
@ -127,7 +127,7 @@ class FB2Input(InputFormatPlugin):
from calibre.ebooks.fb2 import base64_decode from calibre.ebooks.fb2 import base64_decode
self.binary_map = {} self.binary_map = {}
for elem in doc.xpath('./*'): for elem in doc.xpath('./*'):
if elem.text and 'binary' in elem.tag and elem.attrib.has_key('id'): if elem.text and 'binary' in elem.tag and 'id' in elem.attrib:
ct = elem.get('content-type', '') ct = elem.get('content-type', '')
fname = elem.attrib['id'] fname = elem.attrib['id']
ext = ct.rpartition('/')[-1].lower() ext = ct.rpartition('/')[-1].lower()
@ -146,3 +146,4 @@ class FB2Input(InputFormatPlugin):
with open(fname, 'wb') as f: with open(fname, 'wb') as f:
f.write(data) f.write(data)

View File

@ -15,7 +15,7 @@ from collections import OrderedDict, defaultdict
from calibre.ebooks.mobi.utils import (encint, encode_number_as_hex, from calibre.ebooks.mobi.utils import (encint, encode_number_as_hex,
encode_tbs, align_block, RECORD_SIZE, CNCX as CNCX_) encode_tbs, align_block, RECORD_SIZE, CNCX as CNCX_)
class CNCX(CNCX_): # {{{ class CNCX(CNCX_): # {{{
def __init__(self, toc, is_periodical): def __init__(self, toc, is_periodical):
strings = [] strings = []
@ -30,7 +30,7 @@ class CNCX(CNCX_): # {{{
CNCX_.__init__(self, strings) CNCX_.__init__(self, strings)
# }}} # }}}
class TAGX(object): # {{{ class TAGX(object): # {{{
BITMASKS = {11:0b1} BITMASKS = {11:0b1}
BITMASKS.update({x:(1 << i) for i, x in enumerate([1, 2, 3, 4, 5, 21, 22, 23])}) BITMASKS.update({x:(1 << i) for i, x in enumerate([1, 2, 3, 4, 5, 21, 22, 23])})
@ -105,8 +105,7 @@ class IndexEntry(object):
'author_offset': 71, 'author_offset': 71,
} }
RTAG_MAP = {v:k for k, v in TAG_VALUES.iteritems()} RTAG_MAP = {v:k for k, v in TAG_VALUES.iteritems()} # noqa
def __init__(self, offset, label_offset): def __init__(self, offset, label_offset):
self.offset, self.label_offset = offset, label_offset self.offset, self.label_offset = offset, label_offset
@ -131,8 +130,10 @@ class IndexEntry(object):
@dynamic_property @dynamic_property
def size(self): def size(self):
def fget(self): return self.length def fget(self):
def fset(self, val): self.length = val return self.length
def fset(self, val):
self.length = val
return property(fget=fget, fset=fset, doc='Alias for length') return property(fget=fget, fset=fset, doc='Alias for length')
@property @property
@ -237,7 +238,7 @@ class SecondaryIndexEntry(IndexEntry):
# }}} # }}}
class TBS(object): # {{{ class TBS(object): # {{{
''' '''
Take the list of index nodes starting/ending on a record and calculate the Take the list of index nodes starting/ending on a record and calculate the
@ -383,7 +384,6 @@ class TBS(object): # {{{
buf.write(encode_tbs(last_article.index-next_sec.index, buf.write(encode_tbs(last_article.index-next_sec.index,
{0b1000: 0})) {0b1000: 0}))
# If a section TOC starts and extends into the next record add # If a section TOC starts and extends into the next record add
# a trailing vwi. We detect this by TBS type==3, processing last # a trailing vwi. We detect this by TBS type==3, processing last
# section present in the record, and the last article in that # section present in the record, and the last article in that
@ -424,7 +424,7 @@ class TBS(object): # {{{
# }}} # }}}
class Indexer(object): # {{{ class Indexer(object): # {{{
def __init__(self, serializer, number_of_text_records, def __init__(self, serializer, number_of_text_records,
size_of_last_text_record, masthead_offset, is_periodical, size_of_last_text_record, masthead_offset, is_periodical,
@ -460,8 +460,10 @@ class Indexer(object): # {{{
for node in oeb.toc.iterdescendants(): for node in oeb.toc.iterdescendants():
if node.klass == 'article': if node.klass == 'article':
aut, desc = node.author, node.description aut, desc = node.author, node.description
if not aut: aut = _('Unknown') if not aut:
if not desc: desc = _('No details available') aut = _('Unknown')
if not desc:
desc = _('No details available')
node.author, node.description = aut, desc node.author, node.description = aut, desc
self.cncx = CNCX(oeb.toc, self.is_periodical) self.cncx = CNCX(oeb.toc, self.is_periodical)
@ -485,7 +487,7 @@ class Indexer(object): # {{{
self.calculate_trailing_byte_sequences() self.calculate_trailing_byte_sequences()
def create_index_record(self, secondary=False): # {{{ def create_index_record(self, secondary=False): # {{{
header_length = 192 header_length = 192
buf = StringIO() buf = StringIO()
indices = list(SecondaryIndexEntry.entries()) if secondary else self.indices indices = list(SecondaryIndexEntry.entries()) if secondary else self.indices
@ -509,9 +511,9 @@ class Indexer(object): # {{{
header = b'INDX' header = b'INDX'
buf.seek(0), buf.truncate(0) buf.seek(0), buf.truncate(0)
buf.write(pack(b'>I', header_length)) buf.write(pack(b'>I', header_length))
buf.write(b'\0'*4) # Unknown buf.write(b'\0'*4) # Unknown
buf.write(pack(b'>I', 1)) # Header type? Or index record number? buf.write(pack(b'>I', 1)) # Header type? Or index record number?
buf.write(b'\0'*4) # Unknown buf.write(b'\0'*4) # Unknown
# IDXT block offset # IDXT block offset
buf.write(pack(b'>I', header_length + len(index_block))) buf.write(pack(b'>I', header_length + len(index_block)))
# Number of index entries # Number of index entries
@ -529,7 +531,7 @@ class Indexer(object): # {{{
return ans return ans
# }}} # }}}
def create_header(self, secondary=False): # {{{ def create_header(self, secondary=False): # {{{
buf = StringIO() buf = StringIO()
if secondary: if secondary:
tagx_block = TAGX().secondary tagx_block = TAGX().secondary
@ -551,13 +553,13 @@ class Indexer(object): # {{{
buf.write(pack(b'>I', 2)) buf.write(pack(b'>I', 2))
# IDXT offset 20-24 # IDXT offset 20-24
buf.write(pack(b'>I', 0)) # Filled in later buf.write(pack(b'>I', 0)) # Filled in later
# Number of index records 24-28 # Number of index records 24-28
buf.write(pack(b'>I', 1 if secondary else len(self.records))) buf.write(pack(b'>I', 1 if secondary else len(self.records)))
# Index Encoding 28-32 # Index Encoding 28-32
buf.write(pack(b'>I', 65001)) # utf-8 buf.write(pack(b'>I', 65001)) # utf-8
# Unknown 32-36 # Unknown 32-36
buf.write(b'\xff'*4) buf.write(b'\xff'*4)
@ -620,7 +622,7 @@ class Indexer(object): # {{{
return align_block(buf.getvalue()) return align_block(buf.getvalue())
# }}} # }}}
def create_book_index(self): # {{{ def create_book_index(self): # {{{
indices = [] indices = []
seen = set() seen = set()
id_offsets = self.serializer.id_offsets id_offsets = self.serializer.id_offsets
@ -652,7 +654,6 @@ class Indexer(object): # {{{
next_offset = self.serializer.body_end_offset next_offset = self.serializer.body_end_offset
index.length = next_offset - index.offset index.length = next_offset - index.offset
# Remove empty indices # Remove empty indices
indices = [x for x in indices if x.length > 0] indices = [x for x in indices if x.length > 0]
@ -672,7 +673,7 @@ class Indexer(object): # {{{
# }}} # }}}
def create_periodical_index(self): # {{{ def create_periodical_index(self): # {{{
periodical_node = iter(self.oeb.toc).next() periodical_node = iter(self.oeb.toc).next()
periodical_node_offset = self.serializer.body_start_offset periodical_node_offset = self.serializer.body_start_offset
periodical_node_size = (self.serializer.body_end_offset - periodical_node_size = (self.serializer.body_end_offset -
@ -727,7 +728,7 @@ class Indexer(object): # {{{
if ii > -1: if ii > -1:
article.image_index = ii article.image_index = ii
except KeyError: except KeyError:
pass # Image not found in serializer pass # Image not found in serializer
if normalized_articles: if normalized_articles:
normalized_articles.sort(key=lambda x:x.offset) normalized_articles.sort(key=lambda x:x.offset)
@ -885,3 +886,4 @@ class Indexer(object): # {{{
# }}} # }}}

View File

@ -64,7 +64,8 @@ class Extract(ODF2XHTML):
f.write(css.encode('utf-8')) f.write(css.encode('utf-8'))
def get_css_for_class(self, cls): def get_css_for_class(self, cls):
if not cls: return None if not cls:
return None
for rule in self.css.cssRules.rulesOfType(CSSRule.STYLE_RULE): for rule in self.css.cssRules.rulesOfType(CSSRule.STYLE_RULE):
for sel in rule.selectorList: for sel in rule.selectorList:
q = sel.selectorText q = sel.selectorText
@ -94,7 +95,7 @@ class Extract(ODF2XHTML):
style = div.attrib.get('style', '') style = div.attrib.get('style', '')
if style and not style.endswith(';'): if style and not style.endswith(';'):
style = style + ';' style = style + ';'
style += 'position:static' # Ensures position of containing style += 'position:static' # Ensures position of containing
# div is static # div is static
# Ensure that the img is always contained in its frame # Ensure that the img is always contained in its frame
div.attrib['style'] = style div.attrib['style'] = style
@ -112,7 +113,8 @@ class Extract(ODF2XHTML):
for img in imgpath(root): for img in imgpath(root):
div2 = img.getparent() div2 = img.getparent()
div1 = div2.getparent() div1 = div2.getparent()
if (len(div1), len(div2)) != (1, 1): continue if (len(div1), len(div2)) != (1, 1):
continue
cls = div1.get('class', '') cls = div1.get('class', '')
first_rules = filter(None, [self.get_css_for_class(x) for x in first_rules = filter(None, [self.get_css_for_class(x) for x in
cls.split()]) cls.split()])
@ -151,7 +153,6 @@ class Extract(ODF2XHTML):
style = div2.attrib['style'] style = div2.attrib['style']
div2.attrib['style'] = 'display:inline;'+style div2.attrib['style'] = 'display:inline;'+style
def filter_css(self, root, log): def filter_css(self, root, log):
style = root.xpath('//*[local-name() = "style" and @type="text/css"]') style = root.xpath('//*[local-name() = "style" and @type="text/css"]')
if style: if style:
@ -236,7 +237,7 @@ class Extract(ODF2XHTML):
self.lines = [] self.lines = []
self._wfunc = self._wlines self._wfunc = self._wlines
if isinstance(odffile, basestring) \ if isinstance(odffile, basestring) \
or hasattr(odffile, 'read'): # Added by Kovid or hasattr(odffile, 'read'): # Added by Kovid
self.document = odLoad(odffile) self.document = odLoad(odffile)
else: else:
self.document = odffile self.document = odffile
@ -282,7 +283,7 @@ class Extract(ODF2XHTML):
zf = ZipFile(stream, 'r') zf = ZipFile(stream, 'r')
self.extract_pictures(zf) self.extract_pictures(zf)
opf = OPFCreator(os.path.abspath(os.getcwdu()), mi) opf = OPFCreator(os.path.abspath(os.getcwdu()), mi)
opf.create_manifest([(os.path.abspath(f), None) for f in opf.create_manifest([(os.path.abspath(f2), None) for f2 in
walk(os.getcwdu())]) walk(os.getcwdu())])
opf.create_spine([os.path.abspath('index.xhtml')]) opf.create_spine([os.path.abspath('index.xhtml')])
with open('metadata.opf', 'wb') as f: with open('metadata.opf', 'wb') as f:
@ -291,3 +292,4 @@ class Extract(ODF2XHTML):

View File

@ -18,7 +18,7 @@ from calibre import prints, force_unicode, as_unicode
single_shot = partial(QTimer.singleShot, 75) single_shot = partial(QTimer.singleShot, 75)
class DuplicatesAdder(QObject): # {{{ class DuplicatesAdder(QObject): # {{{
added = pyqtSignal(object) added = pyqtSignal(object)
adding_done = pyqtSignal() adding_done = pyqtSignal()
@ -49,7 +49,7 @@ class DuplicatesAdder(QObject): # {{{
# }}} # }}}
class RecursiveFind(QThread): # {{{ class RecursiveFind(QThread): # {{{
update = pyqtSignal(object) update = pyqtSignal(object)
found = pyqtSignal(object) found = pyqtSignal(object)
@ -95,7 +95,7 @@ class RecursiveFind(QThread): # {{{
# }}} # }}}
class DBAdder(QObject): # {{{ class DBAdder(QObject): # {{{
def __init__(self, parent, db, ids, nmap): def __init__(self, parent, db, ids, nmap):
QObject.__init__(self, parent) QObject.__init__(self, parent)
@ -185,8 +185,8 @@ class DBAdder(QObject): # {{{
with open(cover, 'rb') as f: with open(cover, 'rb') as f:
cover = f.read() cover = f.read()
orig_formats = formats orig_formats = formats
formats = [f for f in formats if not f.lower().endswith('.opf')] formats = [f2 for f2 in formats if not f2.lower().endswith('.opf')]
if prefs['add_formats_to_existing']: #automerge is on if prefs['add_formats_to_existing']: # automerge is on
identical_book_list = self.db.find_identical_books(mi) identical_book_list = self.db.find_identical_books(mi)
if identical_book_list: # books with same author and nearly same title exist in db if identical_book_list: # books with same author and nearly same title exist in db
self.merged_books.add(mi.title) self.merged_books.add(mi.title)
@ -221,7 +221,7 @@ class DBAdder(QObject): # {{{
self.number_of_books_added += 1 self.number_of_books_added += 1
self.add_formats(id_, formats) self.add_formats(id_, formats)
else: #automerge is off else: # automerge is off
id_ = self.db.create_book_entry(mi, cover=cover, add_duplicates=False) id_ = self.db.create_book_entry(mi, cover=cover, add_duplicates=False)
if id_ is None: if id_ is None:
self.duplicates.append((mi, cover, orig_formats)) self.duplicates.append((mi, cover, orig_formats))
@ -244,9 +244,9 @@ class DBAdder(QObject): # {{{
# }}} # }}}
class Adder(QObject): # {{{ class Adder(QObject): # {{{
ADD_TIMEOUT = 900 # seconds (15 minutes) ADD_TIMEOUT = 900 # seconds (15 minutes)
def __init__(self, parent, db, callback, spare_server=None): def __init__(self, parent, db, callback, spare_server=None):
QObject.__init__(self, parent) QObject.__init__(self, parent)
@ -385,7 +385,6 @@ class Adder(QObject): # {{{
if self.continue_updating: if self.continue_updating:
single_shot(self.update) single_shot(self.update)
def process_duplicates(self): def process_duplicates(self):
duplicates = self.db_adder.duplicates duplicates = self.db_adder.duplicates
if not duplicates: if not duplicates:
@ -427,7 +426,6 @@ class Adder(QObject): # {{{
del self.db_adder del self.db_adder
self.db_adder = None self.db_adder = None
@property @property
def number_of_books_added(self): def number_of_books_added(self):
return getattr(getattr(self, 'db_adder', None), 'number_of_books_added', return getattr(getattr(self, 'db_adder', None), 'number_of_books_added',
@ -459,7 +457,7 @@ class Adder(QObject): # {{{
# }}} # }}}
class Saver(QObject): # {{{ class Saver(QObject): # {{{
def __init__(self, parent, db, callback, rows, path, opts, def __init__(self, parent, db, callback, rows, path, opts,
spare_server=None): spare_server=None):
@ -489,7 +487,6 @@ class Saver(QObject): # {{{
self.continue_updating = True self.continue_updating = True
single_shot(self.update) single_shot(self.update)
def canceled(self): def canceled(self):
self.continue_updating = False self.continue_updating = False
if self.worker is not None: if self.worker is not None:
@ -499,7 +496,6 @@ class Saver(QObject): # {{{
self.callback(self.worker.path, self.failures, self.worker.error) self.callback(self.worker.path, self.failures, self.worker.error)
self.callback_called = True self.callback_called = True
def update(self): def update(self):
if not self.continue_updating: if not self.continue_updating:
return return
@ -523,7 +519,7 @@ class Saver(QObject): # {{{
# Give the worker time to clean up and set worker.error # Give the worker time to clean up and set worker.error
self.worker.join(2) self.worker.join(2)
except: except:
pass # The worker was not yet started pass # The worker was not yet started
self.callback_called = True self.callback_called = True
self.callback(self.worker.path, self.failures, self.worker.error) self.callback(self.worker.path, self.failures, self.worker.error)
@ -531,7 +527,6 @@ class Saver(QObject): # {{{
self.get_result() self.get_result()
single_shot(self.update) single_shot(self.update)
def get_result(self): def get_result(self):
try: try:
id, title, ok, tb = self.rq.get_nowait() id, title, ok, tb = self.rq.get_nowait()
@ -549,3 +544,4 @@ class Saver(QObject): # {{{
self.failures.add((title, tb)) self.failures.add((title, tb))
# }}} # }}}

View File

@ -22,7 +22,7 @@ from calibre.gui2 import error_dialog
IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp'] IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp']
class Worker(Thread): # {{{ class Worker(Thread): # {{{
def __init__(self, url, fpath, rq): def __init__(self, url, fpath, rq):
Thread.__init__(self) Thread.__init__(self)
@ -44,7 +44,7 @@ class Worker(Thread): # {{{
self.rq.put((a, b, c)) self.rq.put((a, b, c))
# }}} # }}}
class DownloadDialog(QDialog): # {{{ class DownloadDialog(QDialog): # {{{
def __init__(self, url, fname, parent): def __init__(self, url, fname, parent):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
@ -248,7 +248,7 @@ def dnd_get_files(md, exts):
purls = [urlparse(u) for u in urls] purls = [urlparse(u) for u in urls]
# First look for a local file # First look for a local file
local_files = [u2p(x) for x in purls if x.scheme in ('', 'file')] local_files = [u2p(x) for x in purls if x.scheme in ('', 'file')]
local_files = [ p for p in local_files if local_files = [p for p in local_files if
posixpath.splitext(urllib.unquote(p))[1][1:].lower() in posixpath.splitext(urllib.unquote(p))[1][1:].lower() in
exts] exts]
local_files = [x for x in local_files if os.path.exists(x)] local_files = [x for x in local_files if os.path.exists(x)]
@ -266,7 +266,7 @@ def dnd_get_files(md, exts):
remote_urls = [x for x in purls if x.scheme in ('http', 'https', remote_urls = [x for x in purls if x.scheme in ('http', 'https',
'ftp') and posixpath.splitext(x.path)[1][1:].lower() in exts] 'ftp') and posixpath.splitext(x.path)[1][1:].lower() in exts]
if remote_urls: if remote_urls:
filenames = [posixpath.basename(urllib.unquote(rurl.path)) for rurl in filenames = [posixpath.basename(urllib.unquote(rurl2.path)) for rurl2 in
remote_urls] remote_urls]
return [urlunparse(x) for x in remote_urls], filenames return [urlunparse(x) for x in remote_urls], filenames
@ -336,3 +336,4 @@ def get_firefox_rurl(md, exts):
def has_firefox_ext(md, exts): def has_firefox_ext(md, exts):
return bool(get_firefox_rurl(md, exts)[0]) return bool(get_firefox_rurl(md, exts)[0])

View File

@ -77,35 +77,35 @@ utf8enc2latex_mapping = {
# The extraction has been done by the "create_unimap.py" script # The extraction has been done by the "create_unimap.py" script
# located at <http://docutils.sf.net/tools/dev/create_unimap.py>. # located at <http://docutils.sf.net/tools/dev/create_unimap.py>.
#Fix some encoding problem between cp1252 and latin1 # Fix some encoding problem between cp1252 and latin1
# from http://www.microsoft.com/typography/unicode/1252.htm # from http://www.microsoft.com/typography/unicode/1252.htm
u'\x80': '{\\texteuro}', # EURO SIGN u'\x80': '{\\texteuro}', # EURO SIGN
u'\x82': '{,}', # SINGLE LOW-9 QUOTATION MARK u'\x82': '{,}', # SINGLE LOW-9 QUOTATION MARK
u'\x83': '$f$', # LATIN SMALL LETTER F WITH HOOK u'\x83': '$f$', # LATIN SMALL LETTER F WITH HOOK
u'\x84': '{,,}', # DOUBLE LOW-9 QUOTATION MARK u'\x84': '{,,}', # DOUBLE LOW-9 QUOTATION MARK
u'\x85': '{\\ldots}', # HORIZONTAL ELLIPSIS u'\x85': '{\\ldots}', # HORIZONTAL ELLIPSIS
u'\x86': '{\\textdagger}', # DAGGER u'\x86': '{\\textdagger}', # DAGGER
u'\x87': '{\\textdaggerdbl}', # DOUBLE DAGGER u'\x87': '{\\textdaggerdbl}', # DOUBLE DAGGER
u'\x88': '{\textasciicircum}', # MODIFIER LETTER CIRCUMFLEX ACCENT u'\x88': '{\textasciicircum}', # MODIFIER LETTER CIRCUMFLEX ACCENT
u'\x89': '{\\textperthousand}', # PER MILLE SIGN u'\x89': '{\\textperthousand}', # PER MILLE SIGN
u'\x8A': '{\\v{S}}', # LATIN CAPITAL LETTER S WITH CARON u'\x8A': '{\\v{S}}', # LATIN CAPITAL LETTER S WITH CARON
u'\x8B': '{\\guilsinglleft}', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK u'\x8B': '{\\guilsinglleft}', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK
u'\x8C': '{\\OE}', # LATIN CAPITAL LIGATURE OE u'\x8C': '{\\OE}', # LATIN CAPITAL LIGATURE OE
u'\x8E': '{\\v{Z}}', # LATIN CAPITAL LETTER Z WITH CARON u'\x8E': '{\\v{Z}}', # LATIN CAPITAL LETTER Z WITH CARON
u'\x91': '{`}', # LEFT SINGLE QUOTATION MARK u'\x91': '{`}', # LEFT SINGLE QUOTATION MARK
u'\x92': "{'}", # RIGHT SINGLE QUOTATION MARK u'\x92': "{'}", # RIGHT SINGLE QUOTATION MARK
u'\x93': '{\\textquotedblleft}', # LEFT DOUBLE QUOTATION MARK u'\x93': '{\\textquotedblleft}', # LEFT DOUBLE QUOTATION MARK
u'\x94': '{\\textquotedblright}', # RIGHT DOUBLE QUOTATION MARK u'\x94': '{\\textquotedblright}', # RIGHT DOUBLE QUOTATION MARK
u'\x95': '{\\textbullet}', # BULLET u'\x95': '{\\textbullet}', # BULLET
u'\x96': '{\\textendash}', # EN DASH u'\x96': '{\\textendash}', # EN DASH
u'\x97': '{\\textemdash}', # EM DASH u'\x97': '{\\textemdash}', # EM DASH
u'\x98': '{\\texttildelow}', # SMALL TILDE u'\x98': '{\\texttildelow}', # SMALL TILDE
u'\x99': '{\\texttrademark}', # TRADE MARK SIGN u'\x99': '{\\texttrademark}', # TRADE MARK SIGN
u'\x9A': '{\\v{s}}', # LATIN SMALL LETTER S WITH CARON u'\x9A': '{\\v{s}}', # LATIN SMALL LETTER S WITH CARON
u'\x9B': '{\\guilsinglright}', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK u'\x9B': '{\\guilsinglright}', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
u'\x9C': '{\\oe}', # LATIN SMALL LIGATURE OE u'\x9C': '{\\oe}', # LATIN SMALL LIGATURE OE
u'\x9E': '{\\v{z}}', # LATIN SMALL LETTER Z WITH CARON u'\x9E': '{\\v{z}}', # LATIN SMALL LETTER Z WITH CARON
u'\x9F': '{\\"{Y}}', # LATIN CAPITAL LETTER Y WITH DIAERESIS u'\x9F': '{\\"{Y}}', # LATIN CAPITAL LETTER Y WITH DIAERESIS
u'\xa0': '$~$', u'\xa0': '$~$',
u'\xa1': '{\\textexclamdown}', u'\xa1': '{\\textexclamdown}',
@ -2465,7 +2465,7 @@ utf8enc2latex_mapping = {
u'\U0001d7fe': '$\\mathtt{8}$', u'\U0001d7fe': '$\\mathtt{8}$',
u'\U0001d7ff': '$\\mathtt{9}$', u'\U0001d7ff': '$\\mathtt{9}$',
#Items from simple list # Items from simple list
u'\u0106': "{\\a\\'C}", u'\u0106': "{\\a\\'C}",
u'\u0408': '{\\CYRJE}', u'\u0408': '{\\CYRJE}',
u'\u20ac': '{\\texteuro}', u'\u20ac': '{\\texteuro}',
@ -2796,45 +2796,45 @@ utf8enc2latex_mapping = {
u'\u016f': '{\\r u}', u'\u016f': '{\\r u}',
u'\xfa': "{\\'u}" u'\xfa': "{\\'u}"
#Items to add at a latter date (check first) # Items to add at a latter date (check first)
#u'\u0000': r'{$\alpha$}', # u'\u0000': r'{$\alpha$}',
#u'\u0000': r'{$\beta$}', # u'\u0000': r'{$\beta$}',
#u'\u0000': r'{$\gamma$}', # u'\u0000': r'{$\gamma$}',
#u'\u0000': r'{$\delta$}', # u'\u0000': r'{$\delta$}',
#u'\u0000': r'{$\epsilon$}', # u'\u0000': r'{$\epsilon$}',
#u'\u0000': r'{$\varepsilon$}', # u'\u0000': r'{$\varepsilon$}',
#u'\u0000': r'{$\zeta$}', # u'\u0000': r'{$\zeta$}',
#u'\u0000': r'{$\eta$}', # u'\u0000': r'{$\eta$}',
#u'\u0000': r'{$\theta$}', # u'\u0000': r'{$\theta$}',
#u'\u0000': r'{$\vartheta$}', # u'\u0000': r'{$\vartheta$}',
#u'\u0000': r'{$\iota$}', # u'\u0000': r'{$\iota$}',
#u'\u0000': r'{$\kappa$}', # u'\u0000': r'{$\kappa$}',
#u'\u0000': r'{$\lambda$}', # u'\u0000': r'{$\lambda$}',
#u'\u0000': r'{$\mu$}', # u'\u0000': r'{$\mu$}',
#u'\u0000': r'{$\xi$}', # u'\u0000': r'{$\xi$}',
#u'\u0000': r'{$\pi$}', # u'\u0000': r'{$\pi$}',
#u'\u0000': r'{$\varpi$}', # u'\u0000': r'{$\varpi$}',
#u'\u0000': r'{$\rho$}', # u'\u0000': r'{$\rho$}',
#u'\u0000': r'{$\varrho$}', # u'\u0000': r'{$\varrho$}',
#u'\u0000': r'{$\sigma$}', # u'\u0000': r'{$\sigma$}',
#u'\u0000': r'{$\varsigma$}', # u'\u0000': r'{$\varsigma$}',
#u'\u0000': r'{$\tau$}', # u'\u0000': r'{$\tau$}',
#u'\u0000': r'{$\upsilon$}', # u'\u0000': r'{$\upsilon$}',
#u'\u0000': r'{$\phi$}', # u'\u0000': r'{$\phi$}',
#u'\u0000': r'{$\varphi$}', # u'\u0000': r'{$\varphi$}',
#u'\u0000': r'{$\psi$}', # u'\u0000': r'{$\psi$}',
#u'\u0000': r'{$\omega$}', # u'\u0000': r'{$\omega$}',
#u'\u0000': r'{$\Gamma$}', # u'\u0000': r'{$\Gamma$}',
#u'\u0000': r'{$\Delta$}', # u'\u0000': r'{$\Delta$}',
#u'\u0000': r'{$\Theta$}', # u'\u0000': r'{$\Theta$}',
#u'\u0000': r'{$\Lambda$}', # u'\u0000': r'{$\Lambda$}',
#u'\u0000': r'{$\Xi$}', # u'\u0000': r'{$\Xi$}',
#u'\u0000': r'{$\Pi$}', # u'\u0000': r'{$\Pi$}',
#u'\u0000': r'{$\Sigma$}', # u'\u0000': r'{$\Sigma$}',
#u'\u0000': r'{$\Upsilon$}', # u'\u0000': r'{$\Upsilon$}',
#u'\u0000': r'{$\Phi$}', # u'\u0000': r'{$\Phi$}',
#u'\u0000': r'{$\Psi$}', # u'\u0000': r'{$\Psi$}',
#u'\u0000': r'{$\Omega$}', # u'\u0000': r'{$\Omega$}',
} }
entity_mapping = { entity_mapping = {
@ -2847,7 +2847,7 @@ class BibTeX:
def __init__(self): def __init__(self):
self.rep_utf8 = MReplace(utf8enc2latex_mapping) self.rep_utf8 = MReplace(utf8enc2latex_mapping)
self.rep_ent = MReplace(entity_mapping) self.rep_ent = MReplace(entity_mapping)
#Set default conversion to ASCII BibTeX # Set default conversion to ASCII BibTeX
self.ascii_bibtex = True self.ascii_bibtex = True
# This substitution is based on the description of cite key restrictions at # This substitution is based on the description of cite key restrictions at
# http://bibdesk.sourceforge.net/manual/BibDesk%20Help_2.html # http://bibdesk.sourceforge.net/manual/BibDesk%20Help_2.html
@ -2859,27 +2859,23 @@ class BibTeX:
def ValidateCitationKey(self, text): def ValidateCitationKey(self, text):
""" """
removes characters not allowed in BibTeX keys removes characters not allowed in BibTeX keys
>>> ValidateCitationKey(DummyEntry('my@id'))
'myid'
""" """
return self.invalid_cit.sub(u'', text) return self.invalid_cit.sub(u'', text)
def braceUppercase(self, text): def braceUppercase(self, text):
""" Convert uppercase letters to bibtex encoded uppercase """ Convert uppercase letters to bibtex encoded uppercase
>>> braceUppercase('Foo Bar')
'{F}oo {B}ar'
""" """
return self.upper.sub(lambda m: u'{%s}' % m.group(), text) return self.upper.sub(lambda m: u'{%s}' % m.group(), text)
def resolveEntities(self, text): def resolveEntities(self, text):
#for entity, entity_map in entity_mapping.iteritems(): # for entity, entity_map in entity_mapping.iteritems():
# text = text.replace(entity, entity_map) # text = text.replace(entity, entity_map)
#return text # return text
return self.rep_ent.mreplace(text) return self.rep_ent.mreplace(text)
def resolveUnicode(self, text): def resolveUnicode(self, text):
#UTF-8 text as entry # UTF-8 text as entry
#for unichar, latexenc in utf8enc2latex_mapping.iteritems() : # for unichar, latexenc in utf8enc2latex_mapping.iteritems() :
# text = text.replace(unichar, latexenc) # text = text.replace(unichar, latexenc)
text = self.rep_utf8.mreplace(text) text = self.rep_utf8.mreplace(text)
return text.replace(u'$}{$', u'') return text.replace(u'$}{$', u'')
@ -2891,9 +2887,9 @@ class BibTeX:
text.replace('\\', '\\\\') text.replace('\\', '\\\\')
return self.escape.sub(lambda m: u'\\%s' % m.group(), text) return self.escape.sub(lambda m: u'\\%s' % m.group(), text)
#Calibre functions # Calibre functions
#Option to go to official ASCII Bibtex or unofficial UTF-8 # Option to go to official ASCII Bibtex or unofficial UTF-8
#Go from an unicode entry to ASCII Bibtex format without encoding # Go from an unicode entry to ASCII Bibtex format without encoding
def utf8ToBibtex(self, text): def utf8ToBibtex(self, text):
if len(text) == 0: if len(text) == 0:
return '' return ''
@ -2904,5 +2900,6 @@ class BibTeX:
return self.escapeSpecialCharacters(text) return self.escapeSpecialCharacters(text)
def bibtex_author_format(self, item): def bibtex_author_format(self, item):
#Format authors for Bibtex compliance (get a list as input) # Format authors for Bibtex compliance (get a list as input)
return self.utf8ToBibtex(u' and '.join([author for author in item])) return self.utf8ToBibtex(u' and '.join([author for author in item]))