Merge from trunk

This commit is contained in:
Charles Haley 2012-06-24 09:28:43 +02:00
commit c2b317bab4
119 changed files with 24996 additions and 22363 deletions

View File

@ -20,6 +20,80 @@
# - title:
- version: 0.8.57
date: 2012-06-22
new features:
- title: "PDF Output: Full pagination support. No more cutoff bottom line."
type: major
description: "Fixes a long standing bug in calibre's PDF Output that caused the bottom line of some pages to be partially cut off and prevented top and bottom margins from working."
- title: "calibredb add now prints out the ids of added books"
tickets: [1014303]
- title: "Kobo Vox driver: Add support for new Google Play firmware"
tickets: [1014129]
- title: "Driver for Prestigio PMP5097PRO"
tickets: [1013864]
- title: "Add option to disable tooltips in the book list under Preferences->Look & Feel"
- title: "When customizing builtin recipes download the latest version of the recipe to customize instead of using the possibly out of date bundled version"
bug fixes:
- title: "PDF Output: Use the cover from the input document when no cover is specified during a conversion"
- title: "E-book Viewer: Printing now has proper pagination with top and bottom margins no lines partially cut-off at the bottom and full style retention"
- title: "KF8 Input: Handle files with incorrectly encoded guide type entries."
tickets: [1015020]
- title: "E-book viewer: Disable hyphenation on windows xp as Qt WebKit barfs on soft hyphens on windows XP"
- title: "Handle OS X systems with invalid palette colors."
tickets: [1014900]
- title: "Tag Browser: Fix regression that broke partitioning of hierarchical categories."
tickets: [1014065]
- title: "LRF Output: Handle negative page margins"
tickets: [1014103]
- title: "Template language: Fix arithmetic functions to tolerate the value 'None' as returned by raw_field()"
- title: "Fix custom title sort set in the edit metadata dialog getting reset by the conversion dialog"
improved recipes:
- The Economist
- Akter
- 24 Sata sr
- Novi List
- Metro Montreal
- Mode Durable
- CanardPC
- The Economic Collapse
- Our Daily Bread
new recipes:
- title: Akter Daily
author: Darko MIletic
- title: BBC Brasil
author: Claviola
- title: Homopedia.pl
author: rainbowwarrior
- title: National Geographic Magazine
author: Terminal Veracity
- title: Something Awful
author: atordo
- title: Huffington Post UK
author: Krittika Goyal
- version: 0.8.56
date: 2012-06-15

View File

@ -195,7 +195,7 @@ It can get tiresome to keep re-adding a plugin to calibre to test small changes.
Once you've located the zip file of your plugin you can then directly update it with your changes instead of re-adding it each time. To do so from the command line, in the directory that contains your plugin source code, use::
calibre -s; sleep 4s; zip -R /path/to/plugin/zip/file.zip *; calibre
calibre -s; zip -R /path/to/plugin/zip/file.zip *; calibre
This will shutdown a running calibre. Wait for the shutdown to complete, then update your plugin files and relaunch calibre.
It relies on the freely available zip command line tool.

View File

@ -1,6 +1,7 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2009-2012, Darko Miletic <darko.miletic at gmail.com>'
'''
24sata.rs
@ -21,26 +22,29 @@ class Ser24Sata(BasicNewsRecipe):
encoding = 'utf-8'
use_embedded_content = False
language = 'sr'
publication_type = 'newspaper'
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}'
publication_type = 'newsportal'
extra_css = """
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
body{font-family: serif1, serif}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
, 'linearize_tables' : True
'comment' : description
, 'tags' : category
, 'publisher': publisher
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
feeds = [(u'Vesti Dana', u'http://www.24sata.rs/rss.php')]
def preprocess_html(self, soup):
return self.adeify_images(soup)
feeds = [
(u'Vesti' , u'http://www.24sata.rs/rss/vesti.xml' ),
(u'Sport' , u'http://www.24sata.rs/rss/sport.xml' ),
(u'Šou' , u'http://www.24sata.rs/rss/sou.xml' ),
(u'Specijal', u'http://www.24sata.rs/rss/specijal.xml'),
(u'Novi Sad', u'http://www.24sata.rs/rss/ns.xml' )
]
def print_version(self, url):
article = url.partition('#')[0]
article_id = article.partition('id=')[2]
return 'http://www.24sata.rs/_print.php?id=' + article_id
dpart, spart, apart = url.rpartition('/')
return dpart + '/print/' + apart

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2010-2012, Darko Miletic <darko.miletic at gmail.com>'
'''
abc.com.py
'''
@ -7,7 +7,7 @@ abc.com.py
from calibre.web.feeds.news import BasicNewsRecipe
class ABC_py(BasicNewsRecipe):
title = 'ABC digital'
title = 'ABC Color'
__author__ = 'Darko Miletic'
description = 'Noticias de Paraguay y el resto del mundo'
publisher = 'ABC'
@ -15,12 +15,16 @@ class ABC_py(BasicNewsRecipe):
oldest_article = 2
max_articles_per_feed = 200
no_stylesheets = True
encoding = 'cp1252'
encoding = 'utf8'
use_embedded_content = False
language = 'es_PY'
remove_empty_feeds = True
masthead_url = 'http://www.abc.com.py/plantillas/img/abc-logo.png'
publication_type = 'newspaper'
extra_css = ' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} '
extra_css = """
body{font-family: UnitSlabProMedium,"Times New Roman",serif }
img{margin-bottom: 0.4em; display: block;}
"""
conversion_options = {
'comment' : description
@ -29,21 +33,19 @@ class ABC_py(BasicNewsRecipe):
, 'language' : language
}
remove_tags = [dict(name=['form','iframe','embed','object','link','base','table']),dict(attrs={'class':'toolbox'})]
remove_tags_after = dict(attrs={'class':'date'})
keep_only_tags = [dict(attrs={'class':'zcontent'})]
remove_tags = [
dict(name=['form','iframe','embed','object','link','base','table']),
dict(attrs={'class':['es-carousel-wrapper']}),
dict(attrs={'id':['tools','article-banner-1']})
]
keep_only_tags = [dict(attrs={'id':'article'})]
feeds = [
(u'Ultimo momento' , u'http://www.abc.com.py/ultimo-momento.xml' )
,(u'Nacionales' , u'http://www.abc.com.py/nacionales.xml' )
,(u'Internacionales' , u'http://www.abc.com.py/internacionales.xml' )
,(u'Deportes' , u'http://www.abc.com.py/deportes.xml' )
,(u'Espectaculos' , u'http://www.abc.com.py/espectaculos.xml' )
,(u'Ciencia y Tecnologia', u'http://www.abc.com.py/ciencia-y-tecnologia.xml')
(u'Ultimo momento', u'http://www.abc.com.py/rss.xml' )
,(u'Nacionales' , u'http://www.abc.com.py/nacionales/rss.xml' )
,(u'Mundo' , u'http://www.abc.com.py/internacionales/rss.xml')
,(u'Deportes' , u'http://www.abc.com.py/deportes/rss.xml' )
,(u'Espectaculos' , u'http://www.abc.com.py/espectaculos/rss.xml' )
,(u'TecnoCiencia' , u'http://www.abc.com.py/ciencia/rss.xml' )
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return soup

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2010-2012, Darko Miletic <darko.miletic at gmail.com>'
'''
akter.co.rs
'''
@ -8,7 +8,7 @@ import re
from calibre.web.feeds.news import BasicNewsRecipe
class Akter(BasicNewsRecipe):
title = 'AKTER'
title = 'AKTER - Nedeljnik'
__author__ = 'Darko Miletic'
description = 'AKTER - nedeljni politicki magazin savremene Srbije'
publisher = 'Akter Media Group d.o.o.'
@ -18,61 +18,37 @@ class Akter(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png'
masthead_url = 'http://www.akter.co.rs/gfx/logoneover.png'
language = 'sr'
publication_type = 'magazine'
remove_empty_feeds = True
PREFIX = 'http://www.akter.co.rs'
extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
.color-2{display:block; margin-bottom: 10px; padding: 5px, 10px;
border-left: 1px solid #D00000; color: #D00000}
img{margin-bottom: 0.8em} """
body{font-family: Tahoma,Geneva,sans1,sans-serif}
img{margin-bottom: 0.8em; display: block;}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
, 'linearize_tables' : True
'comment' : description
, 'tags' : category
, 'publisher': publisher
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
feeds = [
(u'Politika' , u'http://www.akter.co.rs/index.php/politikaprint.html' )
,(u'Ekonomija' , u'http://www.akter.co.rs/index.php/ekonomijaprint.html')
,(u'Life&Style' , u'http://www.akter.co.rs/index.php/lsprint.html' )
,(u'Sport' , u'http://www.akter.co.rs/index.php/sportprint.html' )
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return self.adeify_images(soup)
keep_only_tags = [dict(name='div', attrs={'id':'section_to_print'})]
feeds = [(u'Nedeljnik', u'http://akter.co.rs/rss/nedeljnik')]
def print_version(self, url):
return url + '?tmpl=component&print=1&page='
dpart, spart, apart = url.rpartition('/')
return dpart + '/print-' + apart
def parse_index(self):
totalfeeds = []
lfeeds = self.get_feeds()
for feedobj in lfeeds:
feedtitle, feedurl = feedobj
self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl))
articles = []
soup = self.index_to_soup(feedurl)
for item in soup.findAll(attrs={'class':['sectiontableentry1','sectiontableentry2']}):
link = item.find('a')
url = self.PREFIX + link['href']
title = self.tag_to_string(link)
articles.append({
'title' :title
,'date' :''
,'url' :url
,'description':''
})
totalfeeds.append((feedtitle, articles))
return totalfeeds
def get_cover_url(self):
soup = self.index_to_soup('http://www.akter.co.rs/weekly.html')
divt = soup.find('div', attrs={'class':'lastissue'})
if divt:
imgt = divt.find('img')
if imgt:
return 'http://www.akter.co.rs' + imgt['src']
return None

View File

@ -0,0 +1,44 @@
__license__ = 'GPL v3'
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
'''
akter.co.rs
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class Akter(BasicNewsRecipe):
title = 'AKTER - Dnevnik'
__author__ = 'Darko Miletic'
description = 'AKTER - Najnovije vesti iz Srbije'
publisher = 'Akter Media Group d.o.o.'
category = 'vesti, online vesti, najnovije vesti, politika, sport, ekonomija, biznis, finansije, berza, kultura, zivot, putovanja, auto, automobili, tehnologija, politicki magazin, dogadjaji, desavanja, lifestyle, zdravlje, zdravstvo, vest, novine, nedeljnik, srbija, novi sad, vojvodina, svet, drustvo, zabava, republika srpska, beograd, intervju, komentar, reportaza, arhiva vesti, news, serbia, politics'
oldest_article = 8
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
masthead_url = 'http://www.akter.co.rs/gfx/logodnover.png'
language = 'sr'
publication_type = 'magazine'
remove_empty_feeds = True
extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: Tahoma,Geneva,sans1,sans-serif}
img{margin-bottom: 0.8em; display: block;}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher': publisher
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
keep_only_tags = [dict(name='div', attrs={'id':'section_to_print'})]
feeds = [(u'Vesti', u'http://akter.co.rs/rss/dnevni')]
def print_version(self, url):
dpart, spart, apart = url.rpartition('/')
return dpart + '/print-' + apart

