Merge from trunk

This commit is contained in:
Charles Haley 2010-12-31 06:13:06 +00:00
commit 81900219a2
50 changed files with 700 additions and 222 deletions

View File

@ -4,6 +4,11 @@ License: GPL-3
The full text of the GPL is distributed as in The full text of the GPL is distributed as in
/usr/share/common-licenses/GPL-3 on Debian systems. /usr/share/common-licenses/GPL-3 on Debian systems.
Files: src/calibre/ebooks/pdf/*.h,*.cpp
License: GPL-2 or later
The full text of the GPL is distributed as in
/usr/share/common-licenses/GPL-2 on Debian systems.
Files: src/calibre/ebooks/BeautifulSoup.py Files: src/calibre/ebooks/BeautifulSoup.py
Copyright: Copyright (c) 2004-2007, Leonard Richardson Copyright: Copyright (c) 2004-2007, Leonard Richardson
License: BSD License: BSD

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,69 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.businessinsider.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Business_insider(BasicNewsRecipe):
title = 'Business Insider'
__author__ = 'Darko Miletic'
description = 'Noticias de Argentina y el resto del mundo'
publisher = 'Business Insider, Inc.'
category = 'news, politics, finances, world'
oldest_article = 2
max_articles_per_feed = 200
no_stylesheets = True
encoding = 'utf8'
use_embedded_content = True
language = 'en'
remove_empty_feeds = True
publication_type = 'newsportal'
masthead_url = 'http://static.businessinsider.com/assets/images/logos/tbi_print.jpg'
extra_css = """
body{font-family: Arial,Helvetica,sans-serif }
img{margin-bottom: 0.4em; display:block}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
remove_tags = [
dict(name=['meta','link'])
,dict(attrs={'class':'feedflare'})
]
remove_attributes=['lang','border']
feeds = [
(u'Latest' , u'http://feeds2.feedburner.com/businessinsider' )
,(u'Markets' , u'http://feeds.feedburner.com/TheMoneyGame' )
,(u'Wall Street' , u'http://feeds.feedburner.com/clusterstock' )
,(u'Tech' , u'http://feeds.feedburner.com/typepad/alleyinsider/silicon_alley_insider')
,(u'The Wire' , u'http://feeds.feedburner.com/businessinsider/thewire' )
,(u'War Room' , u'http://feeds.feedburner.com/businessinsider/warroom' )
,(u'Sports' , u'http://feeds.feedburner.com/businessinsider/sportspage' )
,(u'Tools' , u'http://feeds.feedburner.com/businessinsider/tools' )
,(u'Travel' , u'http://feeds.feedburner.com/businessinsider/travel' )
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll('a'):
if item['href'].startswith('http://feedads'):
item.extract()
else:
if item.string is not None:
tstr = item.string
item.replaceWith(tstr)
for item in soup.findAll('img'):
if not item.has_key('alt'):
item['alt'] = 'image'
return soup

View File

@ -0,0 +1,109 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '04 December 2010, desUBIKado'
__author__ = 'desUBIKado'
__description__ = 'Daily newspaper from Aragon'
__version__ = 'v0.05'
__date__ = '07, December 2010'
'''
elperiodicodearagon.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class elperiodicodearagon(BasicNewsRecipe):
title = u'El Periodico de Aragon'
__author__ = u'desUBIKado'
description = u'Noticias desde Aragon'
publisher = u'elperiodicodearagon.com'
category = u'news, politics, Spain, Aragon'
oldest_article = 2
delay = 0
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
language = 'es'
encoding = 'utf8'
remove_empty_feeds = True
remove_javascript = True
conversion_options = {
'comments' : description
,'tags' : category
,'language' : language
,'publisher' : publisher
}
feeds = [(u'Arag\xf3n', u'http://elperiodicodearagon.com/RSS/2.xml'),
(u'Internacional', u'http://elperiodicodearagon.com/RSS/4.xml'),
(u'Espa\xf1a', u'http://elperiodicodearagon.com/RSS/3.xml'),
(u'Econom\xeda', u'http://elperiodicodearagon.com/RSS/5.xml'),
(u'Deportes', u'http://elperiodicodearagon.com/RSS/7.xml'),
(u'Real Zaragoza', u'http://elperiodicodearagon.com/RSS/10.xml'),
(u'Opini\xf3n', u'http://elperiodicodearagon.com/RSS/103.xml'),
(u'Escenarios', u'http://elperiodicodearagon.com/RSS/105.xml'),
(u'Sociedad', u'http://elperiodicodearagon.com/RSS/104.xml'),
(u'Gente', u'http://elperiodicodearagon.com/RSS/330.xml')]
extra_css = '''
h3{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:xx-large;}
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
dd{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
'''
remove_attributes = ['height','width']
keep_only_tags = [dict(name='div', attrs={'id':'contenidos'})]
# Quitar toda la morralla
remove_tags = [dict(name='ul', attrs={'class':'herramientasDeNoticia'}),
dict(name='span', attrs={'class':'MasInformacion '}),
dict(name='span', attrs={'class':'MasInformacion'}),
dict(name='div', attrs={'class':'Middle'}),
dict(name='div', attrs={'class':'MenuCabeceraRZaragoza'}),
dict(name='div', attrs={'id':'MenuCabeceraRZaragoza'}),
dict(name='div', attrs={'class':'MenuEquipo'}),
dict(name='div', attrs={'class':'TemasRelacionados'}),
dict(name='div', attrs={'class':'GaleriaEnNoticia'}),
dict(name='div', attrs={'class':'Recorte'}),
dict(name='div', attrs={'id':'NoticiasenRecursos'}),
dict(name='div', attrs={'id':'NoticiaEnPapel'}),
dict(name='p', attrs={'class':'RecorteEnNoticias'}),
dict(name='div', attrs={'id':'Comparte'}),
dict(name='div', attrs={'id':'CajaComparte'}),
dict(name='a', attrs={'class':'EscribirComentario'}),
dict(name='a', attrs={'class':'AvisoComentario'}),
dict(name='div', attrs={'class':'CajaAvisoComentario'}),
dict(name='div', attrs={'class':'navegaNoticias'}),
dict(name='div', attrs={'id':'PaginadorDiCom'}),
dict(name='div', attrs={'id':'CajaAccesoCuentaUsuario'}),
dict(name='div', attrs={'id':'CintilloComentario'}),
dict(name='div', attrs={'id':'EscribeComentario'}),
dict(name='div', attrs={'id':'FormularioComentario'}),
dict(name='div', attrs={'id':'FormularioNormas'})]
# Recuperamos la portada de papel (la imagen format=1 tiene mayor resolucion)
def get_cover_url(self):
index = 'http://pdf.elperiodicodearagon.com/'
soup = self.index_to_soup(index)
for image in soup.findAll('img',src=True):
if image['src'].startswith('http://pdf.elperiodicodearagon.com/funciones/portada-preview.php?eid='):
return image['src'].rstrip('format=2') + 'format=1'
return None
# Para quitar espacios entre la noticia y los comentarios (lineas 1 y 2)
# El indice no apuntaba correctamente al empiece de la noticia (linea 3)
preprocess_regexps = [
(re.compile(r'<p>&nbsp;</p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'<p> </p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'<p id="">', re.DOTALL|re.IGNORECASE), lambda match: '<p>')
]

View File

@ -1,86 +1,95 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
''' '''
www.elpais.com/diario/ www.elpais.com
''' '''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class ElPaisImpresa(BasicNewsRecipe): class ElPais_RSS(BasicNewsRecipe):
title = u'El Pa\xeds - edicion impresa' title = 'El Pais'
__author__ = 'Darko Miletic' __author__ = 'Darko Miletic'
description = u'el periodico global en Espa\xf1ol' description = 'el periodico global en Castellano'
publisher = 'EDICIONES EL PAIS, S.L.' publisher = 'EDICIONES EL PAIS, S.L.'
category = 'news, politics,Spain,actualidad,noticias,informacion,videos,fotografias,audios,graficos,nacional,internacional,deportes,economia,tecnologia,cultura,gente,television,sociedad,opinion,blogs,foros,chats,encuestas,entrevistas,participacion' category = 'news, politics, finances, world, spain'
oldest_article = 2
max_articles_per_feed = 200
no_stylesheets = True no_stylesheets = True
encoding = 'latin1' encoding = 'cp1252'
use_embedded_content = False use_embedded_content = False
language = 'es' language = 'es_ES'
remove_empty_feeds = True
publication_type = 'newspaper' publication_type = 'newspaper'
masthead_url = 'http://www.elpais.com/im/tit_logo_global.gif' masthead_url = 'http://www.elpais.com/im/tit_logo.gif'
index = 'http://www.elpais.com/diario/' extra_css = """
extra_css = ' p{text-align: justify} body{ text-align: left; font-family: Georgia,"Times New Roman",Times,serif } h2{font-family: Arial,Helvetica,sans-serif} img{margin-bottom: 0.4em} ' body{font-family: Georgia,"Times New Roman",Times,serif }
h3{font-family: Arial,Helvetica,sans-serif}
img{margin-bottom: 0.4em; display:block}
"""
conversion_options = { conversion_options = {
'comment' : description 'comment' : description
, 'tags' : category , 'tags' : category
, 'publisher' : publisher , 'publisher' : publisher
, 'language' : language , 'language' : language
} }
feeds = [ keep_only_tags = [dict(attrs={'class':['cabecera_noticia estirar','cabecera_noticia','','contenido_noticia']})]
(u'Internacional' , index + u'internacional/' ) remove_tags = [
,(u'Espa\xf1a' , index + u'espana/' ) dict(name=['meta','link','base','iframe','embed','object'])
,(u'Economia' , index + u'economia/' ) ,dict(attrs={'class':['info_complementa','estructura_2col_der','votos estirar','votos']})
,(u'Opinion' , index + u'opinion/' ) ,dict(attrs={'id':'utilidades'})
,(u'Vi\xf1etas' , index + u'vineta/' ) ]
,(u'Sociedad' , index + u'sociedad/' ) remove_tags_after = dict(attrs={'id':'utilidades'})
,(u'Cultura' , index + u'cultura/' ) remove_attributes = ['lang','border','width','height']
,(u'Tendencias' , index + u'tendencias/' )
,(u'Gente' , index + u'gente/' )
,(u'Obituarios' , index + u'obituarios/' )
,(u'Deportes' , index + u'deportes/' )
,(u'Pantallas' , index + u'radioytv/' )
,(u'Ultima' , index + u'ultima/' )
,(u'Educacion' , index + u'educacion/' )
,(u'Saludo' , index + u'salud/' )
,(u'Ciberpais' , index + u'ciberpais/' )
,(u'EP3' , index + u'ep3/' )
,(u'Cine' , index + u'cine/' )
,(u'Babelia' , index + u'babelia/' )
,(u'El viajero' , index + u'viajero/' )
,(u'Negocios' , index + u'negocios/' )
,(u'Domingo' , index + u'domingo/' )
,(u'El Pais semanal' , index + u'eps/' )
,(u'Quadern Catalunya' , index + u'quadern-catalunya/' )
]
keep_only_tags=[dict(attrs={'class':['cabecera_noticia','contenido_noticia']})] feeds = [
remove_attributes=['width','height'] (u'Lo ultimo' , u'http://www.elpais.com/rss/feed.html?feedId=17046')
remove_tags=[dict(name='link')] ,(u'America Latina' , u'http://www.elpais.com/rss/feed.html?feedId=17041')
,(u'Mexico' , u'http://www.elpais.com/rss/feed.html?feedId=17042')
def parse_index(self): ,(u'Europa' , u'http://www.elpais.com/rss/feed.html?feedId=17043')
totalfeeds = [] ,(u'Estados Unidos' , u'http://www.elpais.com/rss/feed.html?feedId=17044')
lfeeds = self.get_feeds() ,(u'Oriente proximo' , u'http://www.elpais.com/rss/feed.html?feedId=17045')
for feedobj in lfeeds: ,(u'Espana' , u'http://www.elpais.com/rss/feed.html?feedId=1002' )
feedtitle, feedurl = feedobj ,(u'Andalucia' , u'http://www.elpais.com/rss/feed.html?feedId=17057')
self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl)) ,(u'Catalunia' , u'http://www.elpais.com/rss/feed.html?feedId=17059')
articles = [] ,(u'Comunidad Valenciana' , u'http://www.elpais.com/rss/feed.html?feedId=17061')
soup = self.index_to_soup(feedurl) ,(u'Madrid' , u'http://www.elpais.com/rss/feed.html?feedId=1016' )
for item in soup.findAll('a',attrs={'class':['g19r003','g19i003','g17r003','g17i003']}): ,(u'Pais Vasco' , u'http://www.elpais.com/rss/feed.html?feedId=17062')
url = 'http://www.elpais.com' + item['href'].rpartition('/')[0] ,(u'Galicia' , u'http://www.elpais.com/rss/feed.html?feedId=17063')
title = self.tag_to_string(item) ,(u'Opinion' , u'http://www.elpais.com/rss/feed.html?feedId=1003' )
date = strftime(self.timefmt) ,(u'Sociedad' , u'http://www.elpais.com/rss/feed.html?feedId=1004' )
articles.append({ ,(u'Deportes' , u'http://www.elpais.com/rss/feed.html?feedId=1007' )
'title' :title ,(u'Cultura' , u'http://www.elpais.com/rss/feed.html?feedId=1008' )
,'date' :date ,(u'Cine' , u'http://www.elpais.com/rss/feed.html?feedId=17052')
,'url' :url ,(u'Literatura' , u'http://www.elpais.com/rss/feed.html?feedId=17053')
,'description':'' ,(u'Musica' , u'http://www.elpais.com/rss/feed.html?feedId=17051')
}) ,(u'Arte' , u'http://www.elpais.com/rss/feed.html?feedId=17060')
totalfeeds.append((feedtitle, articles)) ,(u'Tecnologia' , u'http://www.elpais.com/rss/feed.html?feedId=1005' )
return totalfeeds ,(u'Economia' , u'http://www.elpais.com/rss/feed.html?feedId=1006' )
,(u'Ciencia' , u'http://www.elpais.com/rss/feed.html?feedId=17068')
,(u'Salud' , u'http://www.elpais.com/rss/feed.html?feedId=17074')
,(u'Ocio' , u'http://www.elpais.com/rss/feed.html?feedId=17075')
,(u'Justicia y Leyes' , u'http://www.elpais.com/rss/feed.html?feedId=17069')
,(u'Guerras y conflictos' , u'http://www.elpais.com/rss/feed.html?feedId=17070')
,(u'Politica' , u'http://www.elpais.com/rss/feed.html?feedId=17073')
]
def print_version(self, url): def print_version(self, url):
return url + '?print=1' return url + '?print=1'
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll('a'):
if item.string is not None:
tstr = item.string
item.replaceWith(tstr)
else:
item.name='span'
for atrs in ['href','target','alt','title']:
if item.has_key(atrs):
del item[atrs]
for item in soup.findAll('img',alt=False):
item['alt'] = 'image'
return soup

View File

@ -1,50 +1,65 @@
#!/usr/bin/env python #!/usr/bin/env python
__license__ = 'GPL v3' __license__ = 'GPL v3'
__author__ = 'Lorenzo Vigentini' __copyright__ = '04 December 2010, desUBIKado'
__copyright__ = '2009, Lorenzo Vigentini <l.vigentini at gmail.com>' __author__ = 'desUBIKado'
__description__ = 'Daily newspaper from Aragon' __description__ = 'Daily newspaper from Aragon'
__version__ = 'v1.01' __version__ = 'v0.03'
__date__ = '30, January 2010' __date__ = '11, December 2010'
''' '''
http://www.heraldo.es/ [url]http://www.heraldo.es/[/url]
''' '''
import time
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class heraldo(BasicNewsRecipe): class heraldo(BasicNewsRecipe):
author = 'Lorenzo Vigentini' __author__ = 'desUBIKado'
description = 'Daily newspaper from Aragon' description = 'Daily newspaper from Aragon'
cover_url = 'http://www.heraldo.es/MODULOS/global/publico/interfaces/img/logo.gif'
title = u'Heraldo de Aragon' title = u'Heraldo de Aragon'
publisher = 'OJD Nielsen' publisher = 'OJD Nielsen'
category = 'News, politics, culture, economy, general interest' category = 'News, politics, culture, economy, general interest'
language = 'es' language = 'es'
timefmt = '[%a, %d %b, %Y]' timefmt = '[%a, %d %b, %Y]'
oldest_article = 1 oldest_article = 1
max_articles_per_feed = 25 max_articles_per_feed = 100
use_embedded_content = False use_embedded_content = False
recursion = 10
remove_javascript = True remove_javascript = True
no_stylesheets = True no_stylesheets = True
recursion = 10
keep_only_tags = [
dict(name='div', attrs={'class':['titularNoticiaNN','textoGrisVerdanaContenidos']})
]
feeds = [ feeds = [
(u'Portadas ', u'http://www.heraldo.es/index.php/mod.portadas/mem.rss') (u'Portadas', u'http://www.heraldo.es/index.php/mod.portadas/mem.rss')
] ]
keep_only_tags = [dict(name='div', attrs={'id':['dts','com']})]
remove_tags = [dict(name='a', attrs={'class':['com flo-r','enl-if','enl-df']}),
dict(name='div', attrs={'class':['brb-b-s con marg-btt','cnt-rel con']}),
dict(name='form', attrs={'class':'form'})]
remove_tags_before = dict(name='div' , attrs={'id':'dts'})
remove_tags_after = dict(name='div' , attrs={'id':'com'})
def get_cover_url(self):
cover = None
st = time.localtime()
year = str(st.tm_year)
month = "%.2d" % st.tm_mon
day = "%.2d" % st.tm_mday
#[url]http://oldorigin-www.heraldo.es/20101211/primeras/portada_aragon.pdf[/url]
cover='http://oldorigin-www.heraldo.es/'+ year + month + day +'/primeras/portada_aragon.pdf'
br = BasicNewsRecipe.get_browser()
try:
br.open(cover)
except:
self.log("\nPortada no disponible")
cover ='http://www.heraldo.es/MODULOS/global/publico/interfaces/img/logo-Heraldo.png'
return cover
extra_css = ''' extra_css = '''
.articledate {color: gray;font-family: monospace;} h2{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:xx-large;}
.articledescription {display: block;font-family: sans;font-size: 0.7em; text-indent: 0;} '''
.firma {color: #666;display: block;font-family: verdana, arial, helvetica;font-size: 1em;margin-bottom: 8px;}
.textoGrisVerdanaContenidos {color: #56595c;display: block;font-family: Verdana;font-size: 1.28571em;padding-bottom: 10px}
.titularNoticiaNN {display: block;padding-bottom: 10px;padding-left: 0;padding-right: 0;padding-top: 4px}
.titulo {color: #003066;font-family: Tahoma;font-size: 1.92857em;font-weight: bold;line-height: 1.2em}
'''

View File

@ -0,0 +1,52 @@
import re
from calibre.web.feeds.news import BasicNewsRecipe
class KANewsRecipe(BasicNewsRecipe):
title = u'KA-News.de'
description = u'Nachrichten aus Karlsruhe, Deutschland und der Welt.'
__author__ = 'tfeld'
lang='de'
no_stylesheets = True
oldest_article = 7
max_articles_per_feed = 100
feeds = [
(u'News aus Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/karlsruhe.xml'),
(u'Kulturnachrichten aus Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/kultur.xml'),
(u'Durlach: News aus Durlach', 'http://www.ka-news.de/storage/rss/rss/durlach.xml'),
(u'Stutensee: News aus Stutensee Blankenloch, Büchig, Friedrichstal, Staffort, Spöck', 'http://www.ka-news.de/storage/rss/rss/stutensee.xml'),
(u'Bruchsal: News aus Bruchsal', 'http://www.ka-news.de/storage/rss/rss/bruchsal.xml'),
(u'Wirtschaftsnews aus Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/wirtschaft.xml'),
(u'ka-news.de - Sport', 'http://www.ka-news.de/storage/rss/rss/sport.xml'),
(u'KSC-News - News rund um den KSC', 'http://www.ka-news.de/storage/rss/rss/ksc.xml'),
(u'ka-news.de - BG Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/basketball.xml')
]
preprocess_regexps = [
(re.compile(r'width:[0-9]*?px', re.DOTALL|re.IGNORECASE), lambda match: ''),
]
remove_tags_before = dict(id='artdetail_ueberschrift')
remove_tags_after = dict(id='artdetail_unterzeile')
remove_tags = [dict(name=['div'], attrs={'class': 'lbx_table'}),
dict(name=['div'], attrs={'class': 'lk_zumthema'}),
dict(name=['div'], attrs={'class': 'lk_thumb'}),
dict(name=['div'], attrs={'class': 'lk_trenner'}),
dict(name=['div'], attrs={'class': 'lupen_container'}),
dict(name=['script']),
dict(name=['span'], attrs={'style': 'display:none;'}),
dict(name=['span'], attrs={'class': 'comm_info'}),
dict(name=['h3'], attrs={'id': 'artdetail_unterzeile'})]
# removing style attribute _after_ removing specifig tags above
remove_attributes = ['width','height','style']
extra_css = '''
h1{ font-size:large; font-weight:bold; }
h2{ font-size:medium; font-weight:bold; }
'''
def get_cover_url(self):
return 'http://www.ka-news.de/storage/scl/techkanews/logos/434447_m1t1w250q75s1v29681_ka-news-Logo_mit_Schatten_transparent.png'

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '11 December 2010, desUBIKado'
__author__ = 'desUBIKado'
__description__ = 'Entertainment guide from Aragon'
__version__ = 'v0.01'
__date__ = '11, December 2010'
'''
[url]http://www.redaragon.es/[/url]
'''
from calibre.web.feeds.news import BasicNewsRecipe
class heraldo(BasicNewsRecipe):
__author__ = 'desUBIKado'
description = u'Guia de ocio desde Aragon'
title = u'RedAragon'
publisher = 'Grupo Z'
category = 'Concerts, Movies, Entertainment news'
cover_url = 'http://www.redaragon.com/2008_img/logotipo.gif'
language = 'es'
timefmt = '[%a, %d %b, %Y]'
oldest_article = 15
max_articles_per_feed = 100
encoding = 'iso-8859-1'
use_embedded_content = False
remove_javascript = True
no_stylesheets = True
feeds = [(u'Conciertos', u'http://redaragon.com/rss/agenda.asp?tid=1'),
(u'Exposiciones', u'http://redaragon.com/rss/agenda.asp?tid=5'),
(u'Teatro', u'http://redaragon.com/rss/agenda.asp?tid=10'),
(u'Conferencias', u'http://redaragon.com/rss/agenda.asp?tid=2'),
(u'Ferias', u'http://redaragon.com/rss/agenda.asp?tid=6'),
(u'Filmotecas/Cineclubs', u'http://redaragon.com/rss/agenda.asp?tid=7'),
(u'Presentaciones', u'http://redaragon.com/rss/agenda.asp?tid=9'),
(u'Fiestas', u'http://redaragon.com/rss/agenda.asp?tid=11'),
(u'Infantil', u'http://redaragon.com/rss/agenda.asp?tid=13'),
(u'Otros', u'http://redaragon.com/rss/agenda.asp?tid=8')]
keep_only_tags = [dict(name='div', attrs={'id':'FichaEventoAgenda'})]
remove_tags = [dict(name='div', attrs={'class':['Comparte','CajaAgenda','Caja','Cintillo']})]
remove_tags_before = dict(name='div' , attrs={'id':'FichaEventoAgenda'})
remove_tags_after = dict(name='div' , attrs={'class':'Cintillo'})

View File

@ -25,22 +25,20 @@ class Salon_com(BasicNewsRecipe):
feeds = [ feeds = [
('News & Politics', 'http://feeds.salon.com/salon/news'), ('News & Politics', 'http://feeds.salon.com/salon/news'),
('War Room', 'http://feeds.salon.com/salon/war_room'), ('War Room', 'http://feeds.feedburner.com/salon/war_room'),
('Arts & Entertainment', 'http://feeds.salon.com/salon/ent'), ('Joan Walsh', 'http://feeds.feedburner.com/Salon_Joan_Walsh'),
('I Like to Watch', 'http://feeds.salon.com/salon/iltw'), ('Glenn Greenwald', 'http://feeds.feedburner.com/salon/greenwald'),
('Beyond Multiplex', 'http://feeds.salon.com/salon/btm'),
('Book Reviews', 'http://feeds.salon.com/salon/books'),
('All Life', 'http://feeds.salon.com/salon/mwt'),
('All Opinion', 'http://feeds.salon.com/salon/opinion'),
('Glenn Greenwald', 'http://feeds.salon.com/salon/greenwald'),
('Garrison Keillor', 'http://dir.salon.com/topics/garrison_keillor/index.rss'),
('Joan Walsh', 'http://www.salon.com/rss/walsh.rss'),
('All Sports', 'http://feeds.salon.com/salon/sports'),
('Tech & Business', 'http://feeds.salon.com/salon/tech'), ('Tech & Business', 'http://feeds.salon.com/salon/tech'),
('How World Works', 'http://feeds.salon.com/salon/htww') ('Ask the Pilot', 'http://feeds.feedburner.com/salon/ask_the_pilot'),
('How World Works', 'http://feeds.feedburner.com/salon/htww'),
('Life', 'http://feeds.feedburner.com/salon/mwt'),
('Broadsheet', 'http://feeds.feedburner.com/salon/broadsheet'),
('Movie Reviews', 'http://feeds.feedburner.com/salon/movie_reviews'),
('Film Salon', 'http://feeds.feedburner.com/Salon/Film_Salon'),
('TV', 'http://feeds.feedburner.com/salon/tv'),
('Books', 'http://feeds.feedburner.com/salon/books')
] ]
def print_version(self, url): def print_version(self, url):
return url.replace('/index.html', '/print.html') return url.replace('/index.html', '/print.html')

View File

@ -1,17 +1,19 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2010, JOlo'
''' '''
www.theweek.com www.theweek.com
''' '''
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
import re
class TheWeekFree(BasicNewsRecipe): class TheWeek(BasicNewsRecipe):
title = 'The Week Magazine - Free content' title = 'The Week Magazine'
__author__ = 'Darko Miletic' __author__ = 'Jim Olo'
description = "The best of the US and international media. Daily coverage of commentary and analysis of the day's events, as well as arts, entertainment, people and gossip, and political cartoons." description = "The best of the US and international media. Daily coverage of commentary and analysis of the day's events, as well as arts, entertainment, people and gossip, and political cartoons."
publisher = 'The Week Publications, Inc.' publisher = 'The Week Publications, Inc.'
masthead_url = 'http://test.theweek.com/images/logo_theweek.gif'
cover_url = masthead_url
category = 'news, politics, USA' category = 'news, politics, USA'
oldest_article = 7 oldest_article = 7
max_articles_per_feed = 100 max_articles_per_feed = 100
@ -19,31 +21,27 @@ class TheWeekFree(BasicNewsRecipe):
encoding = 'utf-8' encoding = 'utf-8'
use_embedded_content = False use_embedded_content = False
language = 'en' language = 'en'
preprocess_regexps = [(re.compile(r'<h3><a href=.*</body>', re.DOTALL), lambda match: '</body>')]
remove_tags_before = dict(name='h1')
remove_tags_after = dict(name='div', attrs={'class':'articleSubscribe4free'})
remove_tags = [
dict(name='div', attrs={'class':['floatLeft','imageCaption','slideshowImageAttribution','postDate','utilities','cartoonInfo','left','middle','col300','articleSubscribe4free',' articleFlyout','articleFlyout floatRight','fourFreeBar']})
,dict(name='div', attrs={'id':['cartoonThumbs','rightColumn','header','partners']})
,dict(name='ul', attrs={'class':['slideshowNav','hotTopicsList topicList']})
]
remove_attributes = ['width','height', 'style', 'font', 'color']
extra_css = '''
h1{font-family:Geneva, Arial, Helvetica, sans-serif;color:#154B7A;}
h3{font-size: 14px;color:#999999; font-family:Geneva, Arial, Helvetica, sans-serif;font-weight: bold;}
h2{color:#666666; font-family:Geneva, Arial, Helvetica, sans-serif;font-size:small;}
p {font-family:Arial,Helvetica,sans-serif;}
'''
filter_regexps = [r'www\.palmcoastdata\.com']
conversion_options = { feeds = [
'comment' : description (u'News-Opinion', u'http://theweek.com/section/index/news_opinion.rss'),
, 'tags' : category (u'Business', u'http://theweek.com/section/index/business.rss'),
, 'publisher' : publisher (u'Arts-Life', u'http://theweek.com/section/index/arts_life.rss'),
, 'language' : language (u'Cartoons', u'http://theweek.com/section/index/cartoon_wit/0/all-cartoons.rss')
} ]
keep_only_tags = [
dict(name=['h1','h2'])
, dict(name='div', attrs={'class':'basefont'})
, dict(name='div', attrs={'id':'slideshowLoader'})
]
remove_tags = [
dict(name='div', attrs={'id':['digg_dugg','articleRight','dateHeader']})
,dict(name=['object','embed','iframe'])
]
feeds = [
(u'News & Opinions' , u'http://www.theweek.com/section/index/news_opinion.rss')
,(u'Arts & Leisure' , u'http://www.theweek.com/section/index/arts_leisure.rss')
,(u'Business' , u'http://www.theweek.com/section/index/business.rss' )
,(u'Cartoon & Short takes' , u'http://www.theweek.com/section/index/cartoons_wit.rss')
]

View File

@ -38,12 +38,12 @@ class Wired(BasicNewsRecipe):
keep_only_tags = [dict(name='div', attrs={'class':'post'})] keep_only_tags = [dict(name='div', attrs={'class':'post'})]
remove_tags_after = dict(name='div', attrs={'class':'tweetmeme_button'}) remove_tags_after = dict(name='div', attrs={'class':'tweetmeme_button'})
remove_tags = [ remove_tags = [
dict(name=['object','embed','iframe','link']) dict(name=['object','embed','iframe','link','meta','base'])
,dict(name='div', attrs={'class':['podcast_storyboard','tweetmeme_button']}) ,dict(name='div', attrs={'class':['podcast_storyboard','tweetmeme_button']})
,dict(attrs={'id':'ff_bottom_nav'}) ,dict(attrs={'id':'ff_bottom_nav'})
,dict(name='a',attrs={'href':'http://www.wired.com/app'}) ,dict(name='a',attrs={'href':'http://www.wired.com/app'})
] ]
remove_attributes = ['height','width'] remove_attributes = ['height','width','lang','border','clear']
def parse_index(self): def parse_index(self):
@ -78,7 +78,9 @@ class Wired(BasicNewsRecipe):
divurl = item.find('div',attrs={'class':'feature-header'}) divurl = item.find('div',attrs={'class':'feature-header'})
if divurl: if divurl:
divdesc = item.find('div',attrs={'class':'feature-text'}) divdesc = item.find('div',attrs={'class':'feature-text'})
url = 'http://www.wired.com' + divurl.a['href'] url = divurl.a['href']
if not divurl.a['href'].startswith('http://www.wired.com'):
url = 'http://www.wired.com' + divurl.a['href']
title = self.tag_to_string(divurl.a) title = self.tag_to_string(divurl.a)
description = self.tag_to_string(divdesc) description = self.tag_to_string(divdesc)
date = strftime(self.timefmt) date = strftime(self.timefmt)
@ -127,5 +129,17 @@ class Wired(BasicNewsRecipe):
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']
for item in soup.findAll('a'):
if item.string is not None:
tstr = item.string
item.replaceWith(tstr)
else:
item.name='span'
for atrs in ['href','target','alt','title','name','id']:
if item.has_key(atrs):
del item[atrs]
for item in soup.findAll('img'):
if not item.has_key('alt'):
item['alt'] = 'image'
return soup return soup

View File

@ -478,7 +478,7 @@ from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \
from calibre.devices.sne.driver import SNE from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \ from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \ GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \
TREKSTOR, EEEREADER TREKSTOR, EEEREADER, NEXTBOOK
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO from calibre.devices.kobo.driver import KOBO
from calibre.devices.bambook.driver import BAMBOOK from calibre.devices.bambook.driver import BAMBOOK
@ -606,6 +606,7 @@ plugins += [
BAMBOOK, BAMBOOK,
TREKSTOR, TREKSTOR,
EEEREADER, EEEREADER,
NEXTBOOK,
ITUNES, ITUNES,
] ]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -439,6 +439,13 @@ class TabletOutput(iPadOutput):
screen_size = (sys.maxint, sys.maxint) screen_size = (sys.maxint, sys.maxint)
comic_screen_size = (sys.maxint, sys.maxint) comic_screen_size = (sys.maxint, sys.maxint)
class SamsungGalaxy(TabletOutput):
name = 'Samsung Galaxy'
shortname = 'galaxy'
description = _('Intended for the Samsung Galaxy and similar tablet devices with '
'a resolution of 600x1280')
screen_size = comic_screen_size = (600, 1280)
class SonyReaderOutput(OutputProfile): class SonyReaderOutput(OutputProfile):
name = 'Sony Reader' name = 'Sony Reader'
@ -707,7 +714,7 @@ class BambookOutput(OutputProfile):
output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output, output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output,
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output, SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput, HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
iPadOutput, KoboReaderOutput, TabletOutput, iPadOutput, KoboReaderOutput, TabletOutput, SamsungGalaxy,
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput, SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput, IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
BambookOutput, NookColorOutput] BambookOutput, NookColorOutput]

View File

@ -64,7 +64,8 @@ class ANDROID(USBMS):
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000'] 'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
'SGH-T849']
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', 'SAMSUNG_ANDROID', 'GT-P1000_CARD'] 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD']

View File

@ -29,12 +29,16 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
booklist_class = BookList booklist_class = BookList
book_class = Book book_class = Book
ip = None
FORMATS = [ "snb" ] FORMATS = [ "snb" ]
VENDOR_ID = 0x230b VENDOR_ID = 0x230b
PRODUCT_ID = 0x0001 PRODUCT_ID = 0x0001
BCD = None BCD = None
CAN_SET_METADATA = False CAN_SET_METADATA = False
THUMBNAIL_HEIGHT = 155 THUMBNAIL_HEIGHT = 155
EXTRA_CUSTOMIZATION_MESSAGE = \
_("Device IP Address (restart calibre after changing)")
icon = I("devices/bambook.png") icon = I("devices/bambook.png")
# OPEN_FEEDBACK_MESSAGE = _( # OPEN_FEEDBACK_MESSAGE = _(
@ -47,6 +51,10 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
METADATA_FILE_GUID = 'calibremetadata.snb' METADATA_FILE_GUID = 'calibremetadata.snb'
bambook = None bambook = None
is_connected = False
def __init__(self, ip):
self.ip = ip
def reset(self, key='-1', log_packets=False, report_progress=None, def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) : detected_device=None) :
@ -60,15 +68,23 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
self.eject() self.eject()
# Connect # Connect
self.bambook = Bambook() self.bambook = Bambook()
self.bambook.Connect() self.bambook.Connect(ip = self.ip, timeout = 10000)
if self.bambook.GetState() != CONN_CONNECTED: if self.bambook.GetState() != CONN_CONNECTED:
self.bambook = None self.bambook = None
raise Exception(_("Unable to connect to Bambook.")) raise OpenFeedback(_("Unable to connect to Bambook. \n"
"If you are trying to connect via Wi-Fi, "
"please make sure the IP address of Bambook has been correctly configured."))
self.is_connected = True
return True
def unmount_device(self):
self.eject()
def eject(self): def eject(self):
if self.bambook: if self.bambook:
self.bambook.Disconnect() self.bambook.Disconnect()
self.bambook = None self.bambook = None
self.is_connected = False
def post_yank_cleanup(self): def post_yank_cleanup(self):
self.eject() self.eject()
@ -475,3 +491,8 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
def get_guid(uuid): def get_guid(uuid):
guid = hashlib.md5(uuid).hexdigest()[0:15] + ".snb" guid = hashlib.md5(uuid).hexdigest()[0:15] + ".snb"
return guid return guid
class BAMBOOKWifi(BAMBOOK):
def is_usb_connected(self, devices_on_system, debug=False,
only_presence=False):
return self.is_connected, self

View File

@ -329,6 +329,8 @@ class Bambook:
self.handle = None self.handle = None
def Connect(self, ip = DEFAULT_BAMBOOK_IP, timeout = 10000): def Connect(self, ip = DEFAULT_BAMBOOK_IP, timeout = 10000):
if ip == None or ip == '':
ip = DEFAULT_BAMBOOK_IP
self.handle = BambookConnect(ip, timeout) self.handle = BambookConnect(ip, timeout)
if self.handle and self.handle != 0: if self.handle and self.handle != 0:
return True return True

View File

@ -230,7 +230,7 @@ class POCKETBOOK301(USBMS):
class POCKETBOOK602(USBMS): class POCKETBOOK602(USBMS):
name = 'PocketBook Pro 602/902 Device Interface' name = 'PocketBook Pro 602/902 Device Interface'
description = _('Communicate with the PocketBook 602 reader.') description = _('Communicate with the PocketBook 602/603/902/903 reader.')
author = 'Kovid Goyal' author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm', FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
@ -244,7 +244,7 @@ class POCKETBOOK602(USBMS):
BCD = [0x0324] BCD = [0x0324]
VENDOR_NAME = '' VENDOR_NAME = ''
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB902'] WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902', 'PB903']
class POCKETBOOK701(USBMS): class POCKETBOOK701(USBMS):

View File

@ -264,3 +264,23 @@ class EEEREADER(USBMS):
VENDOR_NAME = 'LINUX' VENDOR_NAME = 'LINUX'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET' WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
class NEXTBOOK(USBMS):
name = 'Nextbook device interface'
gui_name = 'Nextbook'
description = _('Communicate with the Nextbook Reader')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = ['epub', 'fb2', 'txt', 'pdf']
VENDOR_ID = [0x05e3]
PRODUCT_ID = [0x0726]
BCD = [0x021a]
EBOOK_DIR_MAIN = ''
VENDOR_NAME = 'NEXT2'
WINDOWS_MAIN_MEM = '1.0.14'

View File

@ -41,9 +41,12 @@ class FB2Input(InputFormatPlugin):
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.oeb.base import XLINK_NS, XHTML_NS, RECOVER_PARSER from calibre.ebooks.oeb.base import XLINK_NS, XHTML_NS, RECOVER_PARSER
from calibre.ebooks.chardet import xml_to_unicode
NAMESPACES = {'f':FB2NS, 'l':XLINK_NS} NAMESPACES = {'f':FB2NS, 'l':XLINK_NS}
log.debug('Parsing XML...') log.debug('Parsing XML...')
raw = stream.read().replace('\0', '') raw = stream.read().replace('\0', '')
raw = xml_to_unicode(raw, strip_encoding_pats=True,
assume_utf8=True)[0]
try: try:
doc = etree.fromstring(raw) doc = etree.fromstring(raw)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:

View File

@ -9,6 +9,7 @@ import mimetypes, os
from base64 import b64decode from base64 import b64decode
from lxml import etree from lxml import etree
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.chardet import xml_to_unicode
XLINK_NS = 'http://www.w3.org/1999/xlink' XLINK_NS = 'http://www.w3.org/1999/xlink'
def XLINK(name): def XLINK(name):
@ -23,7 +24,10 @@ def get_metadata(stream):
tostring = lambda x : etree.tostring(x, method='text', tostring = lambda x : etree.tostring(x, method='text',
encoding=unicode).strip() encoding=unicode).strip()
parser = etree.XMLParser(recover=True, no_network=True) parser = etree.XMLParser(recover=True, no_network=True)
root = etree.fromstring(stream.read(), parser=parser) raw = stream.read()
raw = xml_to_unicode(raw, strip_encoding_pats=True,
assume_utf8=True)[0]
root = etree.fromstring(raw, parser=parser)
authors, author_sort = [], None authors, author_sort = [], None
for au in XPath('//fb2:author')(root): for au in XPath('//fb2:author')(root):
fname = lname = author = None fname = lname = author = None

View File

@ -513,11 +513,14 @@ class MobiReader(object):
mobi_version = self.book_header.mobi_version mobi_version = self.book_header.mobi_version
for x in root.xpath('//ncx'): for x in root.xpath('//ncx'):
x.getparent().remove(x) x.getparent().remove(x)
svg_tags = []
for i, tag in enumerate(root.iter(etree.Element)): for i, tag in enumerate(root.iter(etree.Element)):
tag.attrib.pop('xmlns', '') tag.attrib.pop('xmlns', '')
for x in tag.attrib: for x in tag.attrib:
if ':' in x: if ':' in x:
del tag.attrib[x] del tag.attrib[x]
if tag.tag and barename(tag.tag) == 'svg':
svg_tags.append(tag)
if tag.tag and barename(tag.tag.lower()) in \ if tag.tag and barename(tag.tag.lower()) in \
('country-region', 'place', 'placetype', 'placename', ('country-region', 'place', 'placetype', 'placename',
'state', 'city', 'street', 'address', 'content', 'form'): 'state', 'city', 'street', 'address', 'content', 'form'):
@ -628,6 +631,11 @@ class MobiReader(object):
cls = cls + (' ' if cls else '') + ncls cls = cls + (' ' if cls else '') + ncls
attrib['class'] = cls attrib['class'] = cls
for tag in svg_tags:
p = tag.getparent()
if hasattr(p, 'remove'):
p.remove(tag)
def create_opf(self, htmlfile, guide=None, root=None): def create_opf(self, htmlfile, guide=None, root=None):
mi = getattr(self.book_header.exth, 'mi', self.embedded_mi) mi = getattr(self.book_header.exth, 'mi', self.embedded_mi)
if mi is None: if mi is None:

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
*/ */

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
*/ */

