From f8107ebffd19090bcb91e9723ed5512017ebd970 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 9 May 2010 14:30:14 -0600 Subject: [PATCH 1/7] Support for the Azbooka --- src/calibre/customize/builtins.py | 3 ++- src/calibre/devices/hanvon/driver.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 7f1b524033..63c26df5a5 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -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/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' From 2288b241370fe249f2f25f1f0e14a896811ab742 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 May 2010 07:51:30 -0600 Subject: [PATCH 2/7] Various Russian News surces by Darko Miletic --- resources/images/news/aif_ru.png | Bin 0 -> 1003 bytes resources/images/news/izvestia.png | Bin 0 -> 475 bytes resources/images/news/kommersant.png | Bin 0 -> 353 bytes resources/images/news/ria_ru.png | Bin 0 -> 534 bytes resources/recipes/aif_ru.recipe | 31 +++++++++++++++++++ resources/recipes/izvestia.recipe | 28 +++++++++++++++++ resources/recipes/kommersant.recipe | 42 ++++++++++++++++++++++++++ resources/recipes/ria_ru.recipe | 43 +++++++++++++++++++++++++++ src/calibre/library/db/__init__.py | 9 ++++++ src/calibre/library/db/base.py | 37 +++++++++++++++++++++++ 10 files changed, 190 insertions(+) create mode 100644 resources/images/news/aif_ru.png create mode 100644 resources/images/news/izvestia.png create mode 100644 resources/images/news/kommersant.png create mode 100644 resources/images/news/ria_ru.png create mode 100644 resources/recipes/aif_ru.recipe create mode 100644 resources/recipes/izvestia.recipe create mode 100644 resources/recipes/kommersant.recipe create mode 100644 resources/recipes/ria_ru.recipe create mode 100644 src/calibre/library/db/__init__.py create mode 100644 src/calibre/library/db/base.py diff --git a/resources/images/news/aif_ru.png b/resources/images/news/aif_ru.png new file mode 100644 index 0000000000000000000000000000000000000000..57d98f383291318e642dbef3bfd1f99f9cbda20a GIT binary patch literal 1003 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87??#oT^vI!PPmQRU(>nkAz3=y|of{u6 z?on7eWm?h(p{X|nIzkq!g+*WfC77(GEo|=gc>bco$9}?#xo4Yo-Ez}jXfZ*{DTA{q zpv~}dSDA)^v&6H&H2c1YG`ZaqnZ^mR2^F4FO6atL)}Ie^eo-#wEbwp4q`5KNKr9$gV z|Qy@UZ{AE(33oMp>)d^`HQW#|6Ptn8&b7mH40Sj(fq+S0(vZZlh< z<$(UfBRTWa<@MG#_QBbJk zoX(NM{Vso_!AIfE$&dH^e#S8`C_(esy10c;8=Eg&V~R?g&YjWYuUg*!bM|GPFZOI- zIhWc0Z8^F!^WJWQrr9})zdq}&m|(lV~x1}vNuxmeh>FsaG< zdhm|hm2YmjU79UDN4}ET^aIOJ?faXayW9MJ_4-2TlxyAbMafx0XTuurzKMM$m=+wo z#oX+HyP|J9|HQc1hqFHx{@QLHIWs<~RQL44_-7sZI-57t%-(m<(S&=u<~@CZ?7Rn0 zRt7)WZoVV@-qV%e?=LL;XI?kYXXCz!>ff)6eckGL>KB9M{RuJ>i@qPqesS}s>z)*A zTW594A9pS3;E7?>b}FB5dH&(^ zc!kPE>@l<5{q~=^*j|0GB0#t~tMjYDl6BkLlh5-wC+pUk9@;8v!05F~X~kc|g-R>l zG>SWKac9Y3>R1)<)lnqqa>II&cl-H&uv=`7@0#m>ZZj~msFt`!l%yn|N12ZcFgN9olmY`_J%}>cpt3=jdXkZ0I24)Zqb=hLa Qff^V*UHx3vIVCg!0B&xyv;Y7A literal 0 HcmV?d00001 diff --git a/resources/images/news/izvestia.png b/resources/images/news/izvestia.png new file mode 100644 index 0000000000000000000000000000000000000000..d80a2a06f061ec5c34b2e4a863747ef3f4e608ed GIT binary patch literal 475 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah2>S z4={E+nQaFWEGuwK2hw1@3^B*n9%f)*uX4CANmg+BfuQ1I~KnDm|9fPw4v!xwfJg%Hiy1v(MGf#Tivons? zX0hMyZS(cN8X2uuk;|}Zrf#XOfV+!|mlnfelO0bR8>(uU*Rx!D-m%_7!oXivf@PJ} zyA3;!bQFK}u?gGCVK#rt4d!DFZZ8cKpZU49%kMsC@z(q1BDHlp~AxZyT5NaYL)wdvw_KBRYT#Q6Vk4$B;SAFO<~xx$+2>S z4={E+nQaFWEGuwK2hw1@3^B*n9tLvyJY5_^G|uM+9u#Cz(yOuPOC*hbCZp96H6YKdz^NlIc#s#S7PDv)9@GB7mIH89mR zGzc*;w=y=fGPKk+Ftai+Xt?!Z35tf?{FKbJN@NX&23AH!R)!`J4LdwH`T#XBc)I$z JtaD0e0svJ)cf9}r literal 0 HcmV?d00001 diff --git a/resources/images/news/ria_ru.png b/resources/images/news/ria_ru.png new file mode 100644 index 0000000000000000000000000000000000000000..069c37f72e5bf74cc65ecf52049972ebf5102b80 GIT binary patch literal 534 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#Qt6T^vI!P9L4L*3ZRIz|B8DLDz zb>*@OzOX9~Dw$77KQL5n40qsXsuxY-b#|JNa?v};<^6rV38pGN8JGO_85`dh`DcCS z$E5JnHJ@)JnC+?LmzyDbeZ%H(mvcMw8=ej9U7>WT`)o$^ z!*YhT;))^*^VV!LzV6vkIp>d=UvUO_QmvAUQh^kMk%6Isu7Rnpp+Sg&xs|b*m7%4sfti(oLBp*N nOHefA=BH$)RU&IJG_V39Qxk}Wg(93?Kn)C@u6{1-oD!Mj literal 0 HcmV?d00001 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/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 {{{ +# }}} + + + + From 87e89cb28b164b4aef2d734738fef87ffdb89d83 Mon Sep 17 00:00:00 2001 From: Carl Witty Date: Mon, 10 May 2010 10:13:10 -0700 Subject: [PATCH 3/7] When creating a .pmlz file, compress the .pml --- src/calibre/customize/builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 63c26df5a5..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') From 5e5a613b8fbf21457728edcdb3431c706e96192e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 May 2010 11:50:27 -0600 Subject: [PATCH 4/7] Linux prs 500 udev rule: Use SUBSYSTEMS instead of the deprecated BUS --- src/calibre/linux.py | 2 +- src/calibre/manual/develop.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 From 76c7add44c32fea56a7a866fc83edc6f94cde82e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 May 2010 15:13:54 -0600 Subject: [PATCH 5/7] Conversion pipeline: Support for the :first-letter pseudo selector --- src/calibre/ebooks/oeb/stylizer.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) 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: From 4ea4986a9dd39a0c23f23bb723047729275c8326 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 May 2010 16:02:27 -0600 Subject: [PATCH 6/7] Support for the Samsung GT-I5700 --- src/calibre/devices/android/driver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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' From 348b268c7bc371bef9c199697fb371f35fed7e4f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 May 2010 19:09:12 -0600 Subject: [PATCH 7/7] E-book viewer: Fix next page scrolling when current document is just a little more than a screenfull. Also use a more robust method to insert blank space at the end of the document when the last screenfull is partially empty. --- src/calibre/gui2/viewer/documentview.py | 44 ++++++++++++++++--------- src/calibre/gui2/viewer/main.ui | 2 +- 2 files changed, 30 insertions(+), 16 deletions(-) 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