View File

@ -147,10 +147,9 @@ class BBCBrasilRecipe(BasicNewsRecipe):
# Author of this recipe.
__author__ = 'claviola'
__author__ = 'Carlos Laviola'
# Specify English as the language of the RSS feeds (ISO-639 code).
language = 'en_GB'
language = 'pt_BR'
# Set tags.
tags = 'news, sport, blog'

View File

@ -20,7 +20,23 @@ class Economist(BasicNewsRecipe):
INDEX = 'http://www.economist.com/printedition'
description = ('Global news and current affairs from a European'
' perspective. Best downloaded on Friday mornings (GMT)')
extra_css = '.headline {font-size: x-large;} \n h2 { font-size: small; } \n h1 { font-size: medium; }'
extra_css = '''
.headline {font-size: x-large;}
h2 { font-size: small; }
h1 { font-size: medium; }
.pullquote {
float: right;
font-size: larger;
font-weight: bold;
font-style: italic;
page-break-inside:avoid;
border-bottom: 3px solid black;
border-top: 3px solid black;
width: 228px;
margin: 0px 0px 10px 15px;
padding: 7px 0px 9px;
}
'''
oldest_article = 7.0
remove_tags = [
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),

View File

@ -20,7 +20,24 @@ class Economist(BasicNewsRecipe):
INDEX = 'http://www.economist.com/printedition'
description = ('Global news and current affairs from a European'
' perspective. Best downloaded on Friday mornings (GMT)')
extra_css = '.headline {font-size: x-large;} \n h2 { font-size: small; } \n h1 { font-size: medium; }'
extra_css = '''
.headline {font-size: x-large;}
h2 { font-size: small; }
h1 { font-size: medium; }
.pullquote {
float: right;
font-size: larger;
font-weight: bold;
font-style: italic;
page-break-inside:avoid;
border-bottom: 3px solid black;
border-top: 3px solid black;
width: 228px;
margin: 0px 0px 10px 15px;
padding: 7px 0px 9px;
}
'''
oldest_article = 7.0
remove_tags = [
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),

View File