View File

@ -1,3 +1,10 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v2+
*/
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <sstream> #include <sstream>

View File

@ -1,3 +1,10 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v2+
*/
#pragma once #pragma once
#include <vector> #include <vector>

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
*/ */

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
*/ */

View File

@ -1,3 +1,10 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v2+
*/
#ifndef PDF2XML #ifndef PDF2XML
#define UNICODE #define UNICODE
#define PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
*/ */
#include <Outline.h> #include <Outline.h>

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
* Based on pdftohtml from the poppler project. * Based on pdftohtml from the poppler project.
*/ */

View File

@ -1,6 +1,6 @@
/** /**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net> * Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3 * License: GNU GPL v2+
*/ */

View File

@ -25,10 +25,6 @@ from PyQt4.QtWebKit import QWebView
from pyPdf import PdfFileWriter, PdfFileReader from pyPdf import PdfFileWriter, PdfFileReader
def get_pdf_printer():
return QPrinter(QPrinter.HighResolution)
def get_custom_size(opts): def get_custom_size(opts):
custom_size = None custom_size = None
if opts.custom_size != None: if opts.custom_size != None:
@ -42,12 +38,12 @@ def get_custom_size(opts):
custom_size = None custom_size = None
return custom_size return custom_size
def setup_printer(opts, for_comic=False): def get_pdf_printer(opts, for_comic=False):
from calibre.gui2 import is_ok_to_use_qt from calibre.gui2 import is_ok_to_use_qt
if not is_ok_to_use_qt(): if not is_ok_to_use_qt():
raise Exception('Not OK to use Qt') raise Exception('Not OK to use Qt')
printer = get_pdf_printer() printer = QPrinter(QPrinter.HighResolution)
custom_size = get_custom_size(opts) custom_size = get_custom_size(opts)
if opts.output_profile.short_name == 'default': if opts.output_profile.short_name == 'default':
@ -61,15 +57,22 @@ def setup_printer(opts, for_comic=False):
h = opts.output_profile.comic_screen_size[1] if for_comic else \ h = opts.output_profile.comic_screen_size[1] if for_comic else \
opts.output_profile.height opts.output_profile.height
dpi = opts.output_profile.dpi dpi = opts.output_profile.dpi
printer.setPaperSize(QSizeF(float(w) / dpi, float(h)/dpi), QPrinter.Inch) printer.setPaperSize(QSizeF(float(w) / dpi, float(h) / dpi), QPrinter.Inch)
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) if for_comic:
# Comic pages typically have their own margins, or their background
# color is not white, in which case the margin looks bad
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
else:
printer.setPageMargins(opts.margin_left, opts.margin_top,
opts.margin_right, opts.margin_bottom, QPrinter.Point)
printer.setOrientation(orientation(opts.orientation)) printer.setOrientation(orientation(opts.orientation))
printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFormat(QPrinter.PdfFormat)
printer.setFullPage(True)
return printer return printer
def get_printer_page_size(opts, for_comic=False): def get_printer_page_size(opts, for_comic=False):
printer = setup_printer(opts, for_comic=for_comic) printer = get_pdf_printer(opts, for_comic=for_comic)
size = printer.paperSize(QPrinter.Millimeter) size = printer.paperSize(QPrinter.Millimeter)
return size.width() / 10., size.height() / 10. return size.width() / 10., size.height() / 10.
@ -154,24 +157,11 @@ class PDFWriter(QObject): # {{{
self.view.load(QUrl.fromLocalFile(item)) self.view.load(QUrl.fromLocalFile(item))
def get_printer(self, set_horz_margins=False):
printer = get_pdf_printer()
printer.setPaperSize(QSizeF(self.size[0] * 10, self.size[1] * 10), QPrinter.Millimeter)
if set_horz_margins:
printer.setPageMargins(0., self.opts.margin_top, 0.,
self.opts.margin_bottom, QPrinter.Point)
else:
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
printer.setOrientation(orientation(self.opts.orientation))
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setFullPage(not set_horz_margins)
return printer
def _render_html(self, ok): def _render_html(self, ok):
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' % (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 = self.get_printer(set_horz_margins=True) printer = get_pdf_printer(self.opts)
printer.setOutputFileName(item_path) printer.setOutputFileName(item_path)
self.view.print_(printer) self.view.print_(printer)
self._render_book() self._render_book()
@ -233,16 +223,11 @@ class ImagePDFWriter(object):
os.remove(f.name) os.remove(f.name)
def render_images(self, outpath, mi, items): def render_images(self, outpath, mi, items):
printer = get_pdf_printer() printer = get_pdf_printer(self.opts, for_comic=True)
printer.setPaperSize(QSizeF(self.size[0] * 10, self.size[1] * 10), QPrinter.Millimeter)
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
printer.setOrientation(orientation(self.opts.orientation))
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(outpath) printer.setOutputFileName(outpath)
printer.setDocName(mi.title) printer.setDocName(mi.title)
printer.setCreator(u'%s [%s]'%(__appname__, __version__)) printer.setCreator(u'%s [%s]'%(__appname__, __version__))
# Seems to be no way to set author # Seems to be no way to set author
printer.setFullPage(True)
painter = QPainter(printer) painter = QPainter(printer)
painter.setRenderHints(QPainter.Antialiasing|QPainter.SmoothPixmapTransform) painter.setRenderHints(QPainter.Antialiasing|QPainter.SmoothPixmapTransform)

