diff --git a/resources/images/news/aif_ru.png b/resources/images/news/aif_ru.png new file mode 100644 index 0000000000..57d98f3832 Binary files /dev/null and b/resources/images/news/aif_ru.png differ diff --git a/resources/images/news/izvestia.png b/resources/images/news/izvestia.png new file mode 100644 index 0000000000..d80a2a06f0 Binary files /dev/null and b/resources/images/news/izvestia.png differ diff --git a/resources/images/news/kommersant.png b/resources/images/news/kommersant.png new file mode 100644 index 0000000000..f9b76536dd Binary files /dev/null and b/resources/images/news/kommersant.png differ diff --git a/resources/images/news/ria_ru.png b/resources/images/news/ria_ru.png new file mode 100644 index 0000000000..069c37f72e Binary files /dev/null and b/resources/images/news/ria_ru.png differ diff --git a/resources/recipes/aif_ru.recipe b/resources/recipes/aif_ru.recipe new file mode 100644 index 0000000000..b5d6015d0c --- /dev/null +++ b/resources/recipes/aif_ru.recipe @@ -0,0 +1,31 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +www.aif.ru +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class AIF_ru(BasicNewsRecipe): + title = 'Arguments & Facts - Russian' + __author__ = 'Darko Miletic' + description = 'News from Russia' + publisher = 'AIF' + category = 'news, politics, Russia' + oldest_article = 2 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'cp1251' + language = 'ru' + publication_type = 'magazine' + extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Verdana,Arial,Helvetica,sans1,sans-serif} ' + keep_only_tags = [dict(name='div',attrs={'id':'inner'})] + remove_tags = [ + dict(name=['iframe','object','link','base','input','img']) + ,dict(name='div',attrs={'class':'photo'}) + ,dict(name='p',attrs={'class':'resizefont'}) + ] + + feeds = [(u'News', u'http://www.aif.ru/rss/all.php')] + diff --git a/resources/recipes/izvestia.recipe b/resources/recipes/izvestia.recipe new file mode 100644 index 0000000000..7d04ab7b07 --- /dev/null +++ b/resources/recipes/izvestia.recipe @@ -0,0 +1,28 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +izvestia.ru +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class Izvestia(BasicNewsRecipe): + title = 'Izvestia' + __author__ = 'Darko Miletic' + description = 'News from Russia' + publisher = 'Izvestia' + category = 'news, politics, Russia' + oldest_article = 5 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'cp1251' + language = 'ru' + publication_type = 'newspaper' + masthead_url = 'http://images.izvestia.ru/izv/sys/logo.gif' + extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Verdana,Arial,Helvetica,sans1,sans-serif} ' + keep_only_tags = [dict(name='div', attrs={'class':'newsFull'})] + remove_tags = [dict(name=['iframe','object','img','link','base'])] + remove_tags_before = dict(name='h1', attrs={'class':'statya'}) + + feeds = [(u'Daily edition', u'http://rss.feedsportal.com/c/32171/f/424076/index.rss')] diff --git a/resources/recipes/kommersant.recipe b/resources/recipes/kommersant.recipe new file mode 100644 index 0000000000..f24a5da909 --- /dev/null +++ b/resources/recipes/kommersant.recipe @@ -0,0 +1,42 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +www.kommersant.ru +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class Kommersant_ru(BasicNewsRecipe): + title = 'Kommersant' + __author__ = 'Darko Miletic' + description = 'News from Russia' + publisher = 'Kommersant' + category = 'news, politics, Russia' + oldest_article = 5 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'cp1251' + language = 'ru' + publication_type = 'newspaper' + masthead_url = 'http://www.kommersant.ru/CorpPics/logo_daily_1.gif' + extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Arial, sans1, sans-serif} span#ctl00_ContentPlaceHolderStyle_LabelSubTitle{margin-bottom: 1em; display: block} .author{margin-bottom: 1em; display: block} .paragraph{margin-bottom: 1em; display: block} .vvodka{font-weight: bold; margin-bottom: 1em} ' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + + keep_only_tags = [ + dict(attrs={'id':'ctl00_ContentPlaceHolderStyle_PanelHeader'}) + ,dict(attrs={'class':['vvodka','paragraph','author']}) + ] + remove_tags = [dict(name=['iframe','object','link','img','base'])] + + feeds = [(u'Articles', u'http://feeds.kommersant.ru/RSS_Export/RU/daily.xml')] + + def print_version(self, url): + return url.replace('doc-rss.aspx','doc.aspx') + '&print=true' + diff --git a/resources/recipes/ria_ru.recipe b/resources/recipes/ria_ru.recipe new file mode 100644 index 0000000000..ad01b02db0 --- /dev/null +++ b/resources/recipes/ria_ru.recipe @@ -0,0 +1,43 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +www.rian.ru +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class RIANovosti(BasicNewsRecipe): + title = 'RIA Novosti - Russian' + __author__ = 'Darko Miletic' + description = 'News from Russia' + publisher = 'RIA' + category = 'news, politics, Russia' + oldest_article = 2 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'utf8' + language = 'ru' + publication_type = 'newsportal' + masthead_url = 'http://img.beta.rian.ru/images/22868/43/228684314.jpg' + extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Arial,Helvetica,sans1,sans-serif} ' + remove_tags_before = dict(name='h1') + remove_tags_after = dict(name='div', attrs={'class':'text'}) + remove_tags = [dict(name=['iframe','object','link','img','base'])] + + feeds = [ + (u'Frontpage', u'http://www.rian.ru/export/rss2/lenta/index.xml') + ,(u'Politics', u'http://www.rian.ru/export/rss2/politics/index.xml') + ,(u'World', u'http://www.rian.ru/export/rss2/world/index.xml') + ,(u'Economy', u'http://www.rian.ru/export/rss2/economy/index.xml') + ,(u'Society', u'http://www.rian.ru/export/rss2/society/index.xml') + ,(u'Moscow', u'http://www.rian.ru/export/rss2/moscow/index.xml') + ,(u'Defense', u'http://www.rian.ru/export/rss2/defense_safety/index.xml') + ,(u'Science', u'http://www.rian.ru/export/rss2/science/index.xml') + ,(u'Turism', u'http://www.rian.ru/export/rss2/tourism/index.xml') + ,(u'Culture', u'http://www.rian.ru/export/rss2/culture/index.xml') + ] + + def print_version(self, url): + return url.replace('.html','-print.html') + diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 7f1b524033..93d5283b4e 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -68,7 +68,7 @@ class PML2PMLZ(FileTypePlugin): of = self.temporary_file('_plugin_pml2pmlz.pmlz') pmlz = zipfile.ZipFile(of.name, 'w') - pmlz.write(pmlfile, os.path.basename(pmlfile)) + pmlz.write(pmlfile, os.path.basename(pmlfile), zipfile.ZIP_DEFLATED) pml_img = os.path.splitext(pmlfile)[0] + '_img' i_img = os.path.join(os.path.dirname(pmlfile),'images') @@ -450,7 +450,7 @@ from calibre.devices.eslick.driver import ESLICK from calibre.devices.nuut2.driver import NUUT2 from calibre.devices.iriver.driver import IRIVER_STORY from calibre.devices.binatone.driver import README -from calibre.devices.hanvon.driver import N516, EB511, ALEX +from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA from calibre.devices.edge.driver import EDGE from calibre.devices.teclast.driver import TECLAST_K3 from calibre.devices.sne.driver import SNE @@ -538,6 +538,7 @@ plugins += [ ALEX, PALMPRE, KOBO, + AZBOOKA, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ x.__name__.endswith('MetadataReader')] diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index beed7c679d..a4daefbea1 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -27,7 +27,7 @@ class ANDROID(USBMS): 0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]}, # Samsung - 0x04e8 : { 0x681d : [0x0222], 0x681c : [0x0222]}, + 0x04e8 : { 0x681d : [0x0222], 0x681c : [0x0222, 0x0224]}, # Acer 0x502 : { 0x3203 : [0x0100]}, @@ -38,9 +38,9 @@ class ANDROID(USBMS): 'be used') EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN) - VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER'] + VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER', 'GT-I5700'] WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', - '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE'] + '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD'] OSX_MAIN_MEM = 'HTC Android Phone Media' diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py index 1e76b62eb6..463dc69079 100644 --- a/src/calibre/devices/hanvon/driver.py +++ b/src/calibre/devices/hanvon/driver.py @@ -50,6 +50,20 @@ class ALEX(N516): EBOOK_DIR_MAIN = 'eBooks' SUPPORTS_SUB_DIRS = True +class AZBOOKA(ALEX): + + name = 'Azbooka driver' + gui_name = 'Azbooka' + description = _('Communicate with the Azbooka') + + VENDOR_NAME = 'LINUX' + WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET' + + MAIN_MEMORY_VOLUME_LABEL = 'Azbooka Internal Memory' + + EBOOK_DIR_MAIN = '' + + class EB511(USBMS): name = 'Elonex EB 511 driver' gui_name = 'EB 511' diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index d2583b95ba..0637dddfb6 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -176,6 +176,9 @@ class Stylizer(object): class_sel_pat = re.compile(r'\.[a-z]+', re.IGNORECASE) capital_sel_pat = re.compile(r'h|[A-Z]+') for _, _, cssdict, text, _ in rules: + fl = ':first-letter' in text + if fl: + text = text.replace(':first-letter', '') try: selector = CSSSelector(text) except (AssertionError, ExpressionError, etree.XPathSyntaxError, @@ -202,8 +205,21 @@ class Stylizer(object): if found: self.logger.warn('Ignoring case mismatches for CSS selector: %s in %s' %(text, item.href)) - for elem in matches: - self.style(elem)._update_cssdict(cssdict) + if fl: + from lxml.builder import ElementMaker + E = ElementMaker(namespace=XHTML_NS) + for elem in matches: + for x in elem.iter(): + if x.text: + span = E.span(x.text[0]) + span.tail = x.text[1:] + x.text = None + x.insert(0, span) + self.style(span)._update_cssdict(cssdict) + break + else: + for elem in matches: + self.style(elem)._update_cssdict(cssdict) for elem in xpath(tree, '//h:img[@width or @height]'): base = elem.get('style', '').strip() if base: diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index a1ecf14dfd..2267efb893 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -384,25 +384,25 @@ class Document(QWebPage): @property def height(self): - ans = self.javascript('document.body.offsetHeight', 'int') # contentsSize gives inaccurate results - if ans == 0: - ans = self.mainFrame().contentsSize().height() - return ans + j = self.javascript('document.body.offsetHeight', 'int') + q = self.mainFrame().contentsSize().height() + if q == j: + return j + if min(j, q) <= 0: + return max(j, q) + window_height = self.window_height + if j == window_height: + return j if q < 1.2*j else q + return j @property def width(self): return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results def set_bottom_padding(self, amount): - body = self.mainFrame().documentElement().findFirst('body') - if body.isNull(): - return - old_padding = unicode(body.styleProperty('padding-bottom', - body.ComputedStyle)).strip() - padding = u'%dpx'%amount - if old_padding != padding: - body.setStyleProperty('padding-bottom', padding + ' !important') - + s = QSize(-1, -1) if amount == 0 else QSize(self.width, + self.height+amount) + self.setPreferredContentsSize(s) class EntityDeclarationProcessor(object): @@ -705,13 +705,21 @@ class DocumentView(QWebView): def next_page(self): window_height = self.document.window_height + document_height = self.document.height + ddelta = document_height - window_height + #print '\nWindow height:', window_height + #print 'Document height:', self.document.height + delta_y = window_height - 25 - if self.document.at_bottom: + if self.document.at_bottom or ddelta <= 0: if self.manager is not None: self.manager.next_document() + elif ddelta < 25: + self.scroll_by(y=ddelta) + return else: oopos = self.document.ypos - #print '\nOriginal position:', oopos + #print 'Original position:', oopos self.document.set_bottom_padding(0) opos = self.document.ypos #print 'After set padding=0:', self.document.ypos @@ -722,8 +730,14 @@ class DocumentView(QWebView): lower_limit = opos + delta_y # Max value of top y co-ord after scrolling max_y = self.document.height - window_height # The maximum possible top y co-ord if max_y < lower_limit: + padding = lower_limit - max_y + if padding == window_height: + if self.manager is not None: + self.manager.next_document() + return #print 'Setting padding to:', lower_limit - max_y self.document.set_bottom_padding(lower_limit - max_y) + #print 'Document height:', self.document.height max_y = self.document.height - window_height lower_limit = min(max_y, lower_limit) #print 'Scroll to:', lower_limit diff --git a/src/calibre/gui2/viewer/main.ui b/src/calibre/gui2/viewer/main.ui index 24d9a3bdd0..be18a3036b 100644 --- a/src/calibre/gui2/viewer/main.ui +++ b/src/calibre/gui2/viewer/main.ui @@ -11,7 +11,7 @@ - Ebook Viewer + E-book Viewer diff --git a/src/calibre/library/db/__init__.py b/src/calibre/library/db/__init__.py new file mode 100644 index 0000000000..0080175bfa --- /dev/null +++ b/src/calibre/library/db/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + + diff --git a/src/calibre/library/db/base.py b/src/calibre/library/db/base.py new file mode 100644 index 0000000000..a2374583eb --- /dev/null +++ b/src/calibre/library/db/base.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +''' Design documentation {{{ + + Storage paradigm {{{ + * Agnostic to storage paradigm (i.e. no book per folder assumptions) + * Two separate concepts: A store and collection + A store is a backend, like a sqlite database associated with a path on + the local filesystem, or a cloud based storage solution. + A collection is a user defined group of stores. Most of the logic for + data manipulation sorting/searching/restrictions should be in the collection + class. The collection class should transparently handle the + conversion from store name + id to row number in the collection. + * Not sure how feasible it is to allow many-many maps between stores + and collections. + }}} + + Event system {{{ + * Comprehensive event system that other components can subscribe to + * Subscribers should be able to temporarily block receiving events + * Should event dispatch be asynchronous? + * Track last modified time for metadata and each format + }}} +}}}''' + +# Imports {{{ +# }}} + + + + diff --git a/src/calibre/linux.py b/src/calibre/linux.py index d0ac82e319..331783c775 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -313,7 +313,7 @@ class PostInstall: with open(os.path.join(base, '95-calibre.rules'), 'wb') as udev: self.manifest.append(udev.name) udev.write('''# Sony Reader PRS-500\n''' - '''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,) + '''SUBSYSTEMS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,) ) except: if self.opts.fatal_errors: diff --git a/src/calibre/manual/develop.rst b/src/calibre/manual/develop.rst index f061100f6d..ca067e45bc 100644 --- a/src/calibre/manual/develop.rst +++ b/src/calibre/manual/develop.rst @@ -152,7 +152,7 @@ Linux development environment used in windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development environment from source are in the INSTALL file in the source tree. Here we will address using the binary a runtime. -Install the |app| using the binary installer. The opena terminal and change to the previously checked out |app| code directory, for example:: +Install the |app| using the binary installer. Then open a terminal and change to the previously checked out |app| code directory, for example:: cd /home/kovid/work/calibre