@ -0,0 +1,56 @@
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.utils.ipc.simple_worker import fork_job
from calibre.ptempfile import PersistentTemporaryFile
js_fetcher = '''
import calibre.web.jsbrowser.browser as jsbrowser
def grab(url):
browser = jsbrowser.Browser()
#10 second timeout
browser.visit(url, 10)
browser.run_for_a_time(10)
html = browser.html
browser.close()
return html
'''
class MarketingSensoriale(BasicNewsRecipe):
title = u'Marketing sensoriale'
__author__ = 'NotTaken'
description = 'Marketing Sensoriale, il Blog'
category = 'Blog'
oldest_article = 7
max_articles_per_feed = 200
no_stylesheets = True
encoding = 'utf8'
use_embedded_content = False
language = 'it'
remove_empty_feeds = True
recursions = 0
requires_version = (0, 8, 58)
auto_cleanup = False
simultaneous_downloads = 1
articles_are_obfuscated = True
remove_tags_after = [dict(name='div', attrs={'class':['article-footer']})]
def get_article_url(self, article):
return article.get('feedburner_origlink', None)
def get_obfuscated_article(self, url):
result = fork_job(js_fetcher, 'grab', (url,), module_is_source_code=True)
html = result['result']
if isinstance(html, type(u'')):
html = html.encode('utf-8')
pt = PersistentTemporaryFile('.html')
pt.write(html)
pt.close()
return pt.name
feeds = [(u'Marketing sensoriale', u'http://feeds.feedburner.com/MarketingSensoriale?format=xml')]

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
@ -10,11 +10,11 @@ from calibre.web.feeds.news import BasicNewsRecipe
class OGlobo(BasicNewsRecipe):
title = 'O Globo'
__author__ = 'Darko Miletic and Sujata Raman'
__author__ = 'Darko Miletic and Carlos Laviola'
description = 'News from Brasil'
publisher = 'O Globo'
category = 'news, politics, Brasil'
oldest_article = 2
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
@ -39,43 +39,35 @@ class OGlobo(BasicNewsRecipe):
.commentario p{color:#007BB5; font-style:italic;}
'''
keep_only_tags = [dict(name='div', attrs={'id':'ltintb'}),
dict(name='a', attrs={'class':['img imgLoader','img ftr imgLoader']}),]
remove_tags = [
dict(name='script')
,dict(name='object')
,dict(name='form')
,dict(name='div', attrs={'id':['linksPatGoogle','rdpm','cor','com','env','rcm_st','coment',]})
,dict(name='div', attrs={'class':'box-zap-anu2'})
,dict(name='a', attrs={'class':'assine'})
,dict(name='link')
,dict(name='div', attrs={'id':'header'})
,dict(name='p', attrs={'id':'info-date-press'})
]
feeds = [
(u'Todos os canais', u'http://oglobo.globo.com/rss/plantao.xml')
,(u'Ciencia', u'http://oglobo.globo.com/rss/plantaociencia.xml')
,(u'Educacao', u'http://oglobo.globo.com/rss/plantaoeducacao.xml')
,(u'Opiniao', u'http://oglobo.globo.com/rss/plantaoopiniao.xml')
,(u'Sao Paulo', u'http://oglobo.globo.com/rss/plantaosaopaulo.xml')
,(u'Viagem', u'http://oglobo.globo.com/rss/plantaoviagem.xml')
,(u'Cultura', u'http://oglobo.globo.com/rss/plantaocultura.xml')
,(u'Esportes', u'http://oglobo.globo.com/rss/plantaoesportes.xml')
,(u'Mundo', u'http://oglobo.globo.com/rss/plantaomundo.xml')
,(u'Pais', u'http://oglobo.globo.com/rss/plantaopais.xml')
,(u'Rio', u'http://oglobo.globo.com/rss/plantaorio.xml')
,(u'Saude', u'http://oglobo.globo.com/rss/plantaosaude.xml')
,(u'Viver Melhor', u'http://oglobo.globo.com/rss/plantaovivermelhor.xml')
,(u'Economia', u'http://oglobo.globo.com/rss/plantaoeconomia.xml')
,(u'Tecnologia', u'http://oglobo.globo.com/rss/plantaotecnologia.xml')
(u'Todos os canais', u'http://oglobo.globo.com/rss.xml?completo=true')
,(u'Ciencia', u'http://oglobo.globo.com/rss.xml?secao=ciencia&completo=true')
,(u'Educacao', u'http://oglobo.globo.com/rss.xml?secao=educacao&completo=true')
,(u'Opiniao', u'http://oglobo.globo.com/rss.xml?secao=opiniao&completo=true')
,(u'Cultura', u'http://oglobo.globo.com/rss.xml?secao=cultura&completo=true')
,(u'Esportes', u'http://oglobo.globo.com/rss.xml?secao=esportes&completo=true')
,(u'Mundo', u'http://oglobo.globo.com/rss.xml?secao=mundo&completo=true')
,(u'Pais', u'http://oglobo.globo.com/rss.xml?secao=pais&completo=true')
,(u'Rio', u'http://oglobo.globo.com/rss.xml?secao=rio&completo=true')
,(u'Saude', u'http://oglobo.globo.com/rss.xml?secao=saude&completo=true')
,(u'Economia', u'http://oglobo.globo.com/rss.xml?secao=economia&completo=true')
,(u'Tecnologia', u'http://oglobo.globo.com/rss.xml?secao=tecnologia&completo=true')
]
def print_version(self, url):
return url + '?service=print'
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return soup
language = 'pt'
language = 'pt_BR'

Binary file not shown.

View File

@ -60,8 +60,13 @@ function goto_reference(ref) {
if (num < 0) {alert("Invalid reference: "+ref); return;}
var p = $("p");
if (num >= p.length) {alert("Reference not found: "+ref); return;}
$.scrollTo($(p[num]), 1000,
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
var dest = $(p[num]);
if (window.paged_display.in_paged_mode) {
var xpos = dest.offset().left;
window.paged_display.scroll_to_xpos(xpos, true, true, 1000);
} else
$.scrollTo(dest, 1000,
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
}

View File

@ -24,6 +24,15 @@ def get_rsync_pw():
return open('/home/kovid/work/kde/conf/buildbot').read().partition(
':')[-1].strip()
def is_vm_running(name):
pat = '/%s/'%name
pids= [pid for pid in os.listdir('/proc') if pid.isdigit()]
for pid in pids:
cmdline = open(os.path.join('/proc', pid, 'cmdline'), 'rb').read()
if 'vmware-vmx' in cmdline and pat in cmdline:
return True
return False
class Rsync(Command):
description = 'Sync source tree from development machine'
@ -46,15 +55,17 @@ class Push(Command):
def run(self, opts):
from threading import Thread
threads = []
for host in (
r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre',
'kovid@ox:calibre',
r'kovid@win7:/cygdrive/c/Users/kovid/calibre',
):
rcmd = BASE_RSYNC + EXCLUDES + ['.', host]
print '\n\nPushing to:', host, '\n'
threads.append(Thread(target=subprocess.check_call, args=(rcmd,)))
threads[-1].start()
for host, vmname in {
r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre':'winxp',
'kovid@ox:calibre':None,
r'kovid@win7:/cygdrive/c/Users/kovid/calibre':'Windows 7',
}.iteritems():
if vmname is None or is_vm_running(vmname):
rcmd = BASE_RSYNC + EXCLUDES + ['.', host]
print '\n\nPushing to:', vmname or host, '\n'
threads.append(Thread(target=subprocess.check_call, args=(rcmd,),
kwargs={'stdout':open(os.devnull, 'wb')}))
threads[-1].start()
for thread in threads:
thread.join()
@ -118,13 +129,7 @@ class VMInstaller(Command):
def run_vm(self):
pat = '/%s/'%(self.VM_CHECK or self.VM_NAME)
pids= [pid for pid in os.listdir('/proc') if pid.isdigit()]
for pid in pids:
cmdline = open(os.path.join('/proc', pid, 'cmdline'), 'rb').read()
if 'vmware-vmx' in cmdline and pat in cmdline:
return
if is_vm_running(self.VM_CHECK or self.VM_NAME): return
self.__p = subprocess.Popen([self.vm])
def start_vm(self, sleep=75):

View File

@ -97,7 +97,7 @@ Now, run configure and make::
-no-plugin-manifests is needed so that loading the plugins does not fail looking for the CRT assembly
configure -opensource -release -ltcg -qt-zlib -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -no-plugin-manifests -openssl -I Q:\openssl\include -L Q:\openssl\lib && nmake
configure -opensource -release -qt-zlib -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -no-plugin-manifests -openssl -I Q:\openssl\include -L Q:\openssl\lib && nmake
Add the path to the bin folder inside the Qt dir to your system PATH.
@ -115,7 +115,7 @@ PyQt4
Compiling instructions::
python configure.py -c -j5 -e QtCore -e QtGui -e QtSvg -e QtNetwork -e QtWebKit -e QtXmlPatterns --verbose
python configure.py -c -j5 -e QtCore -e QtGui -e QtSvg -e QtNetwork -e QtWebKit -e QtXmlPatterns --verbose --confirm-license
nmake
nmake install

File diff suppressed because it is too large Load Diff

View File

@ -13,31 +13,31 @@ msgstr ""
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
"devel@lists.alioth.debian.org>\n"
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
"PO-Revision-Date: 2011-09-27 18:14+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n"
"PO-Revision-Date: 2012-06-14 09:06+0000\n"
"Last-Translator: Eugene Marshal <Unknown>\n"
"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-11-26 05:35+0000\n"
"X-Generator: Launchpad (build 14381)\n"
"X-Launchpad-Export-Date: 2012-06-15 04:42+0000\n"
"X-Generator: Launchpad (build 15414)\n"
"Language: ru\n"
#. name for aaa
msgid "Ghotuo"
msgstr ""
msgstr "Гхотуо"
#. name for aab
msgid "Alumu-Tesu"
msgstr ""
msgstr "Алуму-тесу"
#. name for aac
msgid "Ari"
msgstr ""
msgstr "Ари"
#. name for aad
msgid "Amal"
msgstr ""
msgstr "Амал"
#. name for aae
msgid "Albanian; Arbëreshë"
@ -45,11 +45,11 @@ msgstr ""
#. name for aaf
msgid "Aranadan"
msgstr ""
msgstr "Аранадан"
#. name for aag
msgid "Ambrak"
msgstr ""
msgstr "Амбрак"
#. name for aah
msgid "Arapesh; Abu'"
@ -57,23 +57,23 @@ msgstr ""
#. name for aai
msgid "Arifama-Miniafia"
msgstr ""
msgstr "Арифама-Миниафиа"
#. name for aak
msgid "Ankave"
msgstr ""
msgstr "Анкаве"
#. name for aal
msgid "Afade"
msgstr ""
msgstr "Афаде"
#. name for aam
msgid "Aramanik"
msgstr ""
msgstr "Араманик"
#. name for aan
msgid "Anambé"
msgstr ""
msgstr "Анамбе"
#. name for aao
msgid "Arabic; Algerian Saharan"
@ -93,7 +93,7 @@ msgstr "Афар"
#. name for aas
msgid "Aasáx"
msgstr ""
msgstr "Асакс"
#. name for aat
msgid "Albanian; Arvanitika"
@ -101,27 +101,27 @@ msgstr ""
#. name for aau
msgid "Abau"
msgstr ""
msgstr "Абау"
#. name for aaw
msgid "Solong"
msgstr ""
msgstr "Солонг"
#. name for aax
msgid "Mandobo Atas"
msgstr ""
msgstr "Мандобо Атас"
#. name for aaz
msgid "Amarasi"
msgstr ""
msgstr "Амараси"
#. name for aba
msgid "Abé"
msgstr ""
msgstr "Абе"
#. name for abb
msgid "Bankon"
msgstr ""
msgstr "Банкон"
#. name for abc
msgid "Ayta; Ambala"
@ -129,7 +129,7 @@ msgstr ""
#. name for abd
msgid "Manide"
msgstr ""
msgstr "Мэнайд"
#. name for abe
msgid "Abnaki; Western"
@ -137,11 +137,11 @@ msgstr ""
#. name for abf
msgid "Abai Sungai"
msgstr ""
msgstr "Абаи Сунгаи"
#. name for abg
msgid "Abaga"
msgstr ""
msgstr "Абага"
#. name for abh
msgid "Arabic; Tajiki"
@ -149,11 +149,11 @@ msgstr ""
#. name for abi
msgid "Abidji"
msgstr ""
msgstr "Абиджи"
#. name for abj
msgid "Aka-Bea"
msgstr ""
msgstr "Ака-Беа"
#. name for abk
msgid "Abkhazian"
@ -161,19 +161,19 @@ msgstr "Абхазский"
#. name for abl
msgid "Lampung Nyo"
msgstr ""
msgstr "Лампунг Ньё"
#. name for abm
msgid "Abanyom"
msgstr ""
msgstr "Абанйом"
#. name for abn
msgid "Abua"
msgstr ""
msgstr "Абуа"
#. name for abo
msgid "Abon"
msgstr ""
msgstr "Абон"
#. name for abp
msgid "Ayta; Abellen"
@ -185,7 +185,7 @@ msgstr ""
#. name for abr
msgid "Abron"
msgstr ""
msgstr "Аброн"
#. name for abs
msgid "Malay; Ambonese"

View File

@ -4,7 +4,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = u'calibre'
numeric_version = (0, 8, 56)
numeric_version = (0, 8, 57)
__version__ = u'.'.join(map(unicode, numeric_version))
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"

View File

@ -90,6 +90,7 @@ class ANDROID(USBMS):
0x4e22 : [0x0100, 0x226, 0x227, 0x231],
0xb058 : [0x0222, 0x226, 0x227],
0x0ff9 : [0x0226],
0xc91 : HTC_BCDS,
0xdddd : [0x216],
},
@ -165,7 +166,10 @@ class ANDROID(USBMS):
0x2237: { 0x2208 : [0x0226] },
# Lenovo
0x17ef : { 0x7421 : [0x0216] },
0x17ef : {
0x7421 : [0x0216],
0x741b : [0x9999],
},
# Pantech
0x10a9 : { 0x6050 : [0x227] },
@ -203,7 +207,8 @@ class ANDROID(USBMS):
'GT-I9003_CARD', 'XT912', 'FILE-CD_GADGET', 'RK29_SDK', 'MB855',
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER',
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX']
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX',
'THINKPAD_TABLET']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',

View File

@ -5,7 +5,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, dbus
import os, dbus, re
def node_mountpoint(node):
@ -19,13 +19,20 @@ def node_mountpoint(node):
return de_mangle(line[1])
return None
class NoUDisks1(Exception):
pass
class UDisks(object):
def __init__(self):
self.bus = dbus.SystemBus()
self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks',
try:
self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks',
'/org/freedesktop/UDisks'), 'org.freedesktop.UDisks')
except dbus.exceptions.DBusException as e:
if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown':
raise NoUDisks1()
raise
def device(self, device_node_path):
devpath = self.main.FindDeviceByDeviceFile(device_node_path)
@ -56,6 +63,102 @@ class UDisks(object):
d = self.device(parent)
d.DriveEject([])
class NoUDisks2(Exception):
pass
class UDisks2(object):
BLOCK = 'org.freedesktop.UDisks2.Block'
FILESYSTEM = 'org.freedesktop.UDisks2.Filesystem'
DRIVE = 'org.freedesktop.UDisks2.Drive'
def __init__(self):
self.bus = dbus.SystemBus()
try:
self.bus.get_object('org.freedesktop.UDisks2',
'/org/freedesktop/UDisks2')
except dbus.exceptions.DBusException as e:
if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown':
raise NoUDisks2()
raise
def device(self, device_node_path):
device_node_path = os.path.realpath(device_node_path)
devname = device_node_path.split('/')[-1]
# First we try a direct object path
bd = self.bus.get_object('org.freedesktop.UDisks2',
'/org/freedesktop/UDisks2/block_devices/%s'%devname)
try:
device = bd.Get(self.BLOCK, 'Device',
dbus_interface='org.freedesktop.DBus.Properties')
device = bytearray(device).replace(b'\x00', b'').decode('utf-8')
except:
device = None
if device == device_node_path:
return bd
# Enumerate all devices known to UDisks
devs = self.bus.get_object('org.freedesktop.UDisks2',
'/org/freedesktop/UDisks2/block_devices')
xml = devs.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable')
for dev in re.finditer(r'name=[\'"](.+?)[\'"]', type(u'')(xml)):
bd = self.bus.get_object('org.freedesktop.UDisks2',
'/org/freedesktop/UDisks2/block_devices/%s2'%dev.group(1))
try:
device = bd.Get(self.BLOCK, 'Device',
dbus_interface='org.freedesktop.DBus.Properties')
device = bytearray(device).replace(b'\x00', b'').decode('utf-8')
except:
device = None
if device == device_node_path:
return bd
raise ValueError('%r not known to UDisks2'%device_node_path)
def mount(self, device_node_path):
d = self.device(device_node_path)
mount_options = ['rw', 'noexec', 'nosuid',
'sync', 'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()]
try:
return unicode(d.Mount(
{
'auth.no_user_interaction':True,
'options':','.join(mount_options)
},
dbus_interface=self.FILESYSTEM))
except:
# May be already mounted, check
mp = node_mountpoint(str(device_node_path))
if mp is None:
raise
return mp
def unmount(self, device_node_path):
d = self.device(device_node_path)
d.Unmount({'force':True, 'auth.no_user_interaction':True},
dbus_interface=self.FILESYSTEM)
def drive_for_device(self, device):
drive = device.Get(self.BLOCK, 'Drive',
dbus_interface='org.freedesktop.DBus.Properties')
return self.bus.get_object('org.freedesktop.UDisks2', drive)
def eject(self, device_node_path):
drive = self.drive_for_device(self.device(device_node_path))
drive.Eject({'auth.no_user_interaction':True},
dbus_interface=self.DRIVE)
def get_udisks(ver=None):
if ver is None:
try:
u = UDisks2()
except NoUDisks2:
u = UDisks()
return u
return UDisks2() if ver == 2 else UDisks()
def mount(node_path):
u = UDisks()
u.mount(node_path)
@ -68,15 +171,19 @@ def umount(node_path):
u = UDisks()
u.unmount(node_path)
if __name__ == '__main__':
def test_udisks(ver=None):
import sys
dev = sys.argv[1]
print 'Testing with node', dev
u = UDisks()
u = get_udisks(ver=ver)
print 'Using Udisks:', u.__class__.__name__
print 'Mounted at:', u.mount(dev)
print 'Unmounting'
u.unmount(dev)
print 'Ejecting:'
u.eject(dev)
if __name__ == '__main__':
test_udisks()

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
'''
@ -61,7 +61,7 @@ ORIENTATIONS = ['portrait', 'landscape']
class PDFOutput(OutputFormatPlugin):
name = 'PDF Output'
author = 'John Schember and Kovid Goyal'
author = 'Kovid Goyal'
file_type = 'pdf'
options = set([
@ -97,24 +97,6 @@ class PDFOutput(OutputFormatPlugin):
self.metadata = oeb_book.metadata
self.cover_data = None
# Remove page-break-before on <body> element as it causes
# blank pages in PDF Output
from calibre.ebooks.oeb.base import XPath
stylesheet = self.oeb.manifest.main_stylesheet
if stylesheet is not None:
from cssutils.css import CSSRule
classes = set(['.calibre'])
for x in self.oeb.spine:
root = x.data
body = XPath('//h:body[@class]')(root)
if body:
classes.add('.'+body[0].get('class'))
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
if rule.selectorList.selectorText in classes:
rule.style.removeProperty('page-break-before')
rule.style.removeProperty('page-break-after')
if input_plugin.is_image_collection:
log.debug('Converting input as an image collection...')
@ -128,16 +110,12 @@ class PDFOutput(OutputFormatPlugin):
self.write(ImagePDFWriter, images)
def get_cover_data(self):
g, m = self.oeb.guide, self.oeb.manifest
if 'titlepage' not in g:
if 'cover' in g:
href = g['cover'].href
from calibre.ebooks.oeb.base import urlnormalize
for item in m:
if item.href == urlnormalize(href):
self.cover_data = item.data
if not isinstance(self.cover_data, basestring):
self.cover_data = None
oeb = self.oeb
if (oeb.metadata.cover and
unicode(oeb.metadata.cover[0]) in oeb.manifest.ids):
cover_id = unicode(oeb.metadata.cover[0])
item = oeb.manifest.ids[cover_id]
self.cover_data = item.data
def convert_text(self, oeb_book):
from calibre.ebooks.pdf.writer import PDFWriter

View File

@ -687,7 +687,11 @@ class Amazon(Source):
return True
for div in root.xpath(r'//div[starts-with(@id, "result_")]'):
for a in div.xpath(r'descendant::a[@class="title" and @href]'):
links = div.xpath(r'descendant::a[@class="title" and @href]')
if not links:
# New amazon markup
links = div.xpath('descendant::h3/a[@href]')
for a in links:
title = tostring(a, method='text', encoding=unicode)
if title_ok(title):
matches.append(a.get('href'))

View File

@ -167,7 +167,8 @@ def test_identify(tests): # {{{
# }}}
def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None): # {{{
def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None,
fail_missing_meta=True): # {{{
'''
:param name: Plugin name
:param tests: List of 2-tuples. Each two tuple is of the form (args,
@ -246,7 +247,8 @@ def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None): # {{{
None]
if not good:
prints('Failed to find', plugin.test_fields(possibles[0]))
raise SystemExit(1)
if fail_missing_meta:
raise SystemExit(1)
if results[0] is not possibles[0]:
prints('Most relevant result failed the tests')
@ -263,21 +265,22 @@ def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None): # {{{
results.append(rq.get_nowait())
except Empty:
break
if not results:
if not results and fail_missing_meta:
prints('Cover download failed')
raise SystemExit(1)
cdata = results[0]
cover = os.path.join(tdir, plugin.name.replace(' ',
'')+'-%s-cover.jpg'%sanitize_file_name2(mi.title.replace(' ',
'_')))
with open(cover, 'wb') as f:
f.write(cdata[-1])
elif results:
cdata = results[0]
cover = os.path.join(tdir, plugin.name.replace(' ',
'')+'-%s-cover.jpg'%sanitize_file_name2(mi.title.replace(' ',
'_')))
with open(cover, 'wb') as f:
f.write(cdata[-1])
prints('Cover downloaded to:', cover)
prints('Cover downloaded to:', cover)
if len(cdata[-1]) < 10240:
prints('Downloaded cover too small')
raise SystemExit(1)
if len(cdata[-1]) < 10240:
prints('Downloaded cover too small')
raise SystemExit(1)
prints('Average time per query', sum(times)/len(times))

View File

@ -111,7 +111,7 @@ class Skeleton(object):
self.chunks = chunks
self.skeleton = self.render(root)
self.body_offset = self.skeleton.find('<body')
self.body_offset = self.skeleton.find(b'<body')
self.calculate_metrics(root)
self.calculate_insert_positions()
@ -127,7 +127,7 @@ class Skeleton(object):
self.metrics = {}
for tag in root.xpath('//*[@aid]'):
text = (tag.text or '').encode('utf-8')
raw = tostring(tag, with_tail=True)
raw = close_self_closing_tags(tostring(tag, with_tail=True))
start_length = len(raw.partition(b'>')[0]) + len(text) + 1
end_length = len(raw.rpartition(b'<')[-1]) + 1
self.metrics[tag.get('aid')] = Metric(start_length, end_length)

View File

@ -15,15 +15,6 @@ log = (args...) -> # {{{
process.stdout.write(msg + '\n')
# }}}
body_height = () -> # {{{
db = document.body
dde = document.documentElement
if db? and dde?
return Math.max(db.scrollHeight, dde.scrollHeight, db.offsetHeight,
dde.offsetHeight, db.clientHeight, dde.clientHeight)
return 0
# }}}
window_scroll_pos = (win=window) -> # {{{
if typeof(win.pageXOffset) == 'number'
x = win.pageXOffset
@ -59,12 +50,12 @@ absleft = (elem) -> # {{{
# }}}
class PagedDisplay
###
This class is a namespace to expose functions via the
window.paged_display object. The most important functions are:
layout(): causes the currently loaded document to be laid out in columns.
###
# This class is a namespace to expose functions via the
# window.paged_display object. The most important functions are:
#
# set_geometry(): sets the parameters used to layout text in paged mode
#
# layout(): causes the currently loaded document to be laid out in columns.
constructor: () ->
if not this instanceof arguments.callee
@ -74,35 +65,42 @@ class PagedDisplay
this.screen_width = 0
this.in_paged_mode = false
this.current_margin_side = 0
this.is_full_screen_layout = false
set_geometry: (cols_per_screen=2, margin_top=20, margin_side=40, margin_bottom=20) ->
set_geometry: (cols_per_screen=1, margin_top=20, margin_side=40, margin_bottom=20) ->
this.margin_top = margin_top
this.margin_side = margin_side
this.margin_bottom = margin_bottom
this.cols_per_screen = cols_per_screen
layout: () ->
# Remove the top margin from the first child of body as that gets added
# to the top margin of body. This is done here just in case
# getComputedStyle() causes a relayout. The re-layout will be much
# faster before we implement the multi-column layout.
for node in document.body.childNodes
if node.nodeType == 1 # Element node
style = window.getComputedStyle(node)
if style.display in ['block', 'table']
node.style.setProperty('margin-top', '0px')
break
body_style = window.getComputedStyle(document.body)
# When laying body out in columns, webkit bleeds the top margin of the
# first block element out above the columns, leading to an extra top
# margin for the page. We compensate for that here. Computing the
# boundingrect of body is very expensive with column layout, so we do
# it before the column layout is applied.
first_layout = false
if not this.in_paged_mode
document.body.style.marginTop = '0px'
extra_margin = document.body.getBoundingClientRect().top
margin_top = (this.margin_top - extra_margin) + 'px'
# Check if the current document is a full screen layout like
# cover, if so we treat it specially.
single_screen = (document.body.scrollWidth < window.innerWidth + 25 and document.body.scrollHeight < window.innerHeight + 25)
first_layout = true
else
# resize event
margin_top = body_style.marginTop
ww = window.innerWidth
wh = window.innerHeight
body_height = wh - this.margin_bottom = this.margin_top
n = this.cols_per_screen
# Calculate the column width so that cols_per_screen columns fit in the
# window in such a way the right margin of the last column is <=
# side_margin (it may be less if the window width is not a
# multiple of n*(col_width+2*side_margin).
n = this.cols_per_screen
adjust = ww - Math.floor(ww/n)*n
# Ensure that the margins are large enough that the adjustment does not
# cause them to become negative semidefinite
@ -113,7 +111,6 @@ class PagedDisplay
this.page_width = col_width + 2*sm
this.screen_width = this.page_width * this.cols_per_screen
body_style = window.getComputedStyle(document.body)
fgcolor = body_style.getPropertyValue('color')
bs = document.body.style
@ -121,9 +118,9 @@ class PagedDisplay
bs.setProperty('-webkit-column-width', col_width+'px')
bs.setProperty('-webkit-column-rule-color', fgcolor)
bs.setProperty('overflow', 'visible')
bs.setProperty('height', 'auto')
bs.setProperty('width', 'auto')
bs.setProperty('margin-top', this.margin_top+'px')
bs.setProperty('height', (window.innerHeight - this.margin_top - this.margin_bottom) + 'px')
bs.setProperty('width', (window.innerWidth - 2*sm)+'px')
bs.setProperty('margin-top', margin_top)
bs.setProperty('margin-bottom', this.margin_bottom+'px')
bs.setProperty('margin-left', sm+'px')
bs.setProperty('margin-right', sm+'px')
@ -146,6 +143,15 @@ class PagedDisplay
priority = rule.style.getPropertyPriority(prop)
rule.style.setProperty(cprop, val, priority)
if first_layout
# Because of a bug in webkit column mode, svg elements defined with
# width 100% are wider than body and lead to a blank page after the
# current page (when cols_per_screen == 1). Similarly img elements
# with height=100% overflow the first column
has_svg = document.getElementsByTagName('svg').length > 0
only_img = document.getElementsByTagName('img').length == 1 and document.getElementsByTagName('div').length < 2 and document.getElementsByTagName('p').length < 2
this.is_full_screen_layout = (only_img or has_svg) and single_screen and document.body.scrollWidth > document.body.clientWidth
this.in_paged_mode = true
this.current_margin_side = sm
return sm
@ -155,18 +161,48 @@ class PagedDisplay
xpos = Math.floor(document.body.scrollWidth * frac)
this.scroll_to_xpos(xpos)
scroll_to_xpos: (xpos) ->
scroll_to_xpos: (xpos, animated=false, notify=false, duration=1000) ->
# Scroll so that the column containing xpos is the left most column in
# the viewport
if typeof(xpos) != 'number'
log(xpos, 'is not a number, cannot scroll to it!')
return
if this.is_full_screen_layout
window.scrollTo(0, 0)
return
pos = 0
until (pos <= xpos < pos + this.page_width)
pos += this.page_width
limit = document.body.scrollWidth - this.screen_width
pos = limit if pos > limit
window.scrollTo(pos, 0)
if animated
this.animated_scroll(pos, duration=1000, notify=notify)
else
window.scrollTo(pos, 0)
animated_scroll: (pos, duration=1000, notify=true) ->
# Scroll the window to X-position pos in an animated fashion over
# duration milliseconds. If notify is true, py_bridge.animated_scroll_done is
# called.
delta = pos - window.pageXOffset
interval = 50
steps = Math.floor(duration/interval)
step_size = Math.floor(delta/steps)
this.current_scroll_animation = {target:pos, step_size:step_size, interval:interval, notify:notify, fn: () =>
a = this.current_scroll_animation
npos = window.pageXOffset + a.step_size
completed = false
if Math.abs(npos - a.target) < Math.abs(a.step_size)
completed = true
npos = a.target
window.scrollTo(npos, 0)
if completed
if notify
window.py_bridge.animated_scroll_done()
else
setTimeout(a.fn, a.interval)
}
this.current_scroll_animation.fn()
current_pos: (frac) ->
# The current scroll position as a fraction between 0 and 1
@ -178,6 +214,8 @@ class PagedDisplay
current_column_location: () ->
# The location of the left edge of the left most column currently
# visible in the viewport
if this.is_full_screen_layout
return 0
x = window.pageXOffset + Math.max(10, this.current_margin_side)
edge = Math.floor(x/this.page_width) * this.page_width
while edge < x
@ -187,6 +225,8 @@ class PagedDisplay
next_screen_location: () ->
# The position to scroll to for the next screen (which could contain
# more than one pages). Returns -1 if no further scrolling is possible.
if this.is_full_screen_layout
return -1
cc = this.current_column_location()
ans = cc + this.screen_width
limit = document.body.scrollWidth - window.innerWidth
@ -197,6 +237,8 @@ class PagedDisplay
previous_screen_location: () ->
# The position to scroll to for the previous screen (which could contain
# more than one pages). Returns -1 if no further scrolling is possible.
if this.is_full_screen_layout
return -1
cc = this.current_column_location()
ans = cc - this.screen_width
if ans < 0
@ -209,6 +251,8 @@ class PagedDisplay
# The position to scroll to for the next column (same as
# next_screen_location() if columns per screen == 1). Returns -1 if no
# further scrolling is possible.
if this.is_full_screen_layout
return -1
cc = this.current_column_location()
ans = cc + this.page_width
limit = document.body.scrollWidth - window.innerWidth
@ -220,6 +264,8 @@ class PagedDisplay
# The position to scroll to for the previous column (same as
# previous_screen_location() if columns per screen == 1). Returns -1 if
# no further scrolling is possible.
if this.is_full_screen_layout
return -1
cc = this.current_column_location()
ans = cc - this.page_width
if ans < 0
@ -308,8 +354,7 @@ if window?
window.paged_display = new PagedDisplay()
# TODO:
# Go to reference positions
# Indexing
# Resizing of images
# Special handling for identifiable covers (colspan)?
# Full screen mode
# Highlight on jump_to_anchor

View File

@ -105,14 +105,14 @@ class UniqueFilenames(object): # {{{
base, ext = posixpath.splitext(item.href)
nhref = base + suffix + ext
nhref = oeb.manifest.generate(href=nhref)[1]
spine_pos = item.spine_position
oeb.manifest.remove(item)
nitem = oeb.manifest.add(item.id, nhref, item.media_type, data=data,
fallback=item.fallback)
self.seen_filenames.add(posixpath.basename(nhref))
self.rename_map[item.href] = nhref
if item.spine_position is not None:
oeb.spine.insert(item.spine_position, nitem, item.linear)
oeb.spine.remove(item)
oeb.manifest.remove(item)
if spine_pos is not None:
oeb.spine.insert(spine_pos, nitem, item.linear)
else:
self.seen_filenames.add(fname)

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
'''
@ -87,11 +87,6 @@ def get_pdf_printer(opts, for_comic=False, output_file_name=None):
return printer
def get_printer_page_size(opts, for_comic=False):
printer = get_pdf_printer(opts, for_comic=for_comic)
size = printer.paperSize(QPrinter.Millimeter)
return size.width() / 10., size.height() / 10.
def draw_image_page(printer, painter, p, preserve_aspect_ratio=True):
page_rect = printer.pageRect()
if preserve_aspect_ratio:
@ -138,13 +133,16 @@ class PDFWriter(QObject): # {{{
self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
self.view.loadFinished.connect(self._render_html,
type=Qt.QueuedConnection)
for x in (Qt.Horizontal, Qt.Vertical):
self.view.page().mainFrame().setScrollBarPolicy(x,
Qt.ScrollBarAlwaysOff)
self.render_queue = []
self.combine_queue = []
self.tmp_path = PersistentTemporaryDirectory(u'_pdf_output_parts')
self.opts = opts
self.size = get_printer_page_size(opts)
self.cover_data = cover_data
self.paged_js = None
def dump(self, items, out_stream, pdf_metadata):
self.metadata = pdf_metadata
@ -176,19 +174,46 @@ class PDFWriter(QObject): # {{{
if ok:
item_path = os.path.join(self.tmp_path, '%i.pdf' % len(self.combine_queue))
self.logger.debug('\tRendering item %s as %i.pdf' % (os.path.basename(str(self.view.url().toLocalFile())), len(self.combine_queue)))
printer = get_pdf_printer(self.opts, output_file_name=item_path)
self.view.page().mainFrame().evaluateJavaScript('''
document.body.style.backgroundColor = "white";
''')
self.view.print_(printer)
printer.abort()
self.do_paged_render(item_path)
else:
# The document is so corrupt that we can't render the page.
self.loop.exit(0)
raise Exception('Document cannot be rendered.')
self._render_book()
def do_paged_render(self, outpath):
from PyQt4.Qt import QSize, QPainter
if self.paged_js is None:
from calibre.utils.resources import compiled_coffeescript
self.paged_js = compiled_coffeescript('ebooks.oeb.display.paged',
dynamic=False)
printer = get_pdf_printer(self.opts, output_file_name=outpath)
painter = QPainter(printer)
zoomx = printer.logicalDpiX()/self.view.logicalDpiX()
zoomy = printer.logicalDpiY()/self.view.logicalDpiY()
painter.scale(zoomx, zoomy)
pr = printer.pageRect()
evaljs = self.view.page().mainFrame().evaluateJavaScript
evaljs(self.paged_js)
self.view.page().setViewportSize(QSize(pr.width()/zoomx,
pr.height()/zoomy))
evaljs('''
document.body.style.backgroundColor = "white";
paged_display.set_geometry(1, 0, 0, 0);
paged_display.layout();
''')
mf = self.view.page().mainFrame()
while True:
mf.render(painter)
nsl = evaljs('paged_display.next_screen_location()').toInt()
if not nsl[1] or nsl[0] <= 0: break
evaljs('window.scrollTo(%d, 0)'%nsl[0])
printer.newPage()
painter.end()
printer.abort()
def _delete_tmpdir(self):
if os.path.exists(self.tmp_path):
shutil.rmtree(self.tmp_path, True)
@ -237,7 +262,6 @@ class ImagePDFWriter(object):
def __init__(self, opts, log, cover_data=None):
self.opts = opts
self.log = log
self.size = get_printer_page_size(opts, for_comic=True)
def dump(self, items, out_stream, pdf_metadata):
f = PersistentTemporaryFile('_comic2pdf.pdf')

View File

@ -329,10 +329,11 @@ class AddAction(InterfaceAction):
x.decode(preferred_encoding, 'replace') for x in
self._adder.merged_books])
info_dialog(self.gui, _('Merged some books'),
_('The following duplicate books were found and incoming '
'book formats were processed and merged into your '
'Calibre database according to your automerge '
'settings:'), det_msg=books, show=True)
_('The following %d duplicate books were found and incoming '
'book formats were processed and merged into your '
'Calibre database according to your automerge '
'settings:')%len(self._adder.merged_books),
det_msg=books, show=True)
if getattr(self._adder, 'number_of_books_added', 0) > 0 or \
getattr(self._adder, 'merged_books', False):