View File

@ -46,14 +46,27 @@ class SNBInput(InputFormatPlugin):
meta = snbFile.GetFileStream('snbf/book.snbf') meta = snbFile.GetFileStream('snbf/book.snbf')
if meta != None: if meta != None:
meta = etree.fromstring(meta) meta = etree.fromstring(meta)
oeb.metadata.add('title', meta.find('.//head/name').text) l = { 'title' : './/head/name',
oeb.metadata.add('creator', meta.find('.//head/author').text, attrib={'role':'aut'}) 'creator' : './/head/author',
oeb.metadata.add('language', meta.find('.//head/language').text.lower().replace('_', '-')) 'language' : './/head/language',
oeb.metadata.add('creator', meta.find('.//head/generator').text) 'generator': './/head/generator',
oeb.metadata.add('publisher', meta.find('.//head/publisher').text) 'publisher': './/head/publisher',
cover = meta.find('.//head/cover') 'cover' : './/head/cover', }
if cover != None and cover.text != None: d = {}
oeb.guide.add('cover', 'Cover', cover.text) for item in l:
node = meta.find(l[item])
if node != None:
d[item] = node.text if node.text != None else ''
else:
d[item] = ''
oeb.metadata.add('title', d['title'])
oeb.metadata.add('creator', d['creator'], attrib={'role':'aut'})
oeb.metadata.add('language', d['language'].lower().replace('_', '-'))
oeb.metadata.add('generator', d['generator'])
oeb.metadata.add('publisher', d['publisher'])
if d['cover'] != '':
oeb.guide.add('cover', 'Cover', d['cover'])
bookid = str(uuid.uuid4()) bookid = str(uuid.uuid4())
oeb.metadata.add('identifier', bookid, id='uuid_id', scheme='uuid') oeb.metadata.add('identifier', bookid, id='uuid_id', scheme='uuid')

