mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
c2b317bab4
@ -20,6 +20,80 @@
|
|||||||
# - title:
|
# - 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
|
- version: 0.8.56
|
||||||
date: 2012-06-15
|
date: 2012-06-15
|
||||||
|
|
||||||
|
@ -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::
|
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.
|
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.
|
It relies on the freely available zip command line tool.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__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
|
24sata.rs
|
||||||
@ -21,26 +22,29 @@ class Ser24Sata(BasicNewsRecipe):
|
|||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'sr'
|
language = 'sr'
|
||||||
publication_type = 'newspaper'
|
publication_type = 'newsportal'
|
||||||
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}'
|
extra_css = """
|
||||||
|
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
|
||||||
|
body{font-family: serif1, serif}
|
||||||
|
"""
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'tags' : category
|
, 'tags' : category
|
||||||
, 'publisher' : publisher
|
, 'publisher': publisher
|
||||||
, 'language' : language
|
, 'language' : language
|
||||||
, 'linearize_tables' : True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||||
|
|
||||||
feeds = [(u'Vesti Dana', u'http://www.24sata.rs/rss.php')]
|
feeds = [
|
||||||
|
(u'Vesti' , u'http://www.24sata.rs/rss/vesti.xml' ),
|
||||||
def preprocess_html(self, soup):
|
(u'Sport' , u'http://www.24sata.rs/rss/sport.xml' ),
|
||||||
return self.adeify_images(soup)
|
(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):
|
def print_version(self, url):
|
||||||
article = url.partition('#')[0]
|
dpart, spart, apart = url.rpartition('/')
|
||||||
article_id = article.partition('id=')[2]
|
return dpart + '/print/' + apart
|
||||||
return 'http://www.24sata.rs/_print.php?id=' + article_id
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
__license__ = 'GPL v3'
|
__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
|
abc.com.py
|
||||||
'''
|
'''
|
||||||
@ -7,7 +7,7 @@ abc.com.py
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class ABC_py(BasicNewsRecipe):
|
class ABC_py(BasicNewsRecipe):
|
||||||
title = 'ABC digital'
|
title = 'ABC Color'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'Noticias de Paraguay y el resto del mundo'
|
description = 'Noticias de Paraguay y el resto del mundo'
|
||||||
publisher = 'ABC'
|
publisher = 'ABC'
|
||||||
@ -15,12 +15,16 @@ class ABC_py(BasicNewsRecipe):
|
|||||||
oldest_article = 2
|
oldest_article = 2
|
||||||
max_articles_per_feed = 200
|
max_articles_per_feed = 200
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
encoding = 'cp1252'
|
encoding = 'utf8'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'es_PY'
|
language = 'es_PY'
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
|
masthead_url = 'http://www.abc.com.py/plantillas/img/abc-logo.png'
|
||||||
publication_type = 'newspaper'
|
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 = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
@ -29,21 +33,19 @@ class ABC_py(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_tags = [dict(name=['form','iframe','embed','object','link','base','table']),dict(attrs={'class':'toolbox'})]
|
remove_tags = [
|
||||||
remove_tags_after = dict(attrs={'class':'date'})
|
dict(name=['form','iframe','embed','object','link','base','table']),
|
||||||
keep_only_tags = [dict(attrs={'class':'zcontent'})]
|
dict(attrs={'class':['es-carousel-wrapper']}),
|
||||||
|
dict(attrs={'id':['tools','article-banner-1']})
|
||||||
|
]
|
||||||
|
keep_only_tags = [dict(attrs={'id':'article'})]
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Ultimo momento' , u'http://www.abc.com.py/ultimo-momento.xml' )
|
(u'Ultimo momento', u'http://www.abc.com.py/rss.xml' )
|
||||||
,(u'Nacionales' , u'http://www.abc.com.py/nacionales.xml' )
|
,(u'Nacionales' , u'http://www.abc.com.py/nacionales/rss.xml' )
|
||||||
,(u'Internacionales' , u'http://www.abc.com.py/internacionales.xml' )
|
,(u'Mundo' , u'http://www.abc.com.py/internacionales/rss.xml')
|
||||||
,(u'Deportes' , u'http://www.abc.com.py/deportes.xml' )
|
,(u'Deportes' , u'http://www.abc.com.py/deportes/rss.xml' )
|
||||||
,(u'Espectaculos' , u'http://www.abc.com.py/espectaculos.xml' )
|
,(u'Espectaculos' , u'http://www.abc.com.py/espectaculos/rss.xml' )
|
||||||
,(u'Ciencia y Tecnologia', u'http://www.abc.com.py/ciencia-y-tecnologia.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
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
__license__ = 'GPL v3'
|
__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
|
akter.co.rs
|
||||||
'''
|
'''
|
||||||
@ -8,7 +8,7 @@ import re
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Akter(BasicNewsRecipe):
|
class Akter(BasicNewsRecipe):
|
||||||
title = 'AKTER'
|
title = 'AKTER - Nedeljnik'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'AKTER - nedeljni politicki magazin savremene Srbije'
|
description = 'AKTER - nedeljni politicki magazin savremene Srbije'
|
||||||
publisher = 'Akter Media Group d.o.o.'
|
publisher = 'Akter Media Group d.o.o.'
|
||||||
@ -18,61 +18,37 @@ class Akter(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'utf-8'
|
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'
|
language = 'sr'
|
||||||
publication_type = 'magazine'
|
publication_type = 'magazine'
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
PREFIX = 'http://www.akter.co.rs'
|
|
||||||
extra_css = """
|
extra_css = """
|
||||||
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||||
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
|
body{font-family: Tahoma,Geneva,sans1,sans-serif}
|
||||||
.color-2{display:block; margin-bottom: 10px; padding: 5px, 10px;
|
img{margin-bottom: 0.8em; display: block;}
|
||||||
border-left: 1px solid #D00000; color: #D00000}
|
"""
|
||||||
img{margin-bottom: 0.8em} """
|
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'tags' : category
|
, 'tags' : category
|
||||||
, 'publisher' : publisher
|
, 'publisher': publisher
|
||||||
, 'language' : language
|
, 'language' : language
|
||||||
, 'linearize_tables' : True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'section_to_print'})]
|
||||||
feeds = [
|
feeds = [(u'Nedeljnik', u'http://akter.co.rs/rss/nedeljnik')]
|
||||||
(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)
|
|
||||||
|
|
||||||
def print_version(self, url):
|
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):
|
def get_cover_url(self):
|
||||||
totalfeeds = []
|
soup = self.index_to_soup('http://www.akter.co.rs/weekly.html')
|
||||||
lfeeds = self.get_feeds()
|
divt = soup.find('div', attrs={'class':'lastissue'})
|
||||||
for feedobj in lfeeds:
|
if divt:
|
||||||
feedtitle, feedurl = feedobj
|
imgt = divt.find('img')
|
||||||
self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl))
|
if imgt:
|
||||||
articles = []
|
return 'http://www.akter.co.rs' + imgt['src']
|
||||||
soup = self.index_to_soup(feedurl)
|
return None
|
||||||
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
|
|
||||||
|
|
||||||
|
44
recipes/akter_dnevnik.recipe
Normal file
44
recipes/akter_dnevnik.recipe
Normal 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
|
@ -147,10 +147,9 @@ class BBCBrasilRecipe(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
# Author of this recipe.
|
# Author of this recipe.
|
||||||
__author__ = 'claviola'
|
__author__ = 'Carlos Laviola'
|
||||||
|
|
||||||
# Specify English as the language of the RSS feeds (ISO-639 code).
|
language = 'pt_BR'
|
||||||
language = 'en_GB'
|
|
||||||
|
|
||||||
# Set tags.
|
# Set tags.
|
||||||
tags = 'news, sport, blog'
|
tags = 'news, sport, blog'
|
||||||
|
@ -20,7 +20,23 @@ class Economist(BasicNewsRecipe):
|
|||||||
INDEX = 'http://www.economist.com/printedition'
|
INDEX = 'http://www.economist.com/printedition'
|
||||||
description = ('Global news and current affairs from a European'
|
description = ('Global news and current affairs from a European'
|
||||||
' perspective. Best downloaded on Friday mornings (GMT)')
|
' 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
|
oldest_article = 7.0
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
||||||
|
@ -20,7 +20,24 @@ class Economist(BasicNewsRecipe):
|
|||||||
INDEX = 'http://www.economist.com/printedition'
|
INDEX = 'http://www.economist.com/printedition'
|
||||||
description = ('Global news and current affairs from a European'
|
description = ('Global news and current affairs from a European'
|
||||||
' perspective. Best downloaded on Friday mornings (GMT)')
|
' 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
|
oldest_article = 7.0
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
||||||
|
56
recipes/marketing_sensoriale.recipe
Normal file
56
recipes/marketing_sensoriale.recipe
Normal 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')]
|
||||||
|
|
@ -10,11 +10,11 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
|
|
||||||
class OGlobo(BasicNewsRecipe):
|
class OGlobo(BasicNewsRecipe):
|
||||||
title = 'O Globo'
|
title = 'O Globo'
|
||||||
__author__ = 'Darko Miletic and Sujata Raman'
|
__author__ = 'Darko Miletic and Carlos Laviola'
|
||||||
description = 'News from Brasil'
|
description = 'News from Brasil'
|
||||||
publisher = 'O Globo'
|
publisher = 'O Globo'
|
||||||
category = 'news, politics, Brasil'
|
category = 'news, politics, Brasil'
|
||||||
oldest_article = 2
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
@ -39,43 +39,35 @@ class OGlobo(BasicNewsRecipe):
|
|||||||
.commentario p{color:#007BB5; font-style:italic;}
|
.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 = [
|
remove_tags = [
|
||||||
dict(name='script')
|
dict(name='script')
|
||||||
,dict(name='object')
|
|
||||||
,dict(name='form')
|
,dict(name='form')
|
||||||
,dict(name='div', attrs={'id':['linksPatGoogle','rdpm','cor','com','env','rcm_st','coment',]})
|
,dict(name='div', attrs={'id':'header'})
|
||||||
,dict(name='div', attrs={'class':'box-zap-anu2'})
|
,dict(name='p', attrs={'id':'info-date-press'})
|
||||||
,dict(name='a', attrs={'class':'assine'})
|
|
||||||
,dict(name='link')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Todos os canais', u'http://oglobo.globo.com/rss/plantao.xml')
|
(u'Todos os canais', u'http://oglobo.globo.com/rss.xml?completo=true')
|
||||||
,(u'Ciencia', u'http://oglobo.globo.com/rss/plantaociencia.xml')
|
,(u'Ciencia', u'http://oglobo.globo.com/rss.xml?secao=ciencia&completo=true')
|
||||||
,(u'Educacao', u'http://oglobo.globo.com/rss/plantaoeducacao.xml')
|
,(u'Educacao', u'http://oglobo.globo.com/rss.xml?secao=educacao&completo=true')
|
||||||
,(u'Opiniao', u'http://oglobo.globo.com/rss/plantaoopiniao.xml')
|
,(u'Opiniao', u'http://oglobo.globo.com/rss.xml?secao=opiniao&completo=true')
|
||||||
,(u'Sao Paulo', u'http://oglobo.globo.com/rss/plantaosaopaulo.xml')
|
,(u'Cultura', u'http://oglobo.globo.com/rss.xml?secao=cultura&completo=true')
|
||||||
,(u'Viagem', u'http://oglobo.globo.com/rss/plantaoviagem.xml')
|
,(u'Esportes', u'http://oglobo.globo.com/rss.xml?secao=esportes&completo=true')
|
||||||
,(u'Cultura', u'http://oglobo.globo.com/rss/plantaocultura.xml')
|
,(u'Mundo', u'http://oglobo.globo.com/rss.xml?secao=mundo&completo=true')
|
||||||
,(u'Esportes', u'http://oglobo.globo.com/rss/plantaoesportes.xml')
|
,(u'Pais', u'http://oglobo.globo.com/rss.xml?secao=pais&completo=true')
|
||||||
,(u'Mundo', u'http://oglobo.globo.com/rss/plantaomundo.xml')
|
,(u'Rio', u'http://oglobo.globo.com/rss.xml?secao=rio&completo=true')
|
||||||
,(u'Pais', u'http://oglobo.globo.com/rss/plantaopais.xml')
|
,(u'Saude', u'http://oglobo.globo.com/rss.xml?secao=saude&completo=true')
|
||||||
,(u'Rio', u'http://oglobo.globo.com/rss/plantaorio.xml')
|
,(u'Economia', u'http://oglobo.globo.com/rss.xml?secao=economia&completo=true')
|
||||||
,(u'Saude', u'http://oglobo.globo.com/rss/plantaosaude.xml')
|
,(u'Tecnologia', u'http://oglobo.globo.com/rss.xml?secao=tecnologia&completo=true')
|
||||||
,(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')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?service=print'
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
language = 'pt'
|
language = 'pt_BR'
|
||||||
|
|
||||||
|
Binary file not shown.
@ -60,7 +60,12 @@ function goto_reference(ref) {
|
|||||||
if (num < 0) {alert("Invalid reference: "+ref); return;}
|
if (num < 0) {alert("Invalid reference: "+ref); return;}
|
||||||
var p = $("p");
|
var p = $("p");
|
||||||
if (num >= p.length) {alert("Reference not found: "+ref); return;}
|
if (num >= p.length) {alert("Reference not found: "+ref); return;}
|
||||||
$.scrollTo($(p[num]), 1000,
|
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()}});
|
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,15 @@ def get_rsync_pw():
|
|||||||
return open('/home/kovid/work/kde/conf/buildbot').read().partition(
|
return open('/home/kovid/work/kde/conf/buildbot').read().partition(
|
||||||
':')[-1].strip()
|
':')[-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):
|
class Rsync(Command):
|
||||||
|
|
||||||
description = 'Sync source tree from development machine'
|
description = 'Sync source tree from development machine'
|
||||||
@ -46,14 +55,16 @@ class Push(Command):
|
|||||||
def run(self, opts):
|
def run(self, opts):
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
threads = []
|
threads = []
|
||||||
for host in (
|
for host, vmname in {
|
||||||
r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre',
|
r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre':'winxp',
|
||||||
'kovid@ox:calibre',
|
'kovid@ox:calibre':None,
|
||||||
r'kovid@win7:/cygdrive/c/Users/kovid/calibre',
|
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]
|
rcmd = BASE_RSYNC + EXCLUDES + ['.', host]
|
||||||
print '\n\nPushing to:', host, '\n'
|
print '\n\nPushing to:', vmname or host, '\n'
|
||||||
threads.append(Thread(target=subprocess.check_call, args=(rcmd,)))
|
threads.append(Thread(target=subprocess.check_call, args=(rcmd,),
|
||||||
|
kwargs={'stdout':open(os.devnull, 'wb')}))
|
||||||
threads[-1].start()
|
threads[-1].start()
|
||||||
for thread in threads:
|
for thread in threads:
|
||||||
thread.join()
|
thread.join()
|
||||||
@ -118,13 +129,7 @@ class VMInstaller(Command):
|
|||||||
|
|
||||||
|
|
||||||
def run_vm(self):
|
def run_vm(self):
|
||||||
pat = '/%s/'%(self.VM_CHECK or self.VM_NAME)
|
if is_vm_running(self.VM_CHECK or self.VM_NAME): return
|
||||||
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
|
|
||||||
|
|
||||||
self.__p = subprocess.Popen([self.vm])
|
self.__p = subprocess.Popen([self.vm])
|
||||||
|
|
||||||
def start_vm(self, sleep=75):
|
def start_vm(self, sleep=75):
|
||||||
|
@ -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
|
-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.
|
Add the path to the bin folder inside the Qt dir to your system PATH.
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ PyQt4
|
|||||||
|
|
||||||
Compiling instructions::
|
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
|
||||||
nmake install
|
nmake install
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,31 +13,31 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
||||||
"devel@lists.alioth.debian.org>\n"
|
"devel@lists.alioth.debian.org>\n"
|
||||||
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
||||||
"PO-Revision-Date: 2011-09-27 18:14+0000\n"
|
"PO-Revision-Date: 2012-06-14 09:06+0000\n"
|
||||||
"Last-Translator: Kovid Goyal <Unknown>\n"
|
"Last-Translator: Eugene Marshal <Unknown>\n"
|
||||||
"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
|
"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2011-11-26 05:35+0000\n"
|
"X-Launchpad-Export-Date: 2012-06-15 04:42+0000\n"
|
||||||
"X-Generator: Launchpad (build 14381)\n"
|
"X-Generator: Launchpad (build 15414)\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
|
|
||||||
#. name for aaa
|
#. name for aaa
|
||||||
msgid "Ghotuo"
|
msgid "Ghotuo"
|
||||||
msgstr ""
|
msgstr "Гхотуо"
|
||||||
|
|
||||||
#. name for aab
|
#. name for aab
|
||||||
msgid "Alumu-Tesu"
|
msgid "Alumu-Tesu"
|
||||||
msgstr ""
|
msgstr "Алуму-тесу"
|
||||||
|
|
||||||
#. name for aac
|
#. name for aac
|
||||||
msgid "Ari"
|
msgid "Ari"
|
||||||
msgstr ""
|
msgstr "Ари"
|
||||||
|
|
||||||
#. name for aad
|
#. name for aad
|
||||||
msgid "Amal"
|
msgid "Amal"
|
||||||
msgstr ""
|
msgstr "Амал"
|
||||||
|
|
||||||
#. name for aae
|
#. name for aae
|
||||||
msgid "Albanian; Arbëreshë"
|
msgid "Albanian; Arbëreshë"
|
||||||
@ -45,11 +45,11 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for aaf
|
#. name for aaf
|
||||||
msgid "Aranadan"
|
msgid "Aranadan"
|
||||||
msgstr ""
|
msgstr "Аранадан"
|
||||||
|
|
||||||
#. name for aag
|
#. name for aag
|
||||||
msgid "Ambrak"
|
msgid "Ambrak"
|
||||||
msgstr ""
|
msgstr "Амбрак"
|
||||||
|
|
||||||
#. name for aah
|
#. name for aah
|
||||||
msgid "Arapesh; Abu'"
|
msgid "Arapesh; Abu'"
|
||||||
@ -57,23 +57,23 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for aai
|
#. name for aai
|
||||||
msgid "Arifama-Miniafia"
|
msgid "Arifama-Miniafia"
|
||||||
msgstr ""
|
msgstr "Арифама-Миниафиа"
|
||||||
|
|
||||||
#. name for aak
|
#. name for aak
|
||||||
msgid "Ankave"
|
msgid "Ankave"
|
||||||
msgstr ""
|
msgstr "Анкаве"
|
||||||
|
|
||||||
#. name for aal
|
#. name for aal
|
||||||
msgid "Afade"
|
msgid "Afade"
|
||||||
msgstr ""
|
msgstr "Афаде"
|
||||||
|
|
||||||
#. name for aam
|
#. name for aam
|
||||||
msgid "Aramanik"
|
msgid "Aramanik"
|
||||||
msgstr ""
|
msgstr "Араманик"
|
||||||
|
|
||||||
#. name for aan
|
#. name for aan
|
||||||
msgid "Anambé"
|
msgid "Anambé"
|
||||||
msgstr ""
|
msgstr "Анамбе"
|
||||||
|
|
||||||
#. name for aao
|
#. name for aao
|
||||||
msgid "Arabic; Algerian Saharan"
|
msgid "Arabic; Algerian Saharan"
|
||||||
@ -93,7 +93,7 @@ msgstr "Афар"
|
|||||||
|
|
||||||
#. name for aas
|
#. name for aas
|
||||||
msgid "Aasáx"
|
msgid "Aasáx"
|
||||||
msgstr ""
|
msgstr "Асакс"
|
||||||
|
|
||||||
#. name for aat
|
#. name for aat
|
||||||
msgid "Albanian; Arvanitika"
|
msgid "Albanian; Arvanitika"
|
||||||
@ -101,27 +101,27 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for aau
|
#. name for aau
|
||||||
msgid "Abau"
|
msgid "Abau"
|
||||||
msgstr ""
|
msgstr "Абау"
|
||||||
|
|
||||||
#. name for aaw
|
#. name for aaw
|
||||||
msgid "Solong"
|
msgid "Solong"
|
||||||
msgstr ""
|
msgstr "Солонг"
|
||||||
|
|
||||||
#. name for aax
|
#. name for aax
|
||||||
msgid "Mandobo Atas"
|
msgid "Mandobo Atas"
|
||||||
msgstr ""
|
msgstr "Мандобо Атас"
|
||||||
|
|
||||||
#. name for aaz
|
#. name for aaz
|
||||||
msgid "Amarasi"
|
msgid "Amarasi"
|
||||||
msgstr ""
|
msgstr "Амараси"
|
||||||
|
|
||||||
#. name for aba
|
#. name for aba
|
||||||
msgid "Abé"
|
msgid "Abé"
|
||||||
msgstr ""
|
msgstr "Абе"
|
||||||
|
|
||||||
#. name for abb
|
#. name for abb
|
||||||
msgid "Bankon"
|
msgid "Bankon"
|
||||||
msgstr ""
|
msgstr "Банкон"
|
||||||
|
|
||||||
#. name for abc
|
#. name for abc
|
||||||
msgid "Ayta; Ambala"
|
msgid "Ayta; Ambala"
|
||||||
@ -129,7 +129,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for abd
|
#. name for abd
|
||||||
msgid "Manide"
|
msgid "Manide"
|
||||||
msgstr ""
|
msgstr "Мэнайд"
|
||||||
|
|
||||||
#. name for abe
|
#. name for abe
|
||||||
msgid "Abnaki; Western"
|
msgid "Abnaki; Western"
|
||||||
@ -137,11 +137,11 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for abf
|
#. name for abf
|
||||||
msgid "Abai Sungai"
|
msgid "Abai Sungai"
|
||||||
msgstr ""
|
msgstr "Абаи Сунгаи"
|
||||||
|
|
||||||
#. name for abg
|
#. name for abg
|
||||||
msgid "Abaga"
|
msgid "Abaga"
|
||||||
msgstr ""
|
msgstr "Абага"
|
||||||
|
|
||||||
#. name for abh
|
#. name for abh
|
||||||
msgid "Arabic; Tajiki"
|
msgid "Arabic; Tajiki"
|
||||||
@ -149,11 +149,11 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for abi
|
#. name for abi
|
||||||
msgid "Abidji"
|
msgid "Abidji"
|
||||||
msgstr ""
|
msgstr "Абиджи"
|
||||||
|
|
||||||
#. name for abj
|
#. name for abj
|
||||||
msgid "Aka-Bea"
|
msgid "Aka-Bea"
|
||||||
msgstr ""
|
msgstr "Ака-Беа"
|
||||||
|
|
||||||
#. name for abk
|
#. name for abk
|
||||||
msgid "Abkhazian"
|
msgid "Abkhazian"
|
||||||
@ -161,19 +161,19 @@ msgstr "Абхазский"
|
|||||||
|
|
||||||
#. name for abl
|
#. name for abl
|
||||||
msgid "Lampung Nyo"
|
msgid "Lampung Nyo"
|
||||||
msgstr ""
|
msgstr "Лампунг Ньё"
|
||||||
|
|
||||||
#. name for abm
|
#. name for abm
|
||||||
msgid "Abanyom"
|
msgid "Abanyom"
|
||||||
msgstr ""
|
msgstr "Абанйом"
|
||||||
|
|
||||||
#. name for abn
|
#. name for abn
|
||||||
msgid "Abua"
|
msgid "Abua"
|
||||||
msgstr ""
|
msgstr "Абуа"
|
||||||
|
|
||||||
#. name for abo
|
#. name for abo
|
||||||
msgid "Abon"
|
msgid "Abon"
|
||||||
msgstr ""
|
msgstr "Абон"
|
||||||
|
|
||||||
#. name for abp
|
#. name for abp
|
||||||
msgid "Ayta; Abellen"
|
msgid "Ayta; Abellen"
|
||||||
@ -185,7 +185,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for abr
|
#. name for abr
|
||||||
msgid "Abron"
|
msgid "Abron"
|
||||||
msgstr ""
|
msgstr "Аброн"
|
||||||
|
|
||||||
#. name for abs
|
#. name for abs
|
||||||
msgid "Malay; Ambonese"
|
msgid "Malay; Ambonese"
|
||||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = u'calibre'
|
__appname__ = u'calibre'
|
||||||
numeric_version = (0, 8, 56)
|
numeric_version = (0, 8, 57)
|
||||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ class ANDROID(USBMS):
|
|||||||
0x4e22 : [0x0100, 0x226, 0x227, 0x231],
|
0x4e22 : [0x0100, 0x226, 0x227, 0x231],
|
||||||
0xb058 : [0x0222, 0x226, 0x227],
|
0xb058 : [0x0222, 0x226, 0x227],
|
||||||
0x0ff9 : [0x0226],
|
0x0ff9 : [0x0226],
|
||||||
|
0xc91 : HTC_BCDS,
|
||||||
0xdddd : [0x216],
|
0xdddd : [0x216],
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -165,7 +166,10 @@ class ANDROID(USBMS):
|
|||||||
0x2237: { 0x2208 : [0x0226] },
|
0x2237: { 0x2208 : [0x0226] },
|
||||||
|
|
||||||
# Lenovo
|
# Lenovo
|
||||||
0x17ef : { 0x7421 : [0x0216] },
|
0x17ef : {
|
||||||
|
0x7421 : [0x0216],
|
||||||
|
0x741b : [0x9999],
|
||||||
|
},
|
||||||
|
|
||||||
# Pantech
|
# Pantech
|
||||||
0x10a9 : { 0x6050 : [0x227] },
|
0x10a9 : { 0x6050 : [0x227] },
|
||||||
@ -203,7 +207,8 @@ class ANDROID(USBMS):
|
|||||||
'GT-I9003_CARD', 'XT912', 'FILE-CD_GADGET', 'RK29_SDK', 'MB855',
|
'GT-I9003_CARD', 'XT912', 'FILE-CD_GADGET', 'RK29_SDK', 'MB855',
|
||||||
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
|
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
|
||||||
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER',
|
'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',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||||
|
@ -5,7 +5,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, dbus
|
import os, dbus, re
|
||||||
|
|
||||||
def node_mountpoint(node):
|
def node_mountpoint(node):
|
||||||
|
|
||||||
@ -19,13 +19,20 @@ def node_mountpoint(node):
|
|||||||
return de_mangle(line[1])
|
return de_mangle(line[1])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
class NoUDisks1(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
class UDisks(object):
|
class UDisks(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bus = dbus.SystemBus()
|
self.bus = dbus.SystemBus()
|
||||||
|
try:
|
||||||
self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks',
|
self.main = dbus.Interface(self.bus.get_object('org.freedesktop.UDisks',
|
||||||
'/org/freedesktop/UDisks'), '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):
|
def device(self, device_node_path):
|
||||||
devpath = self.main.FindDeviceByDeviceFile(device_node_path)
|
devpath = self.main.FindDeviceByDeviceFile(device_node_path)
|
||||||
@ -56,6 +63,102 @@ class UDisks(object):
|
|||||||
d = self.device(parent)
|
d = self.device(parent)
|
||||||
d.DriveEject([])
|
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):
|
def mount(node_path):
|
||||||
u = UDisks()
|
u = UDisks()
|
||||||
u.mount(node_path)
|
u.mount(node_path)
|
||||||
@ -68,15 +171,19 @@ def umount(node_path):
|
|||||||
u = UDisks()
|
u = UDisks()
|
||||||
u.unmount(node_path)
|
u.unmount(node_path)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def test_udisks(ver=None):
|
||||||
import sys
|
import sys
|
||||||
dev = sys.argv[1]
|
dev = sys.argv[1]
|
||||||
print 'Testing with node', dev
|
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 'Mounted at:', u.mount(dev)
|
||||||
print 'Unmounting'
|
print 'Unmounting'
|
||||||
u.unmount(dev)
|
u.unmount(dev)
|
||||||
print 'Ejecting:'
|
print 'Ejecting:'
|
||||||
u.eject(dev)
|
u.eject(dev)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_udisks()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -61,7 +61,7 @@ ORIENTATIONS = ['portrait', 'landscape']
|
|||||||
class PDFOutput(OutputFormatPlugin):
|
class PDFOutput(OutputFormatPlugin):
|
||||||
|
|
||||||
name = 'PDF Output'
|
name = 'PDF Output'
|
||||||
author = 'John Schember and Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
file_type = 'pdf'
|
file_type = 'pdf'
|
||||||
|
|
||||||
options = set([
|
options = set([
|
||||||
@ -97,24 +97,6 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
self.metadata = oeb_book.metadata
|
self.metadata = oeb_book.metadata
|
||||||
self.cover_data = None
|
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:
|
if input_plugin.is_image_collection:
|
||||||
log.debug('Converting input as an image collection...')
|
log.debug('Converting input as an image collection...')
|
||||||
@ -128,16 +110,12 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
self.write(ImagePDFWriter, images)
|
self.write(ImagePDFWriter, images)
|
||||||
|
|
||||||
def get_cover_data(self):
|
def get_cover_data(self):
|
||||||
g, m = self.oeb.guide, self.oeb.manifest
|
oeb = self.oeb
|
||||||
if 'titlepage' not in g:
|
if (oeb.metadata.cover and
|
||||||
if 'cover' in g:
|
unicode(oeb.metadata.cover[0]) in oeb.manifest.ids):
|
||||||
href = g['cover'].href
|
cover_id = unicode(oeb.metadata.cover[0])
|
||||||
from calibre.ebooks.oeb.base import urlnormalize
|
item = oeb.manifest.ids[cover_id]
|
||||||
for item in m:
|
|
||||||
if item.href == urlnormalize(href):
|
|
||||||
self.cover_data = item.data
|
self.cover_data = item.data
|
||||||
if not isinstance(self.cover_data, basestring):
|
|
||||||
self.cover_data = None
|
|
||||||
|
|
||||||
def convert_text(self, oeb_book):
|
def convert_text(self, oeb_book):
|
||||||
from calibre.ebooks.pdf.writer import PDFWriter
|
from calibre.ebooks.pdf.writer import PDFWriter
|
||||||
|
@ -687,7 +687,11 @@ class Amazon(Source):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
for div in root.xpath(r'//div[starts-with(@id, "result_")]'):
|
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)
|
title = tostring(a, method='text', encoding=unicode)
|
||||||
if title_ok(title):
|
if title_ok(title):
|
||||||
matches.append(a.get('href'))
|
matches.append(a.get('href'))
|
||||||
|
@ -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 name: Plugin name
|
||||||
:param tests: List of 2-tuples. Each two tuple is of the form (args,
|
:param tests: List of 2-tuples. Each two tuple is of the form (args,
|
||||||
@ -246,6 +247,7 @@ def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None): # {{{
|
|||||||
None]
|
None]
|
||||||
if not good:
|
if not good:
|
||||||
prints('Failed to find', plugin.test_fields(possibles[0]))
|
prints('Failed to find', plugin.test_fields(possibles[0]))
|
||||||
|
if fail_missing_meta:
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
if results[0] is not possibles[0]:
|
if results[0] is not possibles[0]:
|
||||||
@ -263,9 +265,10 @@ def test_identify_plugin(name, tests, modify_plugin=lambda plugin:None): # {{{
|
|||||||
results.append(rq.get_nowait())
|
results.append(rq.get_nowait())
|
||||||
except Empty:
|
except Empty:
|
||||||
break
|
break
|
||||||
if not results:
|
if not results and fail_missing_meta:
|
||||||
prints('Cover download failed')
|
prints('Cover download failed')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
elif results:
|
||||||
cdata = results[0]
|
cdata = results[0]
|
||||||
cover = os.path.join(tdir, plugin.name.replace(' ',
|
cover = os.path.join(tdir, plugin.name.replace(' ',
|
||||||
'')+'-%s-cover.jpg'%sanitize_file_name2(mi.title.replace(' ',
|
'')+'-%s-cover.jpg'%sanitize_file_name2(mi.title.replace(' ',
|
||||||
|
@ -111,7 +111,7 @@ class Skeleton(object):
|
|||||||
self.chunks = chunks
|
self.chunks = chunks
|
||||||
|
|
||||||
self.skeleton = self.render(root)
|
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_metrics(root)
|
||||||
|
|
||||||
self.calculate_insert_positions()
|
self.calculate_insert_positions()
|
||||||
@ -127,7 +127,7 @@ class Skeleton(object):
|
|||||||
self.metrics = {}
|
self.metrics = {}
|
||||||
for tag in root.xpath('//*[@aid]'):
|
for tag in root.xpath('//*[@aid]'):
|
||||||
text = (tag.text or '').encode('utf-8')
|
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
|
start_length = len(raw.partition(b'>')[0]) + len(text) + 1
|
||||||
end_length = len(raw.rpartition(b'<')[-1]) + 1
|
end_length = len(raw.rpartition(b'<')[-1]) + 1
|
||||||
self.metrics[tag.get('aid')] = Metric(start_length, end_length)
|
self.metrics[tag.get('aid')] = Metric(start_length, end_length)
|
||||||
|
@ -15,15 +15,6 @@ log = (args...) -> # {{{
|
|||||||
process.stdout.write(msg + '\n')
|
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) -> # {{{
|
window_scroll_pos = (win=window) -> # {{{
|
||||||
if typeof(win.pageXOffset) == 'number'
|
if typeof(win.pageXOffset) == 'number'
|
||||||
x = win.pageXOffset
|
x = win.pageXOffset
|
||||||
@ -59,12 +50,12 @@ absleft = (elem) -> # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class PagedDisplay
|
class PagedDisplay
|
||||||
###
|
# This class is a namespace to expose functions via the
|
||||||
This class is a namespace to expose functions via the
|
# window.paged_display object. The most important functions are:
|
||||||
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.
|
#
|
||||||
###
|
# layout(): causes the currently loaded document to be laid out in columns.
|
||||||
|
|
||||||
constructor: () ->
|
constructor: () ->
|
||||||
if not this instanceof arguments.callee
|
if not this instanceof arguments.callee
|
||||||
@ -74,35 +65,42 @@ class PagedDisplay
|
|||||||
this.screen_width = 0
|
this.screen_width = 0
|
||||||
this.in_paged_mode = false
|
this.in_paged_mode = false
|
||||||
this.current_margin_side = 0
|
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_top = margin_top
|
||||||
this.margin_side = margin_side
|
this.margin_side = margin_side
|
||||||
this.margin_bottom = margin_bottom
|
this.margin_bottom = margin_bottom
|
||||||
this.cols_per_screen = cols_per_screen
|
this.cols_per_screen = cols_per_screen
|
||||||
|
|
||||||
layout: () ->
|
layout: () ->
|
||||||
# Remove the top margin from the first child of body as that gets added
|
body_style = window.getComputedStyle(document.body)
|
||||||
# to the top margin of body. This is done here just in case
|
# When laying body out in columns, webkit bleeds the top margin of the
|
||||||
# getComputedStyle() causes a relayout. The re-layout will be much
|
# first block element out above the columns, leading to an extra top
|
||||||
# faster before we implement the multi-column layout.
|
# margin for the page. We compensate for that here. Computing the
|
||||||
for node in document.body.childNodes
|
# boundingrect of body is very expensive with column layout, so we do
|
||||||
if node.nodeType == 1 # Element node
|
# it before the column layout is applied.
|
||||||
style = window.getComputedStyle(node)
|
first_layout = false
|
||||||
if style.display in ['block', 'table']
|
if not this.in_paged_mode
|
||||||
node.style.setProperty('margin-top', '0px')
|
document.body.style.marginTop = '0px'
|
||||||
break
|
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
|
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
|
# 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 <=
|
# 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
|
# side_margin (it may be less if the window width is not a
|
||||||
# multiple of n*(col_width+2*side_margin).
|
# multiple of n*(col_width+2*side_margin).
|
||||||
|
|
||||||
|
n = this.cols_per_screen
|
||||||
adjust = ww - Math.floor(ww/n)*n
|
adjust = ww - Math.floor(ww/n)*n
|
||||||
# Ensure that the margins are large enough that the adjustment does not
|
# Ensure that the margins are large enough that the adjustment does not
|
||||||
# cause them to become negative semidefinite
|
# cause them to become negative semidefinite
|
||||||
@ -113,7 +111,6 @@ class PagedDisplay
|
|||||||
this.page_width = col_width + 2*sm
|
this.page_width = col_width + 2*sm
|
||||||
this.screen_width = this.page_width * this.cols_per_screen
|
this.screen_width = this.page_width * this.cols_per_screen
|
||||||
|
|
||||||
body_style = window.getComputedStyle(document.body)
|
|
||||||
fgcolor = body_style.getPropertyValue('color')
|
fgcolor = body_style.getPropertyValue('color')
|
||||||
bs = document.body.style
|
bs = document.body.style
|
||||||
|
|
||||||
@ -121,9 +118,9 @@ class PagedDisplay
|
|||||||
bs.setProperty('-webkit-column-width', col_width+'px')
|
bs.setProperty('-webkit-column-width', col_width+'px')
|
||||||
bs.setProperty('-webkit-column-rule-color', fgcolor)
|
bs.setProperty('-webkit-column-rule-color', fgcolor)
|
||||||
bs.setProperty('overflow', 'visible')
|
bs.setProperty('overflow', 'visible')
|
||||||
bs.setProperty('height', 'auto')
|
bs.setProperty('height', (window.innerHeight - this.margin_top - this.margin_bottom) + 'px')
|
||||||
bs.setProperty('width', 'auto')
|
bs.setProperty('width', (window.innerWidth - 2*sm)+'px')
|
||||||
bs.setProperty('margin-top', this.margin_top+'px')
|
bs.setProperty('margin-top', margin_top)
|
||||||
bs.setProperty('margin-bottom', this.margin_bottom+'px')
|
bs.setProperty('margin-bottom', this.margin_bottom+'px')
|
||||||
bs.setProperty('margin-left', sm+'px')
|
bs.setProperty('margin-left', sm+'px')
|
||||||
bs.setProperty('margin-right', sm+'px')
|
bs.setProperty('margin-right', sm+'px')
|
||||||
@ -146,6 +143,15 @@ class PagedDisplay
|
|||||||
priority = rule.style.getPropertyPriority(prop)
|
priority = rule.style.getPropertyPriority(prop)
|
||||||
rule.style.setProperty(cprop, val, priority)
|
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.in_paged_mode = true
|
||||||
this.current_margin_side = sm
|
this.current_margin_side = sm
|
||||||
return sm
|
return sm
|
||||||
@ -155,19 +161,49 @@ class PagedDisplay
|
|||||||
xpos = Math.floor(document.body.scrollWidth * frac)
|
xpos = Math.floor(document.body.scrollWidth * frac)
|
||||||
this.scroll_to_xpos(xpos)
|
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
|
# Scroll so that the column containing xpos is the left most column in
|
||||||
# the viewport
|
# the viewport
|
||||||
if typeof(xpos) != 'number'
|
if typeof(xpos) != 'number'
|
||||||
log(xpos, 'is not a number, cannot scroll to it!')
|
log(xpos, 'is not a number, cannot scroll to it!')
|
||||||
return
|
return
|
||||||
|
if this.is_full_screen_layout
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
return
|
||||||
pos = 0
|
pos = 0
|
||||||
until (pos <= xpos < pos + this.page_width)
|
until (pos <= xpos < pos + this.page_width)
|
||||||
pos += this.page_width
|
pos += this.page_width
|
||||||
limit = document.body.scrollWidth - this.screen_width
|
limit = document.body.scrollWidth - this.screen_width
|
||||||
pos = limit if pos > limit
|
pos = limit if pos > limit
|
||||||
|
if animated
|
||||||
|
this.animated_scroll(pos, duration=1000, notify=notify)
|
||||||
|
else
|
||||||
window.scrollTo(pos, 0)
|
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) ->
|
current_pos: (frac) ->
|
||||||
# The current scroll position as a fraction between 0 and 1
|
# The current scroll position as a fraction between 0 and 1
|
||||||
limit = document.body.scrollWidth - window.innerWidth
|
limit = document.body.scrollWidth - window.innerWidth
|
||||||
@ -178,6 +214,8 @@ class PagedDisplay
|
|||||||
current_column_location: () ->
|
current_column_location: () ->
|
||||||
# The location of the left edge of the left most column currently
|
# The location of the left edge of the left most column currently
|
||||||
# visible in the viewport
|
# visible in the viewport
|
||||||
|
if this.is_full_screen_layout
|
||||||
|
return 0
|
||||||
x = window.pageXOffset + Math.max(10, this.current_margin_side)
|
x = window.pageXOffset + Math.max(10, this.current_margin_side)
|
||||||
edge = Math.floor(x/this.page_width) * this.page_width
|
edge = Math.floor(x/this.page_width) * this.page_width
|
||||||
while edge < x
|
while edge < x
|
||||||
@ -187,6 +225,8 @@ class PagedDisplay
|
|||||||
next_screen_location: () ->
|
next_screen_location: () ->
|
||||||
# The position to scroll to for the next screen (which could contain
|
# The position to scroll to for the next screen (which could contain
|
||||||
# more than one pages). Returns -1 if no further scrolling is possible.
|
# 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()
|
cc = this.current_column_location()
|
||||||
ans = cc + this.screen_width
|
ans = cc + this.screen_width
|
||||||
limit = document.body.scrollWidth - window.innerWidth
|
limit = document.body.scrollWidth - window.innerWidth
|
||||||
@ -197,6 +237,8 @@ class PagedDisplay
|
|||||||
previous_screen_location: () ->
|
previous_screen_location: () ->
|
||||||
# The position to scroll to for the previous screen (which could contain
|
# The position to scroll to for the previous screen (which could contain
|
||||||
# more than one pages). Returns -1 if no further scrolling is possible.
|
# 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()
|
cc = this.current_column_location()
|
||||||
ans = cc - this.screen_width
|
ans = cc - this.screen_width
|
||||||
if ans < 0
|
if ans < 0
|
||||||
@ -209,6 +251,8 @@ class PagedDisplay
|
|||||||
# The position to scroll to for the next column (same as
|
# The position to scroll to for the next column (same as
|
||||||
# next_screen_location() if columns per screen == 1). Returns -1 if no
|
# next_screen_location() if columns per screen == 1). Returns -1 if no
|
||||||
# further scrolling is possible.
|
# further scrolling is possible.
|
||||||
|
if this.is_full_screen_layout
|
||||||
|
return -1
|
||||||
cc = this.current_column_location()
|
cc = this.current_column_location()
|
||||||
ans = cc + this.page_width
|
ans = cc + this.page_width
|
||||||
limit = document.body.scrollWidth - window.innerWidth
|
limit = document.body.scrollWidth - window.innerWidth
|
||||||
@ -220,6 +264,8 @@ class PagedDisplay
|
|||||||
# The position to scroll to for the previous column (same as
|
# The position to scroll to for the previous column (same as
|
||||||
# previous_screen_location() if columns per screen == 1). Returns -1 if
|
# previous_screen_location() if columns per screen == 1). Returns -1 if
|
||||||
# no further scrolling is possible.
|
# no further scrolling is possible.
|
||||||
|
if this.is_full_screen_layout
|
||||||
|
return -1
|
||||||
cc = this.current_column_location()
|
cc = this.current_column_location()
|
||||||
ans = cc - this.page_width
|
ans = cc - this.page_width
|
||||||
if ans < 0
|
if ans < 0
|
||||||
@ -308,8 +354,7 @@ if window?
|
|||||||
window.paged_display = new PagedDisplay()
|
window.paged_display = new PagedDisplay()
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# Go to reference positions
|
|
||||||
# Indexing
|
# Indexing
|
||||||
# Resizing of images
|
# Resizing of images
|
||||||
# Special handling for identifiable covers (colspan)?
|
|
||||||
# Full screen mode
|
# Full screen mode
|
||||||
|
# Highlight on jump_to_anchor
|
||||||
|
@ -105,14 +105,14 @@ class UniqueFilenames(object): # {{{
|
|||||||
base, ext = posixpath.splitext(item.href)
|
base, ext = posixpath.splitext(item.href)
|
||||||
nhref = base + suffix + ext
|
nhref = base + suffix + ext
|
||||||
nhref = oeb.manifest.generate(href=nhref)[1]
|
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,
|
nitem = oeb.manifest.add(item.id, nhref, item.media_type, data=data,
|
||||||
fallback=item.fallback)
|
fallback=item.fallback)
|
||||||
self.seen_filenames.add(posixpath.basename(nhref))
|
self.seen_filenames.add(posixpath.basename(nhref))
|
||||||
self.rename_map[item.href] = nhref
|
self.rename_map[item.href] = nhref
|
||||||
if item.spine_position is not None:
|
if spine_pos is not None:
|
||||||
oeb.spine.insert(item.spine_position, nitem, item.linear)
|
oeb.spine.insert(spine_pos, nitem, item.linear)
|
||||||
oeb.spine.remove(item)
|
|
||||||
oeb.manifest.remove(item)
|
|
||||||
else:
|
else:
|
||||||
self.seen_filenames.add(fname)
|
self.seen_filenames.add(fname)
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -87,11 +87,6 @@ def get_pdf_printer(opts, for_comic=False, output_file_name=None):
|
|||||||
|
|
||||||
return printer
|
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):
|
def draw_image_page(printer, painter, p, preserve_aspect_ratio=True):
|
||||||
page_rect = printer.pageRect()
|
page_rect = printer.pageRect()
|
||||||
if preserve_aspect_ratio:
|
if preserve_aspect_ratio:
|
||||||
@ -138,13 +133,16 @@ class PDFWriter(QObject): # {{{
|
|||||||
self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
|
self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
|
||||||
self.view.loadFinished.connect(self._render_html,
|
self.view.loadFinished.connect(self._render_html,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
|
for x in (Qt.Horizontal, Qt.Vertical):
|
||||||
|
self.view.page().mainFrame().setScrollBarPolicy(x,
|
||||||
|
Qt.ScrollBarAlwaysOff)
|
||||||
self.render_queue = []
|
self.render_queue = []
|
||||||
self.combine_queue = []
|
self.combine_queue = []
|
||||||
self.tmp_path = PersistentTemporaryDirectory(u'_pdf_output_parts')
|
self.tmp_path = PersistentTemporaryDirectory(u'_pdf_output_parts')
|
||||||
|
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.size = get_printer_page_size(opts)
|
|
||||||
self.cover_data = cover_data
|
self.cover_data = cover_data
|
||||||
|
self.paged_js = None
|
||||||
|
|
||||||
def dump(self, items, out_stream, pdf_metadata):
|
def dump(self, items, out_stream, pdf_metadata):
|
||||||
self.metadata = pdf_metadata
|
self.metadata = pdf_metadata
|
||||||
@ -176,19 +174,46 @@ class PDFWriter(QObject): # {{{
|
|||||||
if ok:
|
if ok:
|
||||||
item_path = os.path.join(self.tmp_path, '%i.pdf' % len(self.combine_queue))
|
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)))
|
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.do_paged_render(item_path)
|
||||||
self.view.page().mainFrame().evaluateJavaScript('''
|
|
||||||
document.body.style.backgroundColor = "white";
|
|
||||||
|
|
||||||
''')
|
|
||||||
self.view.print_(printer)
|
|
||||||
printer.abort()
|
|
||||||
else:
|
else:
|
||||||
# The document is so corrupt that we can't render the page.
|
# The document is so corrupt that we can't render the page.
|
||||||
self.loop.exit(0)
|
self.loop.exit(0)
|
||||||
raise Exception('Document cannot be rendered.')
|
raise Exception('Document cannot be rendered.')
|
||||||
self._render_book()
|
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):
|
def _delete_tmpdir(self):
|
||||||
if os.path.exists(self.tmp_path):
|
if os.path.exists(self.tmp_path):
|
||||||
shutil.rmtree(self.tmp_path, True)
|
shutil.rmtree(self.tmp_path, True)
|
||||||
@ -237,7 +262,6 @@ class ImagePDFWriter(object):
|
|||||||
def __init__(self, opts, log, cover_data=None):
|
def __init__(self, opts, log, cover_data=None):
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.log = log
|
self.log = log
|
||||||
self.size = get_printer_page_size(opts, for_comic=True)
|
|
||||||
|
|
||||||
def dump(self, items, out_stream, pdf_metadata):
|
def dump(self, items, out_stream, pdf_metadata):
|
||||||
f = PersistentTemporaryFile('_comic2pdf.pdf')
|
f = PersistentTemporaryFile('_comic2pdf.pdf')
|
||||||
|
@ -329,10 +329,11 @@ class AddAction(InterfaceAction):
|
|||||||
x.decode(preferred_encoding, 'replace') for x in
|
x.decode(preferred_encoding, 'replace') for x in
|
||||||
self._adder.merged_books])
|
self._adder.merged_books])
|
||||||
info_dialog(self.gui, _('Merged some books'),
|
info_dialog(self.gui, _('Merged some books'),
|
||||||
_('The following duplicate books were found and incoming '
|
_('The following %d duplicate books were found and incoming '
|
||||||
'book formats were processed and merged into your '
|
'book formats were processed and merged into your '
|
||||||
'Calibre database according to your automerge '
|
'Calibre database according to your automerge '
|
||||||
'settings:'), det_msg=books, show=True)
|
'settings:')%len(self._adder.merged_books),
|
||||||
|
det_msg=books, show=True)
|
||||||
|
|
||||||
if getattr(self._adder, 'number_of_books_added', 0) > 0 or \
|
if getattr(self._adder, 'number_of_books_added', 0) > 0 or \
|
||||||
getattr(self._adder, 'merged_books', False):
|
getattr(self._adder, 'merged_books', False):
|
||||||
|
@ -116,6 +116,9 @@ class EditorWidget(QWebView): # {{{
|
|||||||
ss = extra_shortcuts.get(wac, None)
|
ss = extra_shortcuts.get(wac, None)
|
||||||
if ss:
|
if ss:
|
||||||
ac.setShortcut(QKeySequence(getattr(QKeySequence, 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.action_color = QAction(QIcon(I('format-text-color')), _('Foreground color'),
|
||||||
self)
|
self)
|
||||||
@ -227,6 +230,9 @@ class EditorWidget(QWebView): # {{{
|
|||||||
js = 'document.execCommand("%s", false, null);' % cmd
|
js = 'document.execCommand("%s", false, null);' % cmd
|
||||||
frame.evaluateJavaScript(js)
|
frame.evaluateJavaScript(js)
|
||||||
|
|
||||||
|
def remove_format_cleanup(self):
|
||||||
|
self.html = self.html
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def html(self):
|
def html(self):
|
||||||
|
|
||||||
|
@ -323,6 +323,10 @@ def communicate(opts, args):
|
|||||||
|
|
||||||
if opts.shutdown_running_calibre:
|
if opts.shutdown_running_calibre:
|
||||||
t.conn.send('shutdown:')
|
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:
|
else:
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
args[1] = os.path.abspath(args[1])
|
args[1] = os.path.abspath(args[1])
|
||||||
|
@ -205,6 +205,8 @@ class Document(QWebPage): # {{{
|
|||||||
return self.anchor_positions
|
return self.anchor_positions
|
||||||
|
|
||||||
def switch_to_paged_mode(self, onresize=False):
|
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)
|
side_margin = self.javascript('window.paged_display.layout()', typ=int)
|
||||||
# Setup the contents size to ensure that there is a right most margin.
|
# 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
|
# 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))
|
self.mainFrame().setScrollPosition(QPoint(x, y))
|
||||||
|
|
||||||
def jump_to_anchor(self, anchor):
|
def jump_to_anchor(self, anchor):
|
||||||
|
if not self.loaded_javascript: return
|
||||||
self.javascript('window.paged_display.jump_to_anchor("%s")'%anchor)
|
self.javascript('window.paged_display.jump_to_anchor("%s")'%anchor)
|
||||||
|
|
||||||
def element_ypos(self, elem):
|
def element_ypos(self, elem):
|
||||||
@ -352,7 +355,7 @@ class Document(QWebPage): # {{{
|
|||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return 0.
|
return 0.
|
||||||
def fset(self, val):
|
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)
|
self.javascript('paged_display.scroll_to_pos(%f)'%val)
|
||||||
else:
|
else:
|
||||||
npos = val * (self.height - self.window_height)
|
npos = val * (self.height - self.window_height)
|
||||||
|
@ -138,7 +138,9 @@ class Reference(QLineEdit):
|
|||||||
self.editingFinished.connect(self.editing_finished)
|
self.editingFinished.connect(self.editing_finished)
|
||||||
|
|
||||||
def editing_finished(self):
|
def editing_finished(self):
|
||||||
self.goto.emit(unicode(self.text()))
|
text = unicode(self.text())
|
||||||
|
self.setText('')
|
||||||
|
self.goto.emit(text)
|
||||||
|
|
||||||
class RecentAction(QAction):
|
class RecentAction(QAction):
|
||||||
|
|
||||||
@ -411,10 +413,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
return c.remember_current_page
|
return c.remember_current_page
|
||||||
|
|
||||||
def print_book(self):
|
def print_book(self):
|
||||||
Printing(self.iterator.spine, False)
|
p = Printing(self.iterator, self)
|
||||||
|
p.start_print()
|
||||||
|
|
||||||
def print_preview(self):
|
def print_preview(self):
|
||||||
Printing(self.iterator.spine, True)
|
p = Printing(self.iterator, self)
|
||||||
|
p.start_preview()
|
||||||
|
|
||||||
def toggle_fullscreen(self, x):
|
def toggle_fullscreen(self, x):
|
||||||
if self.isFullScreen():
|
if self.isFullScreen():
|
||||||
|
@ -1,127 +1,104 @@
|
|||||||
#!/usr/bin/env python
|
#!/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'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QObject, QEventLoop, Qt, QPrintDialog, QPainter, QSize,
|
||||||
import os, sys, urlparse
|
QPrintPreviewDialog)
|
||||||
|
|
||||||
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.QtWebKit import QWebView
|
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):
|
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()
|
def __init__(self, iterator, parent):
|
||||||
if preview:
|
QObject.__init__(self, parent)
|
||||||
self.connect(self.view, SIGNAL('loadFinished(bool)'), self.print_preview)
|
self.current_index = 0
|
||||||
else:
|
self.iterator = iterator
|
||||||
self.connect(self.view, SIGNAL('loadFinished(bool)'), self.print_book)
|
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):
|
def start_print(self):
|
||||||
content = ''
|
self.pd = QPrintDialog(self.parent())
|
||||||
|
self.pd.open(self._start_print)
|
||||||
|
|
||||||
for path in spine:
|
def _start_print(self):
|
||||||
raw = self.raw_content(path)
|
self.do_print(self.pd.printer())
|
||||||
content += self.parsed_content(raw, path)
|
|
||||||
|
|
||||||
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]
|
def do_print(self, printer):
|
||||||
base = base if base != '' else '/'
|
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))
|
for path in self.iterator.spine:
|
||||||
self.loop.exec_()
|
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)')
|
while True:
|
||||||
def load_content(self, content, base):
|
if not first:
|
||||||
self.view.setHtml(content, QUrl(base))
|
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):
|
painter.end()
|
||||||
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
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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_()
|
||||||
|
|
||||||
|
@ -6,14 +6,12 @@ Miscellaneous widgets used in the GUI
|
|||||||
import re, traceback, os
|
import re, traceback, os
|
||||||
|
|
||||||
from PyQt4.Qt import (QIcon, QFont, QLabel, QListWidget, QAction,
|
from PyQt4.Qt import (QIcon, QFont, QLabel, QListWidget, QAction,
|
||||||
QListWidgetItem, QTextCharFormat, QApplication,
|
QListWidgetItem, QTextCharFormat, QApplication, QSyntaxHighlighter,
|
||||||
QSyntaxHighlighter, QCursor, QColor, QWidget,
|
QCursor, QColor, QWidget, QPixmap, QSplitterHandle, QToolButton,
|
||||||
QPixmap, QSplitterHandle, QToolButton,
|
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, QRegExp, QSize,
|
||||||
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal,
|
QSplitter, QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene, QMenu,
|
||||||
QRegExp, QSettings, QSize, QSplitter,
|
QStringListModel, QCompleter, QStringList, QTimer, QRect,
|
||||||
QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene,
|
QFontDatabase, QGraphicsView, QByteArray)
|
||||||
QMenu, QStringListModel, QCompleter, QStringList,
|
|
||||||
QTimer, QRect, QFontDatabase, QGraphicsView)
|
|
||||||
|
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
from calibre.gui2 import (NONE, error_dialog, pixmap_to_data, gprefs,
|
from calibre.gui2 import (NONE, error_dialog, pixmap_to_data, gprefs,
|
||||||
@ -803,69 +801,29 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{
|
|||||||
@classmethod
|
@classmethod
|
||||||
def loadConfig(cls):
|
def loadConfig(cls):
|
||||||
Config = cls.Config
|
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"):
|
for name in ("window", "shell"):
|
||||||
Config["%swidth" % name] = settings.value("%swidth" % name,
|
Config["%swidth" % name] = QVariant(QApplication.desktop().availableGeometry().width() / 2).toInt()[0]
|
||||||
QVariant(QApplication.desktop() \
|
Config["%sheight" % name] = QVariant(QApplication.desktop().availableGeometry().height() / 2).toInt()[0]
|
||||||
.availableGeometry().width() / 2)).toInt()[0]
|
Config["%sy" % name] = QVariant(0).toInt()[0]
|
||||||
Config["%sheight" % name] = settings.value("%sheight" % name,
|
Config["toolbars"] = QByteArray(b'')
|
||||||
QVariant(QApplication.desktop() \
|
Config["splitter"] = QByteArray(b'')
|
||||||
.availableGeometry().height() / 2)).toInt()[0]
|
Config["shellx"] = QVariant(0).toInt()[0]
|
||||||
Config["%sy" % name] = settings.value("%sy" % name,
|
Config["windowx"] = QVariant(QApplication.desktop().availableGeometry().width() / 2).toInt()[0]
|
||||||
QVariant(0)).toInt()[0]
|
Config["remembergeometry"] = QVariant(True).toBool()
|
||||||
Config["toolbars"] = settings.value("toolbars").toByteArray()
|
Config["startwithshell"] = QVariant(True).toBool()
|
||||||
Config["splitter"] = settings.value("splitter").toByteArray()
|
Config["showwindowinfo"] = QVariant(True).toBool()
|
||||||
Config["shellx"] = settings.value("shellx", QVariant(0)).toInt()[0]
|
Config["backupsuffix"] = QVariant(".bak").toString()
|
||||||
Config["windowx"] = settings.value("windowx", QVariant(QApplication \
|
Config["cwd"] = QVariant(".").toString()
|
||||||
.desktop().availableGeometry().width() / 2)).toInt()[0]
|
Config["tooltipsize"] = QVariant(150).toInt()[0]
|
||||||
Config["remembergeometry"] = settings.value("remembergeometry",
|
Config["maxlinestoscan"] = QVariant(5000).toInt()[0]
|
||||||
QVariant(True)).toBool()
|
Config["pythondocpath"] = QVariant("http://docs.python.org").toString()
|
||||||
Config["startwithshell"] = settings.value("startwithshell",
|
Config["autohidefinddialog"] = QVariant(True).toBool()
|
||||||
QVariant(True)).toBool()
|
Config["findcasesensitive"] = QVariant(False).toBool()
|
||||||
Config["showwindowinfo"] = settings.value("showwindowinfo",
|
Config["findwholewords"] = QVariant(False).toBool()
|
||||||
QVariant(True)).toBool()
|
Config["tabwidth"] = QVariant(4).toInt()[0]
|
||||||
setDefaultString("shellstartup", """\
|
Config["fontfamily"] = QVariant("monospace").toString()
|
||||||
from __future__ import division
|
Config["fontsize"] = QVariant(10).toInt()[0]
|
||||||
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]
|
|
||||||
for name, color, bold, italic in (
|
for name, color, bold, italic in (
|
||||||
("normal", "#000000", False, False),
|
("normal", "#000000", False, False),
|
||||||
("keyword", "#000080", True, False),
|
("keyword", "#000080", True, False),
|
||||||
@ -877,12 +835,9 @@ class PythonHighlighter(QSyntaxHighlighter): # {{{
|
|||||||
("number", "#924900", False, False),
|
("number", "#924900", False, False),
|
||||||
("error", "#FF0000", False, False),
|
("error", "#FF0000", False, False),
|
||||||
("pyqt", "#50621A", False, False)):
|
("pyqt", "#50621A", False, False)):
|
||||||
Config["%sfontcolor" % name] = settings.value(
|
Config["%sfontcolor" % name] = QVariant(color).toString()
|
||||||
"%sfontcolor" % name, QVariant(color)).toString()
|
Config["%sfontbold" % name] = QVariant(bold).toBool()
|
||||||
Config["%sfontbold" % name] = settings.value(
|
Config["%sfontitalic" % name] = QVariant(italic).toBool()
|
||||||
"%sfontbold" % name, QVariant(bold)).toBool()
|
|
||||||
Config["%sfontitalic" % name] = settings.value(
|
|
||||||
"%sfontitalic" % name, QVariant(italic)).toBool()
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@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
Loading…
x
Reference in New Issue
Block a user