View File

@ -116,6 +116,9 @@ class EditorWidget(QWebView): # {{{
ss = extra_shortcuts.get(wac, None)
if ss:
ac.setShortcut(QKeySequence(getattr(QKeySequence, ss)))
if wac == 'RemoveFormat':
ac.triggered.connect(self.remove_format_cleanup,
type=Qt.QueuedConnection)
self.action_color = QAction(QIcon(I('format-text-color')), _('Foreground color'),
self)
@ -227,6 +230,9 @@ class EditorWidget(QWebView): # {{{
js = 'document.execCommand("%s", false, null);' % cmd
frame.evaluateJavaScript(js)
def remove_format_cleanup(self):
self.html = self.html
@dynamic_property
def html(self):

View File

@ -323,6 +323,10 @@ def communicate(opts, args):
if opts.shutdown_running_calibre:
t.conn.send('shutdown:')
from calibre.utils.lock import singleinstance
prints(_('Shutdown command sent, waiting for shutdown...'))
while not singleinstance('calibre GUI'):
time.sleep(0.1)
else:
if len(args) > 1:
args[1] = os.path.abspath(args[1])

View File

@ -205,6 +205,8 @@ class Document(QWebPage): # {{{
return self.anchor_positions
def switch_to_paged_mode(self, onresize=False):
if onresize and not self.loaded_javascript:
return
side_margin = self.javascript('window.paged_display.layout()', typ=int)
# Setup the contents size to ensure that there is a right most margin.
# Without this webkit renders the final column with no margin, as the
@ -294,6 +296,7 @@ class Document(QWebPage): # {{{
self.mainFrame().setScrollPosition(QPoint(x, y))
def jump_to_anchor(self, anchor):
if not self.loaded_javascript: return
self.javascript('window.paged_display.jump_to_anchor("%s")'%anchor)
def element_ypos(self, elem):
@ -352,7 +355,7 @@ class Document(QWebPage): # {{{
except ZeroDivisionError:
return 0.
def fset(self, val):
if self.in_paged_mode:
if self.in_paged_mode and self.loaded_javascript:
self.javascript('paged_display.scroll_to_pos(%f)'%val)
else:
npos = val * (self.height - self.window_height)