View File

@ -29,8 +29,7 @@ class TXTOutput(OutputFormatPlugin):
OptionRecommendation(name='output_encoding', recommended_value='utf-8', OptionRecommendation(name='output_encoding', recommended_value='utf-8',
level=OptionRecommendation.LOW, level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. ' \ help=_('Specify the character encoding of the output document. ' \
'The default is utf-8. Note: This option is not honored by all ' \ 'The default is utf-8.')),
'formats.')),
OptionRecommendation(name='inline_toc', OptionRecommendation(name='inline_toc',
recommended_value=False, level=OptionRecommendation.LOW, recommended_value=False, level=OptionRecommendation.LOW,
help=_('Add Table of Contents to beginning of the book.')), help=_('Add Table of Contents to beginning of the book.')),

View File

@ -12,11 +12,15 @@ from PyQt4.Qt import QToolButton, QMenu, pyqtSignal, QIcon
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
from calibre.utils.smtp import config as email_config from calibre.utils.smtp import config as email_config
from calibre.constants import iswindows, isosx from calibre.constants import iswindows, isosx
from calibre.customize.ui import is_disabled
from calibre.devices.bambook.driver import BAMBOOK
class ShareConnMenu(QMenu): # {{{ class ShareConnMenu(QMenu): # {{{
connect_to_folder = pyqtSignal() connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal() connect_to_itunes = pyqtSignal()
connect_to_bambook = pyqtSignal()
config_email = pyqtSignal() config_email = pyqtSignal()
toggle_server = pyqtSignal() toggle_server = pyqtSignal()
dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
@ -34,6 +38,17 @@ class ShareConnMenu(QMenu): # {{{
self.connect_to_itunes_action = mitem self.connect_to_itunes_action = mitem
if not (iswindows or isosx): if not (iswindows or isosx):
mitem.setVisible(False) mitem.setVisible(False)
mitem = self.addAction(QIcon(I('devices/bambook.png')), _('Connect to Bambook'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_bambook.emit())
self.connect_to_bambook_action = mitem
bambook_visible = False
if not is_disabled(BAMBOOK):
device_ip = BAMBOOK.settings().extra_customization
if device_ip:
bambook_visible = True
self.connect_to_bambook_action.setVisible(bambook_visible)
self.addSeparator() self.addSeparator()
self.toggle_server_action = \ self.toggle_server_action = \
self.addAction(QIcon(I('network-server.png')), self.addAction(QIcon(I('network-server.png')),
@ -88,6 +103,7 @@ class ShareConnMenu(QMenu): # {{{
def set_state(self, device_connected): def set_state(self, device_connected):
self.connect_to_folder_action.setEnabled(not device_connected) self.connect_to_folder_action.setEnabled(not device_connected)
self.connect_to_itunes_action.setEnabled(not device_connected) self.connect_to_itunes_action.setEnabled(not device_connected)
self.connect_to_bambook_action.setEnabled(not device_connected)
# }}} # }}}
@ -126,6 +142,7 @@ class ConnectShareAction(InterfaceAction):
self.qaction.setMenu(self.share_conn_menu) self.qaction.setMenu(self.share_conn_menu)
self.share_conn_menu.connect_to_folder.connect(self.gui.connect_to_folder) self.share_conn_menu.connect_to_folder.connect(self.gui.connect_to_folder)
self.share_conn_menu.connect_to_itunes.connect(self.gui.connect_to_itunes) self.share_conn_menu.connect_to_itunes.connect(self.gui.connect_to_itunes)
self.share_conn_menu.connect_to_bambook.connect(self.gui.connect_to_bambook)
def location_selected(self, loc): def location_selected(self, loc):
enabled = loc == 'library' enabled = loc == 'library'

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
import os, collections, sys import os, collections, sys
from Queue import Queue from Queue import Queue
from PyQt4.Qt import QPixmap, QSize, QWidget, Qt, pyqtSignal, \ from PyQt4.Qt import QPixmap, QSize, QWidget, Qt, pyqtSignal, QUrl, \
QPropertyAnimation, QEasingCurve, QThread, QApplication, QFontInfo, \ QPropertyAnimation, QEasingCurve, QThread, QApplication, QFontInfo, \
QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette
from PyQt4.QtWebKit import QWebView from PyQt4.QtWebKit import QWebView
@ -18,7 +18,7 @@ from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS
from calibre.constants import preferred_encoding from calibre.constants import preferred_encoding
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
from calibre.gui2 import config, open_local_file from calibre.gui2 import config, open_local_file, open_url
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
# render_rows(data) {{{ # render_rows(data) {{{
@ -412,6 +412,12 @@ class BookDetails(QWidget): # {{{
self.view_specific_format.emit(int(id_), fmt) self.view_specific_format.emit(int(id_), fmt)
elif typ == 'devpath': elif typ == 'devpath':
open_local_file(val) open_local_file(val)
else:
try:
open_url(QUrl(link, QUrl.TolerantMode))
except:
import traceback
traceback.print_exc()
def mouseDoubleClickEvent(self, ev): def mouseDoubleClickEvent(self, ev):

View File

@ -259,6 +259,19 @@ class EditorWidget(QWebView): # {{{
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
def keyPressEvent(self, ev):
if ev.key() in (Qt.Key_Tab, Qt.Key_Escape, Qt.Key_Backtab):
ev.ignore()
else:
return QWebView.keyPressed(self, ev)
def keyReleaseEvent(self, ev):
if ev.key() in (Qt.Key_Tab, Qt.Key_Escape, Qt.Key_Backtab):
ev.ignore()
else:
return QWebView.keyReleased(self, ev)
# }}} # }}}
# Highlighter {{{ # Highlighter {{{

View File

@ -19,7 +19,7 @@ class PluginWidget(Widget, Ui_Form):
ICON = I('mimetypes/unknown.png') ICON = I('mimetypes/unknown.png')
def __init__(self, parent, get_option, get_help, db=None, book_id=None): def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, ['format', 'inline_toc']) Widget.__init__(self, parent, ['format', 'inline_toc', 'output_encoding'])
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id) self.initialize_options(get_option, get_help, db, book_id)

View File

@ -27,7 +27,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="opt_format"/> <widget class="QComboBox" name="opt_format"/>
</item> </item>
<item row="2" column="0"> <item row="3" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -40,13 +40,23 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="1" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="opt_inline_toc"> <widget class="QCheckBox" name="opt_inline_toc">
<property name="text"> <property name="text">
<string>&amp;Inline TOC</string> <string>&amp;Inline TOC</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Output Encoding:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="opt_output_encoding"/>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -17,6 +17,7 @@ class PluginWidget(Widget, Ui_Form):
ICON = I('mimetypes/unknown.png') ICON = I('mimetypes/unknown.png')
def __init__(self, parent, get_option, get_help, db=None, book_id=None): def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, ['inline_toc', 'full_image_depth']) Widget.__init__(self, parent, ['inline_toc', 'full_image_depth',
'output_encoding'])
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id) self.initialize_options(get_option, get_help, db, book_id)

View File

@ -14,7 +14,7 @@
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="2" column="0"> <item row="3" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -27,20 +27,30 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="opt_inline_toc"> <widget class="QCheckBox" name="opt_inline_toc">
<property name="text"> <property name="text">
<string>&amp;Inline TOC</string> <string>&amp;Inline TOC</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="opt_full_image_depth"> <widget class="QCheckBox" name="opt_full_image_depth">
<property name="text"> <property name="text">
<string>Do not reduce image size and depth</string> <string>Do not reduce image size and depth</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Output Encoding:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="opt_output_encoding"/>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -21,7 +21,8 @@ class PluginWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None): def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, Widget.__init__(self, parent,
['newline', 'max_line_length', 'force_max_line_length', ['newline', 'max_line_length', 'force_max_line_length',
'inline_toc', 'markdown_format', 'keep_links', 'keep_image_references']) 'inline_toc', 'markdown_format', 'keep_links', 'keep_image_references',
'output_encoding'])
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id) self.initialize_options(get_option, get_help, db, book_id)

View File

@ -27,7 +27,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="opt_newline"/> <widget class="QComboBox" name="opt_newline"/>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -40,7 +40,7 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="3" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="opt_inline_toc"> <widget class="QCheckBox" name="opt_inline_toc">
<property name="text"> <property name="text">
<string>&amp;Inline TOC</string> <string>&amp;Inline TOC</string>
@ -60,34 +60,44 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="opt_force_max_line_length"> <widget class="QCheckBox" name="opt_force_max_line_length">
<property name="text"> <property name="text">
<string>Force maximum line length</string> <string>Force maximum line length</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QCheckBox" name="opt_markdown_format"> <widget class="QCheckBox" name="opt_markdown_format">
<property name="text"> <property name="text">
<string>Apply Markdown formatting to text</string> <string>Apply Markdown formatting to text</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QCheckBox" name="opt_keep_links"> <widget class="QCheckBox" name="opt_keep_links">
<property name="text"> <property name="text">
<string>Do not remove links (&lt;a&gt; tags) before processing</string> <string>Do not remove links (&lt;a&gt; tags) before processing</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QCheckBox" name="opt_keep_image_references"> <widget class="QCheckBox" name="opt_keep_image_references">
<property name="text"> <property name="text">
<string>Do not remove image references before processing</string> <string>Do not remove image references before processing</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Output Encoding:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="opt_output_encoding"/>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -24,6 +24,7 @@ from calibre.utils.filenames import ascii_filename
from calibre.devices.errors import FreeSpaceError from calibre.devices.errors import FreeSpaceError
from calibre.devices.apple.driver import ITUNES_ASYNC from calibre.devices.apple.driver import ITUNES_ASYNC
from calibre.devices.folder_device.driver import FOLDER_DEVICE from calibre.devices.folder_device.driver import FOLDER_DEVICE
from calibre.devices.bambook.driver import BAMBOOK, BAMBOOKWifi
from calibre.ebooks.metadata.meta import set_metadata from calibre.ebooks.metadata.meta import set_metadata
from calibre.constants import DEBUG from calibre.constants import DEBUG
from calibre.utils.config import prefs, tweaks from calibre.utils.config import prefs, tweaks
@ -635,6 +636,10 @@ class DeviceMixin(object): # {{{
if dir is not None: if dir is not None:
self.device_manager.mount_device(kls=FOLDER_DEVICE, kind='folder', path=dir) self.device_manager.mount_device(kls=FOLDER_DEVICE, kind='folder', path=dir)
def connect_to_bambook(self):
self.device_manager.mount_device(kls=BAMBOOKWifi, kind='bambook',
path=BAMBOOK.settings().extra_customization)
def connect_to_itunes(self): def connect_to_itunes(self):
self.device_manager.mount_device(kls=ITUNES_ASYNC, kind='itunes', path=None) self.device_manager.mount_device(kls=ITUNES_ASYNC, kind='itunes', path=None)