View File

@ -138,7 +138,9 @@ class Reference(QLineEdit):
self.editingFinished.connect(self.editing_finished)
def editing_finished(self):
self.goto.emit(unicode(self.text()))
text = unicode(self.text())
self.setText('')
self.goto.emit(text)
class RecentAction(QAction):
@ -411,10 +413,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
return c.remember_current_page
def print_book(self):
Printing(self.iterator.spine, False)
p = Printing(self.iterator, self)
p.start_print()
def print_preview(self):
Printing(self.iterator.spine, True)
p = Printing(self.iterator, self)
p.start_preview()
def toggle_fullscreen(self, x):
if self.isFullScreen():

View File

@ -1,127 +1,104 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys, urlparse
from BeautifulSoup import BeautifulSoup, Tag
from PyQt4 import QtCore
from PyQt4.Qt import QUrl, QEventLoop, SIGNAL, QObject, Qt, \
QPrinter, QPrintPreviewDialog, QPrintDialog, QDialog, QMetaObject, Q_ARG
from PyQt4.Qt import (QObject, QEventLoop, Qt, QPrintDialog, QPainter, QSize,
QPrintPreviewDialog)
from PyQt4.QtWebKit import QWebView
PRINTCSS = 'body{width:100%;margin:0;padding:0;font-family:Arial;color:#000;background:none;font-size:12pt;text-align:left;}h1,h2,h3,h4,h5,h6{font-family:Helvetica;}h1{font-size:19pt;}h2{font-size:17pt;}h3{font-size:15pt;}h4,h5,h6{font-size:12pt;}pre,code,samp{font:10ptCourier,monospace;white-space:pre-wrap;page-break-inside:avoid;}blockquote{margin:1.3em;padding:1em;font-size:10pt;}hr{background-color:#ccc;}aimg{border:none;}a:link,a:visited{background:transparent;font-weight:700;text-decoration:underline;color:#333;}a:link:after,a{color:#000;}table{margin:1px;text-align:left;}th{border-bottom:1pxsolid#333;font-weight:bold;}td{border-bottom:1pxsolid#333;}th,td{padding:4px10px4px0;}tfoot{font-style:italic;}caption{background:#fff;margin-bottom:2em;text-align:left;}thead{display:table-header-group;}tr{page-break-inside:avoid;}#header,.header,#footer,.footer,#navbar,.navbar,#navigation,.navigation,#rightSideBar,.rightSideBar,#leftSideBar,.leftSideBar{display:none;}'
from calibre.gui2 import error_dialog
from calibre.ebooks.oeb.display.webview import load_html
from calibre.utils.resources import compiled_coffeescript
class Printing(QObject):
def __init__(self, spine, preview):
from calibre.gui2 import is_ok_to_use_qt
if not is_ok_to_use_qt():
raise Exception('Not OK to use Qt')
QObject.__init__(self)
self.loop = QEventLoop()
self.view = QWebView()
if preview:
self.connect(self.view, SIGNAL('loadFinished(bool)'), self.print_preview)
else:
self.connect(self.view, SIGNAL('loadFinished(bool)'), self.print_book)
def __init__(self, iterator, parent):
QObject.__init__(self, parent)
self.current_index = 0
self.iterator = iterator
self.view = QWebView(self.parent())
self.mf = mf = self.view.page().mainFrame()
for x in (Qt.Horizontal, Qt.Vertical):
mf.setScrollBarPolicy(x, Qt.ScrollBarAlwaysOff)
self.view.loadFinished.connect(self.load_finished)
self.paged_js = compiled_coffeescript('ebooks.oeb.display.paged',
dynamic=False)
self.process_content(spine)
def load_finished(self, ok):
self.loaded_ok = ok
def process_content(self, spine):
content = ''
def start_print(self):
self.pd = QPrintDialog(self.parent())
self.pd.open(self._start_print)
for path in spine:
raw = self.raw_content(path)
content += self.parsed_content(raw, path)
def _start_print(self):
self.do_print(self.pd.printer())
refined_content = self.refine_content(content)
def start_preview(self):
self.pd = QPrintPreviewDialog(self.parent())
self.pd.paintRequested.connect(self.do_print)
self.pd.exec_()
base = os.path.splitdrive(spine[0])[0]
base = base if base != '' else '/'
def do_print(self, printer):
painter = QPainter(printer)
zoomx = printer.logicalDpiX()/self.view.logicalDpiX()
zoomy = printer.logicalDpiY()/self.view.logicalDpiY()
painter.scale(zoomx, zoomy)
pr = printer.pageRect()
self.view.page().setViewportSize(QSize(pr.width()/zoomx,
pr.height()/zoomy))
evaljs = self.mf.evaluateJavaScript
loop = QEventLoop(self)
first = True
QMetaObject.invokeMethod(self, "load_content", Qt.QueuedConnection, Q_ARG('QString', refined_content), Q_ARG('QString', base))
self.loop.exec_()
for path in self.iterator.spine:
self.loaded_ok = None
load_html(path, self.view, codec=getattr(path, 'encoding', 'utf-8'),
mime_type=getattr(path, 'mime_type', None))
while self.loaded_ok is None:
loop.processEvents(loop.ExcludeUserInputEvents)
if not self.loaded_ok:
return error_dialog(self.parent(), _('Failed to render'),
_('Failed to render document %s')%path, show=True)
evaljs(self.paged_js)
evaljs('''
document.body.style.backgroundColor = "white";
paged_display.set_geometry(1, 0, 0, 0);
paged_display.layout();
''')
@QtCore.pyqtSignature('load_content(QString, QString)')
def load_content(self, content, base):
self.view.setHtml(content, QUrl(base))
while True:
if not first:
printer.newPage()
first = False
self.mf.render(painter)
nsl = evaljs('paged_display.next_screen_location()').toInt()
if not nsl[1] or nsl[0] <= 0: break
evaljs('window.scrollTo(%d, 0)'%nsl[0])
def raw_content(self, path):
return open(path, 'rb').read().decode(path.encoding)
def parsed_content(self, raw_content, path):
dom_tree = BeautifulSoup(raw_content).body
# Remove sytle information that is applied to the entire document.
# This does not remove styles applied within a tag.
styles = dom_tree.findAll('style')
for s in styles:
s.extract()
scripts = dom_tree.findAll('script')
for s in scripts:
s.extract()
# Convert all relative links to absolute paths.
links = dom_tree.findAll(src=True)
for s in links:
if QUrl(s['src']).isRelative():
s['src'] = urlparse.urljoin(path, s['src'])
links = dom_tree.findAll(href=True)
for s in links:
if QUrl(s['href']).isRelative():
s['href'] = urlparse.urljoin(path, s['href'])
return unicode(dom_tree)
# Adds the begenning and endings tags to the document.
# Adds the print css.
def refine_content(self, content):
dom_tree = BeautifulSoup('<html><head></head><body>%s</body></html>' % content)
css = dom_tree.findAll('link')
for c in css:
c.extract()
print_css = Tag(BeautifulSoup(), 'style', [('type', 'text/css'), ('title', 'override_css')])
print_css.insert(0, PRINTCSS)
dom_tree.findAll('head')[0].insert(0, print_css)
return unicode(dom_tree)
def print_preview(self, ok):
printer = QPrinter(QPrinter.HighResolution)
printer.setPageMargins(1, 1, 1, 1, QPrinter.Inch)
previewDialog = QPrintPreviewDialog(printer)
self.connect(previewDialog, SIGNAL('paintRequested(QPrinter *)'), self.view.print_)
previewDialog.exec_()
self.disconnect(previewDialog, SIGNAL('paintRequested(QPrinter *)'), self.view.print_)
self.loop.quit()
def print_book(self, ok):
printer = QPrinter(QPrinter.HighResolution)
printer.setPageMargins(1, 1, 1, 1, QPrinter.Inch)
printDialog = QPrintDialog(printer)
printDialog.setWindowTitle(_("Print eBook"))
printDialog.exec_()
if printDialog.result() == QDialog.Accepted:
self.view.print_(printer)
self.loop.quit()
def main():
return 0
painter.end()
if __name__ == '__main__':
sys.exit(main())
from calibre.gui2 import Application
from calibre.ebooks.oeb.iterator.book import EbookIterator
from PyQt4.Qt import QPrinter, QTimer
import sys
app = Application([])
def doit():
with EbookIterator(sys.argv[-1]) as it:
p = Printing(it, None)
printer = QPrinter()
of = sys.argv[-1]+'.pdf'
printer.setOutputFileName(of)
p.do_print(printer)
print ('Printed to:', of)
app.exit()
QTimer.singleShot(0, doit)
app.exec_()