View File

@ -9,7 +9,7 @@ from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \
QDialog, QPixmap, QGraphicsScene, QIcon, QSize QDialog, QPixmap, QGraphicsScene, QIcon, QSize
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
from calibre.gui2 import dynamic, open_local_file from calibre.gui2 import dynamic, open_local_file, open_url
from calibre import fit_image from calibre import fit_image
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -22,6 +22,8 @@ class BookInfo(QDialog, Ui_BookInfo):
self.setupUi(self) self.setupUi(self)
self.cover_pixmap = None self.cover_pixmap = None
self.comments.sizeHint = self.comments_size_hint self.comments.sizeHint = self.comments_size_hint
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
self.comments.linkClicked(self.link_clicked)
self.view_func = view_func self.view_func = view_func
@ -41,6 +43,8 @@ class BookInfo(QDialog, Ui_BookInfo):
screen_height = desktop.availableGeometry().height() - 100 screen_height = desktop.availableGeometry().height() - 100
self.resize(self.size().width(), screen_height) self.resize(self.size().width(), screen_height)
def link_clicked(self, url):
open_url(url)
def comments_size_hint(self): def comments_size_hint(self):
return QSize(350, 250) return QSize(350, 250)
@ -115,6 +119,7 @@ class BookInfo(QDialog, Ui_BookInfo):
lines = [x if x.strip() else '<br><br>' for x in lines] lines = [x if x.strip() else '<br><br>' for x in lines]
comments = '\n'.join(lines) comments = '\n'.join(lines)
self.comments.setHtml('<div>%s</div>' % comments) self.comments.setHtml('<div>%s</div>' % comments)
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
cdata = info.pop('cover', '') cdata = info.pop('cover', '')
self.cover_pixmap = QPixmap.fromImage(cdata) self.cover_pixmap = QPixmap.fromImage(cdata)
self.resize_cover() self.resize_cover()

View File

@ -5,6 +5,4 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import Qt
DEFAULT_SORT = ('timestamp', False) DEFAULT_SORT = ('timestamp', False)

View File

@ -336,7 +336,7 @@ class TagsView(QTreeView): # {{{
# If the number of user categories changed, if custom columns have come or # If the number of user categories changed, if custom columns have come or
# gone, or if columns have been hidden or restored, we must rebuild the # gone, or if columns have been hidden or restored, we must rebuild the
# model. Reason: it is much easier than reconstructing the browser tree. # model. Reason: it is much easier than reconstructing the browser tree.
def set_new_model(self, filter_categories_by = None): def set_new_model(self, filter_categories_by=None):
try: try:
self._model = TagsModel(self.db, parent=self, self._model = TagsModel(self.db, parent=self,
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
@ -1165,7 +1165,7 @@ class TagBrowserWidget(QWidget): # {{{
'containing the text "foo"')) 'containing the text "foo"'))
search_layout.addWidget(self.item_search) search_layout.addWidget(self.item_search)
self.search_button = QPushButton() self.search_button = QPushButton()
self.search_button.setText(_('Find!')) self.search_button.setText(_('&Find'))
self.search_button.setToolTip(_('Find the first/next matching item')) self.search_button.setToolTip(_('Find the first/next matching item'))
self.search_button.setFixedWidth(40) self.search_button.setFixedWidth(40)
search_layout.addWidget(self.search_button) search_layout.addWidget(self.search_button)