View File

@ -6,14 +6,12 @@ Miscellaneous widgets used in the GUI
import re, traceback, os
from PyQt4.Qt import (QIcon, QFont, QLabel, QListWidget, QAction,
QListWidgetItem, QTextCharFormat, QApplication,
QSyntaxHighlighter, QCursor, QColor, QWidget,
QPixmap, QSplitterHandle, QToolButton,
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal,
QRegExp, QSettings, QSize, QSplitter,
QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene,
QMenu, QStringListModel, QCompleter, QStringList,
QTimer, QRect, QFontDatabase, QGraphicsView)
QListWidgetItem, QTextCharFormat, QApplication, QSyntaxHighlighter,
QCursor, QColor, QWidget, QPixmap, QSplitterHandle, QToolButton,
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, QRegExp, QSize,
QSplitter, QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene, QMenu,
QStringListModel, QCompleter, QStringList, QTimer, QRect,
QFontDatabase, QGraphicsView, QByteArray)
from calibre.constants import iswindows
from calibre.gui2 import (NONE, error_dialog, pixmap_to_data, gprefs,
@ -803,69 +801,29 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{
@classmethod
def loadConfig(cls):
Config = cls.Config
settings = QSettings()
def setDefaultString(name, default):
value = settings.value(name).toString()
if value.isEmpty():
value = default
Config[name] = value
for name in ("window", "shell"):
Config["%swidth" % name] = settings.value("%swidth" % name,
QVariant(QApplication.desktop() \
.availableGeometry().width() / 2)).toInt()[0]
Config["%sheight" % name] = settings.value("%sheight" % name,
QVariant(QApplication.desktop() \
.availableGeometry().height() / 2)).toInt()[0]
Config["%sy" % name] = settings.value("%sy" % name,
QVariant(0)).toInt()[0]
Config["toolbars"] = settings.value("toolbars").toByteArray()
Config["splitter"] = settings.value("splitter").toByteArray()
Config["shellx"] = settings.value("shellx", QVariant(0)).toInt()[0]
Config["windowx"] = settings.value("windowx", QVariant(QApplication \
.desktop().availableGeometry().width() / 2)).toInt()[0]
Config["remembergeometry"] = settings.value("remembergeometry",
QVariant(True)).toBool()
Config["startwithshell"] = settings.value("startwithshell",
QVariant(True)).toBool()
Config["showwindowinfo"] = settings.value("showwindowinfo",
QVariant(True)).toBool()
setDefaultString("shellstartup", """\
from __future__ import division
import codecs
import sys
sys.stdin = codecs.getreader("UTF8")(sys.stdin)
sys.stdout = codecs.getwriter("UTF8")(sys.stdout)""")
setDefaultString("newfile", """\
#!/usr/bin/env python
from __future__ import division
import sys
""")
Config["backupsuffix"] = settings.value("backupsuffix",
QVariant(".bak")).toString()
setDefaultString("beforeinput", "#>>>")
setDefaultString("beforeoutput", "#---")
Config["cwd"] = settings.value("cwd", QVariant(".")).toString()
Config["tooltipsize"] = settings.value("tooltipsize",
QVariant(150)).toInt()[0]
Config["maxlinestoscan"] = settings.value("maxlinestoscan",
QVariant(5000)).toInt()[0]
Config["pythondocpath"] = settings.value("pythondocpath",
QVariant("http://docs.python.org")).toString()
Config["autohidefinddialog"] = settings.value("autohidefinddialog",
QVariant(True)).toBool()
Config["findcasesensitive"] = settings.value("findcasesensitive",
QVariant(False)).toBool()
Config["findwholewords"] = settings.value("findwholewords",
QVariant(False)).toBool()
Config["tabwidth"] = settings.value("tabwidth",
QVariant(4)).toInt()[0]
Config["fontfamily"] = settings.value("fontfamily",
QVariant("monospace")).toString()
Config["fontsize"] = settings.value("fontsize",
QVariant(10)).toInt()[0]
Config["%swidth" % name] = QVariant(QApplication.desktop().availableGeometry().width() / 2).toInt()[0]
Config["%sheight" % name] = QVariant(QApplication.desktop().availableGeometry().height() / 2).toInt()[0]
Config["%sy" % name] = QVariant(0).toInt()[0]
Config["toolbars"] = QByteArray(b'')
Config["splitter"] = QByteArray(b'')
Config["shellx"] = QVariant(0).toInt()[0]
Config["windowx"] = QVariant(QApplication.desktop().availableGeometry().width() / 2).toInt()[0]
Config["remembergeometry"] = QVariant(True).toBool()
Config["startwithshell"] = QVariant(True).toBool()
Config["showwindowinfo"] = QVariant(True).toBool()
Config["backupsuffix"] = QVariant(".bak").toString()
Config["cwd"] = QVariant(".").toString()
Config["tooltipsize"] = QVariant(150).toInt()[0]
Config["maxlinestoscan"] = QVariant(5000).toInt()[0]
Config["pythondocpath"] = QVariant("http://docs.python.org").toString()
Config["autohidefinddialog"] = QVariant(True).toBool()
Config["findcasesensitive"] = QVariant(False).toBool()
Config["findwholewords"] = QVariant(False).toBool()
Config["tabwidth"] = QVariant(4).toInt()[0]
Config["fontfamily"] = QVariant("monospace").toString()
Config["fontsize"] = QVariant(10).toInt()[0]
for name, color, bold, italic in (
("normal", "#000000", False, False),
("keyword", "#000080", True, False),
@ -877,12 +835,9 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{
("number", "#924900", False, False),
("error", "#FF0000", False, False),
("pyqt", "#50621A", False, False)):
Config["%sfontcolor" % name] = settings.value(
"%sfontcolor" % name, QVariant(color)).toString()
Config["%sfontbold" % name] = settings.value(
"%sfontbold" % name, QVariant(bold)).toBool()
Config["%sfontitalic" % name] = settings.value(
"%sfontitalic" % name, QVariant(italic)).toBool()
Config["%sfontcolor" % name] = QVariant(color).toString()
Config["%sfontbold" % name] = QVariant(bold).toBool()
Config["%sfontitalic" % name] = QVariant(italic).toBool()
@classmethod

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More