View File

@ -21,6 +21,7 @@ Environment variables
----------------------- -----------------------
* ``CALIBRE_CONFIG_DIRECTORY`` - sets the directory where configuration files are stored/read. * ``CALIBRE_CONFIG_DIRECTORY`` - sets the directory where configuration files are stored/read.
* ``CALIBRE_TEMP_DIR`` - sets the temporary directory used by calibre
* ``CALIBRE_OVERRIDE_DATABASE_PATH`` - allows you to specify the full path to metadata.db. Using this variable you can have metadata.db be in a location other than the library folder. Useful if your library folder is on a networked drive that does not support file locking. * ``CALIBRE_OVERRIDE_DATABASE_PATH`` - allows you to specify the full path to metadata.db. Using this variable you can have metadata.db be in a location other than the library folder. Useful if your library folder is on a networked drive that does not support file locking.
* ``CALIBRE_DEVELOP_FROM`` - Used to run from a calibre development environment. See :ref:`develop`. * ``CALIBRE_DEVELOP_FROM`` - Used to run from a calibre development environment. See :ref:`develop`.
* ``CALIBRE_OVERRIDE_LANG`` - Used to force the language used by the interface (ISO 639 language code) * ``CALIBRE_OVERRIDE_LANG`` - Used to force the language used by the interface (ISO 639 language code)

View File

@ -211,7 +211,7 @@ The following functions are available in addition to those described in single-f
* ``cmp(x, y, lt, eq, gt)`` -- compares x and y after converting both to numbers. Returns ``lt`` if x < y. Returns ``eq`` if x == y. Otherwise returns ``gt``. * ``cmp(x, y, lt, eq, gt)`` -- compares x and y after converting both to numbers. Returns ``lt`` if x < y. Returns ``eq`` if x == y. Otherwise returns ``gt``.
* ``divide(x, y)`` -- returns x / y. Throws an exception if either x or y are not numbers. * ``divide(x, y)`` -- returns x / y. Throws an exception if either x or y are not numbers.
* ``field(name)`` -- returns the metadata field named by ``name``. * ``field(name)`` -- returns the metadata field named by ``name``.
* ``eval(string)`` -- evaluates the string as a program, passing the local variables (those `assign`ed to). This permits using the template processor to construct complex results from local variables. * ``eval(string)`` -- evaluates the string as a program, passing the local variables (those ``assign`` ed to). This permits using the template processor to construct complex results from local variables.
* ``multiply(x, y)`` -- returns x * y. Throws an exception if either x or y are not numbers. * ``multiply(x, y)`` -- returns x * y. Throws an exception if either x or y are not numbers.
* ``print(a, b, ...)`` -- prints the arguments to standard output. Unless you start calibre from the command line (``calibre-debug -g``), the output will go to a black hole. * ``print(a, b, ...)`` -- prints the arguments to standard output. Unless you start calibre from the command line (``calibre-debug -g``), the output will go to a black hole.
* ``strcat(a, b, ...)`` -- can take any number of arguments. Returns a string formed by concatenating all the arguments. * ``strcat(a, b, ...)`` -- can take any number of arguments. Returns a string formed by concatenating all the arguments.
@ -268,7 +268,7 @@ The solution requires creating three composite columns. The first column is used
Title: Revenge of the Wrought-Iron Flamingos Title: Revenge of the Wrought-Iron Flamingos
Output: MLM [03] Revenge of the Wrought-Iron Flamingos Output: MLM [03] Revenge of the Wrought-Iron Flamingos
The following program produces the same results as the original recipe, using only one custom column to hold the results of a program that computes the special title value. The following program produces the same results as the original recipe, using only one custom column to hold the results of a program that computes the special title value::
Custom column: Custom column:
Name: #special_title Name: #special_title
@ -349,4 +349,4 @@ You might find the following tips useful.
* Templates can use other templates by referencing a composite custom column. * Templates can use other templates by referencing a composite custom column.
* In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{null}``. This template will always evaluate to an empty string. * In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{null}``. This template will always evaluate to an empty string.
* The technique described above to show numbers even if they have a zero value works with the standard field series_index. * The technique described above to show numbers even if they have a zero value works with the standard field series_index.

View File

@ -40,7 +40,7 @@ def base_dir():
_base_dir = td _base_dir = td
else: else:
_base_dir = tempfile.mkdtemp(prefix='%s_%s_tmp_'%(__appname__, _base_dir = tempfile.mkdtemp(prefix='%s_%s_tmp_'%(__appname__,
__version__)) __version__), dir=os.environ.get('CALIBRE_TEMP_DIR', None))
atexit.register(remove_dir, _base_dir) atexit.register(remove_dir, _base_dir)
return _base_dir return _base_dir