mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
pre .21 changes
This commit is contained in:
commit
543050779c
@ -1,24 +1,56 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Cyberpresse(BasicNewsRecipe):
|
||||
|
||||
title = u'Cyberpresse'
|
||||
__author__ = 'balok'
|
||||
__author__ = 'balok and Sujata Raman'
|
||||
description = 'Canadian news in French'
|
||||
language = 'fr'
|
||||
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
html2lrf_options = ['--left-margin=0','--right-margin=0','--top-margin=0','--bottom-margin=0']
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<body.*?<!-- END .centerbar -->', re.IGNORECASE | re.DOTALL), lambda match : '<BODY>'),
|
||||
(re.compile(r'<!-- END .entry -->.*?</body>', re.IGNORECASE | re.DOTALL), lambda match : '</BODY>'),
|
||||
(re.compile(r'<strong>Agrandir.*?</strong>', re.IGNORECASE | re.DOTALL), lambda match : '<br>'),
|
||||
]
|
||||
encoding = 'utf-8'
|
||||
|
||||
|
||||
feeds = [(u'Manchettes', u'http://www.cyberpresse.ca/rss/225.xml'),(u'Capitale nationale', u'http://www.cyberpresse.ca/rss/501.xml'),(u'Opinions', u'http://www.cyberpresse.ca/rss/977.xml'),(u'Insolite', u'http://www.cyberpresse.ca/rss/279.xml')]
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'article-page'}),
|
||||
dict(name='div', attrs={'id':'articlePage'}),
|
||||
]
|
||||
|
||||
extra_css = '''
|
||||
.photodata{font-family:Arial,Helvetica,Verdana,sans-serif;color: #999999; font-size: 90%; }
|
||||
h1{font-family:Georgia,Times,serif ; font-size: large; }
|
||||
.amorce{font-family:Arial,Helvetica,Verdana,sans-serif; font-weight:bold;}
|
||||
.article-page{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||
#articlePage{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||
.auteur{font-family:Georgia,Times,sans-serif; font-size: 90%; color:#006699 ;}
|
||||
.bodyText{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||
.byLine{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: 90%;}
|
||||
.entry{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||
.minithumb-auteurs{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: 90%; }
|
||||
a{color:#003399; font-weight:bold; }
|
||||
'''
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':['centerbar','colspan','share-module']}),
|
||||
dict(name='p', attrs={'class':['zoom']}),
|
||||
dict(name='ul', attrs={'class':['stories']}),
|
||||
dict(name='h4', attrs={'class':['general-cat']}),
|
||||
]
|
||||
|
||||
feeds = [(u'Manchettes', u'http://www.cyberpresse.ca/rss/225.xml'),
|
||||
(u'Capitale nationale', u'http://www.cyberpresse.ca/rss/501.xml'),
|
||||
(u'Opinions', u'http://www.cyberpresse.ca/rss/977.xml'),
|
||||
(u'Insolite', u'http://www.cyberpresse.ca/rss/279.xml')
|
||||
]
|
||||
|
||||
def postprocess_html(self, soup, first):
|
||||
|
||||
for tag in soup.findAll(name=['i','strong']):
|
||||
tag.name = 'div'
|
||||
|
||||
return soup
|
||||
|
||||
|
||||
|
@ -16,9 +16,11 @@ class Economist(BasicNewsRecipe):
|
||||
language = 'en'
|
||||
|
||||
__author__ = "Kovid Goyal"
|
||||
description = 'Global news and current affairs from a European perspective'
|
||||
oldest_article = 7.0
|
||||
INDEX = 'http://www.economist.com/printedition'
|
||||
description = ('Global news and current affairs from a European perspective.'
|
||||
' Needs a subscription from ')+INDEX
|
||||
|
||||
oldest_article = 7.0
|
||||
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
|
||||
remove_tags = [dict(name=['script', 'noscript', 'title'])]
|
||||
remove_tags_before = dict(name=lambda tag: tag.name=='title' and tag.parent.name=='body')
|
||||
|
@ -15,8 +15,8 @@ class Guardian(BasicNewsRecipe):
|
||||
__author__ = 'Seabound and Sujata Raman'
|
||||
language = 'en_GB'
|
||||
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 20
|
||||
#oldest_article = 7
|
||||
#max_articles_per_feed = 100
|
||||
remove_javascript = True
|
||||
|
||||
timefmt = ' [%a, %d %b %Y]'
|
||||
@ -45,26 +45,94 @@ class Guardian(BasicNewsRecipe):
|
||||
|
||||
|
||||
|
||||
feeds = [
|
||||
('Front Page', 'http://www.guardian.co.uk/rss'),
|
||||
('Business', 'http://www.guardian.co.uk/business/rss'),
|
||||
('Sport', 'http://www.guardian.co.uk/sport/rss'),
|
||||
('Culture', 'http://www.guardian.co.uk/culture/rss'),
|
||||
('Money', 'http://www.guardian.co.uk/money/rss'),
|
||||
('Life & Style', 'http://www.guardian.co.uk/lifeandstyle/rss'),
|
||||
('Travel', 'http://www.guardian.co.uk/travel/rss'),
|
||||
('Environment', 'http://www.guardian.co.uk/environment/rss'),
|
||||
('Comment','http://www.guardian.co.uk/commentisfree/rss'),
|
||||
]
|
||||
# feeds = [
|
||||
# ('Front Page', 'http://www.guardian.co.uk/rss'),
|
||||
# ('Business', 'http://www.guardian.co.uk/business/rss'),
|
||||
# ('Sport', 'http://www.guardian.co.uk/sport/rss'),
|
||||
# ('Culture', 'http://www.guardian.co.uk/culture/rss'),
|
||||
# ('Money', 'http://www.guardian.co.uk/money/rss'),
|
||||
# ('Life & Style', 'http://www.guardian.co.uk/lifeandstyle/rss'),
|
||||
# ('Travel', 'http://www.guardian.co.uk/travel/rss'),
|
||||
# ('Environment', 'http://www.guardian.co.uk/environment/rss'),
|
||||
# ('Comment','http://www.guardian.co.uk/commentisfree/rss'),
|
||||
# ]
|
||||
|
||||
def get_article_url(self, article):
|
||||
url = article.get('guid', None)
|
||||
if '/video/' in url or '/flyer/' in url or '/quiz/' in url or \
|
||||
'/gallery/' in url or 'ivebeenthere' in url or \
|
||||
'pickthescore' in url or 'audioslideshow' in url :
|
||||
url = None
|
||||
return url
|
||||
# def get_article_url(self, article):
|
||||
# url = article.get('guid', None)
|
||||
# if '/video/' in url or '/flyer/' in url or '/quiz/' in url or \
|
||||
# '/gallery/' in url or 'ivebeenthere' in url or \
|
||||
# 'pickthescore' in url or 'audioslideshow' in url :
|
||||
# url = None
|
||||
# return url
|
||||
|
||||
def parse_index(self):
|
||||
|
||||
articles = []
|
||||
|
||||
|
||||
soup = self.index_to_soup('http://www.guardian.co.uk/theguardian')
|
||||
# find cover pic
|
||||
img = soup.find( 'img',attrs ={'alt':'Guardian digital edition'})
|
||||
|
||||
if img is not None:
|
||||
self.cover_url = img['src']
|
||||
|
||||
# end find cover pic
|
||||
for li in soup.findAll( 'li'):
|
||||
|
||||
if li.a and li.a.has_key('href'):
|
||||
url = li.a['href']
|
||||
if 'mainsection' in url:
|
||||
|
||||
|
||||
#find the articles in the Main Section
|
||||
|
||||
soup = self.index_to_soup(url)
|
||||
|
||||
for tag in soup.findAll('h3'):
|
||||
for a in tag.findAll('a'):
|
||||
|
||||
if a and a.has_key('href'):
|
||||
|
||||
url2 = a['href']
|
||||
|
||||
else:
|
||||
url2 =''
|
||||
|
||||
title = self.tag_to_string(a)
|
||||
#eliminate duplicates
|
||||
if len(articles) == 0:
|
||||
desc = 'Main Section'
|
||||
date = ''
|
||||
articles.append({
|
||||
'title':title,
|
||||
'date':date,
|
||||
'url':url2,
|
||||
'description':desc,
|
||||
})
|
||||
else:
|
||||
if len(articles) > 0:
|
||||
if {'title':title,'date':date,'url':url2,'description':desc} in articles:
|
||||
ulrl2 = ''
|
||||
#eliminate duplicates
|
||||
else:
|
||||
|
||||
desc = 'Main Section'
|
||||
date = ''
|
||||
articles.append({
|
||||
'title':title,
|
||||
'date':date,
|
||||
'url':url2,
|
||||
'description':desc,
|
||||
})
|
||||
#find the articles in the Main Section
|
||||
else:
|
||||
url =''
|
||||
|
||||
|
||||
|
||||
|
||||
return [('Current Issue', articles)]
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
|
71
resources/viewer/bookmarks.js
Normal file
71
resources/viewer/bookmarks.js
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* bookmarks management
|
||||
* Copyright 2008 Kovid Goyal
|
||||
* License: GNU GPL v3
|
||||
*/
|
||||
|
||||
function selector_in_parent(elem) {
|
||||
var num = elem.prevAll().length;
|
||||
var sel = " > *:eq("+num+") ";
|
||||
return sel;
|
||||
}
|
||||
|
||||
function selector(elem) {
|
||||
var obj = elem;
|
||||
var sel = "";
|
||||
while (obj[0] != document) {
|
||||
sel = selector_in_parent(obj) + sel;
|
||||
obj = obj.parent();
|
||||
}
|
||||
return sel;
|
||||
}
|
||||
|
||||
function find_closest_enclosing_block(top) {
|
||||
var START = top-1000;
|
||||
var STOP = top;
|
||||
var matches = [];
|
||||
var elem, temp;
|
||||
var width = 1000;
|
||||
|
||||
for (y = START; y < STOP; y += 20) {
|
||||
for ( x = 0; x < width; x += 20) {
|
||||
elem = document.elementFromPoint(x, y);
|
||||
try {
|
||||
elem = $(elem);
|
||||
temp = elem.offset().top
|
||||
matches.push(elem);
|
||||
if (Math.abs(temp - START) < 25) { y = STOP; break}
|
||||
} catch(error) {}
|
||||
}
|
||||
}
|
||||
|
||||
var miny = Math.abs(matches[0].offset().top - START), min_elem = matches[0];
|
||||
|
||||
for (i = 1; i < matches.length; i++) {
|
||||
elem = matches[i];
|
||||
temp = Math.abs(elem.offset().top - START);
|
||||
if ( temp < miny ) { miny = temp; min_elem = elem; }
|
||||
}
|
||||
return min_elem;
|
||||
}
|
||||
|
||||
function calculate_bookmark(y) {
|
||||
var elem = find_closest_enclosing_block(y);
|
||||
var sel = selector(elem);
|
||||
var ratio = (y - elem.offset().top)/elem.height();
|
||||
if (ratio > 1) { ratio = 1; }
|
||||
if (ratio < 0) { ratio = 0; }
|
||||
return sel + "|" + ratio;
|
||||
}
|
||||
|
||||
function animated_scrolling_done() {
|
||||
window.py_bridge.animated_scroll_done();
|
||||
}
|
||||
|
||||
function scroll_to_bookmark(bookmark) {
|
||||
bm = bookmark.split("|");
|
||||
var ratio = 0.7 * parseFloat(bm[1]);
|
||||
$.scrollTo($(bm[0]), 1000,
|
||||
{over:ratio, onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
||||
}
|
||||
|
26
resources/viewer/hyphenation.js
Normal file
26
resources/viewer/hyphenation.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* bookmarks management
|
||||
* Copyright 2008 Kovid Goyal
|
||||
* License: GNU GPL v3
|
||||
*/
|
||||
|
||||
function init_hyphenate() {
|
||||
window.py_bridge.init_hyphenate();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init_hyphenate, false);
|
||||
|
||||
function do_hyphenation(lang) {
|
||||
Hyphenator.config(
|
||||
{
|
||||
'minwordlength' : 6,
|
||||
//'hyphenchar' : '|',
|
||||
'displaytogglebox' : false,
|
||||
'remoteloading' : false,
|
||||
'onerrorhandler' : function (e) {
|
||||
window.py_bridge.debug(e);
|
||||
}
|
||||
});
|
||||
Hyphenator.hyphenate(document.body, lang);
|
||||
}
|
||||
|
62
resources/viewer/referencing.js
Normal file
62
resources/viewer/referencing.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* reference management
|
||||
* Copyright 2008 Kovid Goyal
|
||||
* License: GNU GPL v3
|
||||
*/
|
||||
|
||||
|
||||
|
||||
var reference_old_bgcol = "transparent";
|
||||
var reference_prefix = "1.";
|
||||
|
||||
function show_reference_panel(ref) {
|
||||
panel = $("#calibre_reference_panel");
|
||||
if (panel.length < 1) {
|
||||
$(document.body).append('<div id="calibre_reference_panel" style="top:20px; left:20px; padding-left:30px; padding-right:30px; font:monospace normal;text-align:center; z-index:10000; background: beige; border:red ridge 2px; position:absolute;"><h5>Paragraph</h5><p style="text-indent:0pt">None</p></div>')
|
||||
panel = $("#calibre_reference_panel");
|
||||
}
|
||||
$("> p", panel).text(ref);
|
||||
panel.css({top:(window.pageYOffset+20)+"px"});
|
||||
panel.fadeIn(500);
|
||||
}
|
||||
|
||||
function toggle_reference(e) {
|
||||
p = $(this);
|
||||
if (e.type == "mouseenter") {
|
||||
reference_old_bgcol = p.css("background-color");
|
||||
p.css({backgroundColor:"beige"});
|
||||
var i = 0;
|
||||
var paras = $("p");
|
||||
for (j = 0; j < paras.length; j++,i++) {
|
||||
if (paras[j] == p[0]) break;
|
||||
}
|
||||
show_reference_panel(reference_prefix+(i+1) );
|
||||
} else {
|
||||
p.css({backgroundColor:reference_old_bgcol});
|
||||
panel = $("#calibre_reference_panel").hide();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function enter_reference_mode() {
|
||||
$("p").bind("mouseenter mouseleave", toggle_reference);
|
||||
}
|
||||
|
||||
function leave_reference_mode() {
|
||||
$("p").unbind("mouseenter mouseleave", toggle_reference);
|
||||
}
|
||||
|
||||
function goto_reference(ref) {
|
||||
var tokens = ref.split(".");
|
||||
if (tokens.length != 2) {alert("Invalid reference: "+ref); return;}
|
||||
var num = parseInt(tokens[1]);
|
||||
if (isNaN(num)) {alert("Invalid reference: "+ref); return;}
|
||||
num -= 1;
|
||||
if (num < 0) {alert("Invalid reference: "+ref); return;}
|
||||
var p = $("p");
|
||||
if (num >= p.length) {alert("Reference not found: "+ref); return;}
|
||||
$.scrollTo($(p[num]), 1000,
|
||||
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
||||
}
|
||||
|
||||
|
@ -386,7 +386,7 @@ def main():
|
||||
{
|
||||
'optimize' : 2,
|
||||
'dist_dir' : 'build/py2app',
|
||||
'argv_emulation' : False,
|
||||
'argv_emulation' : True,
|
||||
'iconfile' : icon,
|
||||
'frameworks': ['libusb.dylib', 'libunrar.dylib'],
|
||||
'includes' : ['sip', 'pkg_resources', 'PyQt4.QtXml',
|
||||
|
@ -19,7 +19,7 @@ class NUUT2(USBMS):
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
# Ordered list of supported formats
|
||||
FORMATS = ['epub', 'pdft', 'txt']
|
||||
FORMATS = ['epub', 'pdf', 'txt']
|
||||
DRM_FORMATS = ['epub']
|
||||
|
||||
VENDOR_ID = [0x140e]
|
||||
|
@ -107,6 +107,9 @@ class FB2MLizer(object):
|
||||
|
||||
def get_cover_page(self):
|
||||
output = u''
|
||||
if 'cover' in self.oeb_book.guide:
|
||||
output += '<image xlink:href="#cover.jpg" />'
|
||||
self.image_hrefs[self.oeb_book.guide['cover'].href] = 'cover.jpg'
|
||||
if 'titlepage' in self.oeb_book.guide:
|
||||
self.log.debug('Generating cover page...')
|
||||
href = self.oeb_book.guide['titlepage'].href
|
||||
|
@ -34,6 +34,7 @@ def metadata_from_formats(formats):
|
||||
mi = metadata_from_filename(list(iter(formats))[0])
|
||||
if not mi.authors:
|
||||
mi.authors = [_('Unknown')]
|
||||
return mi
|
||||
|
||||
def _metadata_from_formats(formats):
|
||||
mi = MetaInformation(None, None)
|
||||
|
@ -19,7 +19,7 @@ from calibre.utils.zipfile import safe_replace, ZipFile
|
||||
from calibre.utils.config import DynamicConfig
|
||||
from calibre.utils.logging import Log
|
||||
from calibre.ebooks.epub.output import EPUBOutput
|
||||
from calibre import guess_type
|
||||
from calibre import guess_type, prints
|
||||
|
||||
TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8')
|
||||
|
||||
@ -99,29 +99,63 @@ class EbookIterator(object):
|
||||
if text in open(path, 'rb').read().decode(path.encoding).lower():
|
||||
return i
|
||||
|
||||
def find_missing_css_files(self):
|
||||
for x in os.walk(os.path.dirname(self.pathtoopf)):
|
||||
for f in x[-1]:
|
||||
if f.endswith('.css'):
|
||||
yield os.path.join(x[0], f)
|
||||
|
||||
def find_declared_css_files(self):
|
||||
for item in self.opf.manifest:
|
||||
if item.mime_type and 'css' in item.mime_type.lower():
|
||||
yield item.path
|
||||
|
||||
def find_embedded_fonts(self):
|
||||
'''
|
||||
This will become unnecessary once Qt WebKit supports the @font-face rule.
|
||||
'''
|
||||
for item in self.opf.manifest:
|
||||
if item.mime_type and 'css' in item.mime_type.lower():
|
||||
css = open(item.path, 'rb').read().decode('utf-8', 'replace')
|
||||
for match in re.compile(r'@font-face\s*{([^}]+)}').finditer(css):
|
||||
block = match.group(1)
|
||||
family = re.compile(r'font-family\s*:\s*([^;]+)').search(block)
|
||||
url = re.compile(r'url\s*\([\'"]*(.+?)[\'"]*\)', re.DOTALL).search(block)
|
||||
if url:
|
||||
path = url.group(1).split('/')
|
||||
path = os.path.join(os.path.dirname(item.path), *path)
|
||||
id = QFontDatabase.addApplicationFont(path)
|
||||
if id != -1:
|
||||
families = [unicode(f) for f in QFontDatabase.applicationFontFamilies(id)]
|
||||
if family:
|
||||
family = family.group(1).strip().replace('"', '')
|
||||
if family not in families:
|
||||
print 'WARNING: Family aliasing not supported:', block
|
||||
else:
|
||||
print 'Loaded embedded font:', repr(family)
|
||||
css_files = set(self.find_declared_css_files())
|
||||
if not css_files:
|
||||
css_files = set(self.find_missing_css_files())
|
||||
bad_map = {}
|
||||
font_family_pat = re.compile(r'font-family\s*:\s*([^;]+)')
|
||||
for csspath in css_files:
|
||||
css = open(csspath, 'rb').read().decode('utf-8', 'replace')
|
||||
for match in re.compile(r'@font-face\s*{([^}]+)}').finditer(css):
|
||||
block = match.group(1)
|
||||
family = font_family_pat.search(block)
|
||||
url = re.compile(r'url\s*\([\'"]*(.+?)[\'"]*\)', re.DOTALL).search(block)
|
||||
if url:
|
||||
path = url.group(1).split('/')
|
||||
path = os.path.join(os.path.dirname(csspath), *path)
|
||||
if not os.access(path, os.R_OK):
|
||||
continue
|
||||
id = QFontDatabase.addApplicationFont(path)
|
||||
if id != -1:
|
||||
families = [unicode(f) for f in QFontDatabase.applicationFontFamilies(id)]
|
||||
if family:
|
||||
family = family.group(1).strip().replace('"', '')
|
||||
bad_map[family] = families[0]
|
||||
if family not in families:
|
||||
prints('WARNING: Family aliasing not fully supported.')
|
||||
prints('\tDeclared family: %s not in actual families: %s'
|
||||
% (family, families))
|
||||
else:
|
||||
prints('Loaded embedded font:', repr(family))
|
||||
if bad_map:
|
||||
def prepend_embedded_font(match):
|
||||
for bad, good in bad_map.items():
|
||||
if bad in match.group(1):
|
||||
prints('Substituting font family: %s -> %s'%(bad, good))
|
||||
return match.group().replace(bad, '"%s"'%good)
|
||||
|
||||
for csspath in css_files:
|
||||
with open(csspath, 'r+b') as f:
|
||||
css = f.read()
|
||||
css = font_family_pat.sub(prepend_embedded_font, css)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(css)
|
||||
|
||||
def __enter__(self, processed=False):
|
||||
self.delete_on_exit = []
|
||||
|
@ -82,13 +82,16 @@ class RBMLizer(object):
|
||||
|
||||
def get_cover_page(self):
|
||||
output = u''
|
||||
if 'cover' in self.oeb_book.guide:
|
||||
if self.name_map.get(self.oeb_book.guide['cover'].href, None):
|
||||
output += '<IMG SRC="%s">' % self.name_map[self.oeb_book.guide['cover'].href]
|
||||
if 'titlepage' in self.oeb_book.guide:
|
||||
self.log.debug('Generating cover page...')
|
||||
href = self.oeb_book.guide['titlepage'].href
|
||||
item = self.oeb_book.manifest.hrefs[href]
|
||||
if item.spine_position is None:
|
||||
stylizer = Stylizer(item.data, item.href, self.oeb_book, self.opts.output_profile)
|
||||
output += self.dump_text(item.data.find(XHTML('body')), stylizer, item)
|
||||
output += ''.join(self.dump_text(item.data.find(XHTML('body')), stylizer, item))
|
||||
return output
|
||||
|
||||
def get_toc(self):
|
||||
@ -152,7 +155,7 @@ class RBMLizer(object):
|
||||
if tag in IMAGE_TAGS:
|
||||
if elem.attrib.get('src', None):
|
||||
if page.abshref(elem.attrib['src']) not in self.name_map.keys():
|
||||
self.name_map[page.abshref(elem.attrib['src'])] = unique_name('%s' % len(self.image_hrefs.keys()), self.image_hrefs.keys(), self.name_map.keys())
|
||||
self.name_map[page.abshref(elem.attrib['src'])] = unique_name('%s' % len(self.name_map.keys()), self.name_map.keys())
|
||||
text.append('<IMG SRC="%s">' % self.name_map[page.abshref(elem.attrib['src'])])
|
||||
|
||||
rb_tag = tag.upper() if tag in TAGS else None
|
||||
|
@ -274,6 +274,7 @@ class GetMetadata(QObject):
|
||||
self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi)
|
||||
|
||||
class TableView(QTableView):
|
||||
|
||||
def __init__(self, parent):
|
||||
QTableView.__init__(self, parent)
|
||||
self.read_settings()
|
||||
@ -585,8 +586,11 @@ def build_forms(srcdir, info=None):
|
||||
if form.endswith('viewer%smain.ui'%os.sep):
|
||||
info('\t\tPromoting WebView')
|
||||
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
|
||||
dat = dat.replace('from PyQt4 import QtWebKit', '')
|
||||
if iswindows:
|
||||
dat = dat.replace('self.view = QWebView(', 'self.view = DocumentView(')
|
||||
dat = dat.replace('from QtWebKit.QWebView import QWebView', '')
|
||||
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
|
||||
dat += '\nQtWebKit'
|
||||
|
||||
open(compiled_form, 'wb').write(dat)
|
||||
|
||||
|
@ -742,6 +742,16 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
|
||||
########################## Connect to device ##############################
|
||||
|
||||
def save_device_view_settings(self):
|
||||
model = self.location_view.model()
|
||||
self.memory_view.write_settings()
|
||||
for x in range(model.rowCount()):
|
||||
if x > 1:
|
||||
if model.location_for_row(x) == 'carda':
|
||||
self.card_a_view.write_settings()
|
||||
elif model.location_for_row(x) == 'cardb':
|
||||
self.carb_b_view.write_settings()
|
||||
|
||||
def device_detected(self, connected):
|
||||
'''
|
||||
Called when a device is connected to the computer.
|
||||
@ -757,6 +767,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.device_connected = True
|
||||
self._sync_menu.enable_device_actions(True, self.device_manager.device.card_prefix())
|
||||
else:
|
||||
self.save_device_view_settings()
|
||||
self.device_connected = False
|
||||
self._sync_menu.enable_device_actions(False)
|
||||
self.location_view.model().update_devices()
|
||||
@ -765,7 +776,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.device_info = ' '
|
||||
if self.current_view() != self.library_view:
|
||||
self.status_bar.reset_info()
|
||||
self.location_selected('library')
|
||||
self.location_view.setCurrentIndex(self.location_view.model().index(0))
|
||||
|
||||
def info_read(self, job):
|
||||
'''
|
||||
@ -807,6 +818,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.card_b_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||
view.sortByColumn(3, Qt.DescendingOrder)
|
||||
view.read_settings()
|
||||
if not view.restore_column_widths():
|
||||
view.resizeColumnsToContents()
|
||||
view.resizeRowsToContents()
|
||||
@ -1662,7 +1674,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
dynamic.set('sort_column', self.library_view.model().sorted_on)
|
||||
self.library_view.write_settings()
|
||||
if self.device_connected:
|
||||
self.memory_view.write_settings()
|
||||
self.save_device_view_settings()
|
||||
|
||||
def restart(self):
|
||||
self.quit(restart=True)
|
||||
|
@ -15,11 +15,12 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
||||
from calibre.utils.config import Config, StringConfig
|
||||
from calibre.utils.localization import get_language
|
||||
from calibre.gui2.viewer.config_ui import Ui_Dialog
|
||||
from calibre.gui2.viewer.js import bookmarks, referencing, hyphenation
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.constants import iswindows
|
||||
from calibre import prints, guess_type
|
||||
|
||||
bookmarks = referencing = hyphenation = jquery = jquery_scrollTo = hyphenator = None
|
||||
|
||||
def load_builtin_fonts():
|
||||
base = P('fonts/liberation/*.ttf')
|
||||
for f in glob.glob(base):
|
||||
@ -192,15 +193,24 @@ class Document(QWebPage):
|
||||
self.hyphenate_default_lang = opts.hyphenate_default_lang
|
||||
|
||||
def load_javascript_libraries(self):
|
||||
global bookmarks, referencing, hyphenation, jquery, jquery_scrollTo, hyphenator
|
||||
self.mainFrame().addToJavaScriptWindowObject("py_bridge", self)
|
||||
jquery = open(P('content_server/jquery.js'), 'rb').read()
|
||||
jquery_scrollTo = open(P('viewer/jquery_scrollTo.js'), 'rb').read()
|
||||
hyphenator = open(P('viewer/hyphenate/Hyphenator.js'),
|
||||
'rb').read().decode('utf-8')
|
||||
if jquery is None:
|
||||
jquery = P('content_server/jquery.js', data=True)
|
||||
if jquery_scrollTo is None:
|
||||
jquery_scrollTo = P('viewer/jquery_scrollTo.js', data=True)
|
||||
if hyphenator is None:
|
||||
hyphenator = P('viewer/hyphenate/Hyphenator.js', data=True).decode('utf-8')
|
||||
self.javascript(jquery)
|
||||
self.javascript(jquery_scrollTo)
|
||||
if bookmarks is None:
|
||||
bookmarks = P('viewer/bookmarks.js', data=True)
|
||||
self.javascript(bookmarks)
|
||||
if referencing is None:
|
||||
referencing = P('viewer/referencing.js', data=True)
|
||||
self.javascript(referencing)
|
||||
if hyphenation is None:
|
||||
hyphenation = P('viewer/hyphenation.js', data=True)
|
||||
self.javascript(hyphenation)
|
||||
default_lang = self.hyphenate_default_lang
|
||||
lang = self.current_language
|
||||
@ -333,6 +343,7 @@ class Document(QWebPage):
|
||||
def width(self):
|
||||
return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results
|
||||
|
||||
|
||||
class EntityDeclarationProcessor(object):
|
||||
|
||||
def __init__(self, html):
|
||||
@ -508,6 +519,7 @@ class DocumentView(QWebView):
|
||||
|
||||
@classmethod
|
||||
def test_line(cls, img, y):
|
||||
'Test if line contains pixels of exactly the same color'
|
||||
start = img.pixel(0, y)
|
||||
for i in range(1, img.width()):
|
||||
if img.pixel(i, y) != start:
|
||||
@ -517,6 +529,7 @@ class DocumentView(QWebView):
|
||||
def find_next_blank_line(self, overlap):
|
||||
img = QImage(self.width(), overlap, QImage.Format_ARGB32)
|
||||
painter = QPainter(img)
|
||||
# Render a region of width x overlap pixels atthe bottom of the current viewport
|
||||
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
|
||||
painter.end()
|
||||
for i in range(overlap-1, -1, -1):
|
||||
@ -542,18 +555,20 @@ class DocumentView(QWebView):
|
||||
self.manager.scrolled(self.scroll_fraction)
|
||||
|
||||
def next_page(self):
|
||||
delta_y = self.document.window_height - 25
|
||||
window_height = self.document.window_height
|
||||
delta_y = window_height - 25
|
||||
if self.document.at_bottom:
|
||||
if self.manager is not None:
|
||||
self.manager.next_document()
|
||||
else:
|
||||
opos = self.document.ypos
|
||||
lower_limit = opos + delta_y
|
||||
max_y = self.document.height - self.document.window_height
|
||||
max_y = self.document.height - window_height
|
||||
lower_limit = min(max_y, lower_limit)
|
||||
if lower_limit > opos:
|
||||
self.document.scroll_to(self.document.xpos, lower_limit)
|
||||
self.find_next_blank_line( self.height() - (self.document.ypos-opos) )
|
||||
actually_scrolled = self.document.ypos - opos
|
||||
self.find_next_blank_line(window_height - actually_scrolled)
|
||||
if self.manager is not None:
|
||||
self.manager.scrolled(self.scroll_fraction)
|
||||
|
||||
|
@ -1,156 +0,0 @@
|
||||
bookmarks = '''
|
||||
|
||||
function selector_in_parent(elem) {
|
||||
var num = elem.prevAll().length;
|
||||
var sel = " > *:eq("+num+") ";
|
||||
return sel;
|
||||
}
|
||||
|
||||
function selector(elem) {
|
||||
var obj = elem;
|
||||
var sel = "";
|
||||
while (obj[0] != document) {
|
||||
sel = selector_in_parent(obj) + sel;
|
||||
obj = obj.parent();
|
||||
}
|
||||
return sel;
|
||||
}
|
||||
|
||||
function find_closest_enclosing_block(top) {
|
||||
var START = top-1000;
|
||||
var STOP = top;
|
||||
var matches = [];
|
||||
var elem, temp;
|
||||
var width = 1000;
|
||||
|
||||
for (y = START; y < STOP; y += 20) {
|
||||
for ( x = 0; x < width; x += 20) {
|
||||
elem = document.elementFromPoint(x, y);
|
||||
try {
|
||||
elem = $(elem);
|
||||
temp = elem.offset().top
|
||||
matches.push(elem);
|
||||
if (Math.abs(temp - START) < 25) { y = STOP; break}
|
||||
} catch(error) {}
|
||||
}
|
||||
}
|
||||
|
||||
var miny = Math.abs(matches[0].offset().top - START), min_elem = matches[0];
|
||||
|
||||
for (i = 1; i < matches.length; i++) {
|
||||
elem = matches[i];
|
||||
temp = Math.abs(elem.offset().top - START);
|
||||
if ( temp < miny ) { miny = temp; min_elem = elem; }
|
||||
}
|
||||
return min_elem;
|
||||
}
|
||||
|
||||
function calculate_bookmark(y) {
|
||||
var elem = find_closest_enclosing_block(y);
|
||||
var sel = selector(elem);
|
||||
var ratio = (y - elem.offset().top)/elem.height();
|
||||
if (ratio > 1) { ratio = 1; }
|
||||
if (ratio < 0) { ratio = 0; }
|
||||
return sel + "|" + ratio;
|
||||
}
|
||||
|
||||
function animated_scrolling_done() {
|
||||
window.py_bridge.animated_scroll_done();
|
||||
}
|
||||
|
||||
function scroll_to_bookmark(bookmark) {
|
||||
bm = bookmark.split("|");
|
||||
var ratio = 0.7 * parseFloat(bm[1]);
|
||||
$.scrollTo($(bm[0]), 1000,
|
||||
{over:ratio, onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
referencing = '''
|
||||
var reference_old_bgcol = "transparent";
|
||||
var reference_prefix = "1.";
|
||||
|
||||
function show_reference_panel(ref) {
|
||||
panel = $("#calibre_reference_panel");
|
||||
if (panel.length < 1) {
|
||||
$(document.body).append('<div id="calibre_reference_panel" style="top:20px; left:20px; padding-left:30px; padding-right:30px; font:monospace normal;text-align:center; z-index:10000; background: beige; border:red ridge 2px; position:absolute;"><h5>Paragraph</h5><p style="text-indent:0pt">None</p></div>')
|
||||
panel = $("#calibre_reference_panel");
|
||||
}
|
||||
$("> p", panel).text(ref);
|
||||
panel.css({top:(window.pageYOffset+20)+"px"});
|
||||
panel.fadeIn(500);
|
||||
}
|
||||
|
||||
function toggle_reference(e) {
|
||||
p = $(this);
|
||||
if (e.type == "mouseenter") {
|
||||
reference_old_bgcol = p.css("background-color");
|
||||
p.css({backgroundColor:"beige"});
|
||||
var i = 0;
|
||||
var paras = $("p");
|
||||
for (j = 0; j < paras.length; j++,i++) {
|
||||
if (paras[j] == p[0]) break;
|
||||
}
|
||||
show_reference_panel(reference_prefix+(i+1) );
|
||||
} else {
|
||||
p.css({backgroundColor:reference_old_bgcol});
|
||||
panel = $("#calibre_reference_panel").hide();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function enter_reference_mode() {
|
||||
$("p").bind("mouseenter mouseleave", toggle_reference);
|
||||
}
|
||||
|
||||
function leave_reference_mode() {
|
||||
$("p").unbind("mouseenter mouseleave", toggle_reference);
|
||||
}
|
||||
|
||||
function goto_reference(ref) {
|
||||
var tokens = ref.split(".");
|
||||
if (tokens.length != 2) {alert("Invalid reference: "+ref); return;}
|
||||
var num = parseInt(tokens[1]);
|
||||
if (isNaN(num)) {alert("Invalid reference: "+ref); return;}
|
||||
num -= 1;
|
||||
if (num < 0) {alert("Invalid reference: "+ref); return;}
|
||||
var p = $("p");
|
||||
if (num >= p.length) {alert("Reference not found: "+ref); return;}
|
||||
$.scrollTo($(p[num]), 1000,
|
||||
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
test = '''
|
||||
$(document.body).click(function(e) {
|
||||
bm = calculate_bookmark(e.pageY);
|
||||
scroll_to_bookmark(bm);
|
||||
});
|
||||
|
||||
$(document).ready(enter_reference_mode);
|
||||
|
||||
'''
|
||||
|
||||
hyphenation = '''
|
||||
function init_hyphenate() {
|
||||
window.py_bridge.init_hyphenate();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init_hyphenate, false);
|
||||
|
||||
function do_hyphenation(lang) {
|
||||
Hyphenator.config(
|
||||
{
|
||||
'minwordlength' : 6,
|
||||
//'hyphenchar' : '|',
|
||||
'displaytogglebox' : false,
|
||||
'remoteloading' : false,
|
||||
'onerrorhandler' : function (e) {
|
||||
window.py_bridge.debug(e);
|
||||
}
|
||||
});
|
||||
Hyphenator.hyphenate(document.body, lang);
|
||||
}
|
||||
'''
|
@ -108,7 +108,7 @@ will appear in the next release of |app|.
|
||||
Can I use both |app| and the SONY software to manage my reader?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Yes, you can use both, provided you don not run them at the same time. That is, you should use the following sequence:
|
||||
Yes, you can use both, provided you do not run them at the same time. That is, you should use the following sequence:
|
||||
Connect reader->Use one of the programs->Disconnect reader. Reconnect reader->Use the other program->disconnect reader.
|
||||
|
||||
The underlying reason is that the Reader uses a single file to keep track
|
||||
@ -122,7 +122,7 @@ other via the computers hard disk.
|
||||
|
||||
If you do need to reset your metadata due to problems caused by using both
|
||||
at the same time, then just delete the media.xml file on the Reader using
|
||||
your PC's file explorer and it'll be recreated after disconnection.
|
||||
your PC's file explorer and it will be recreated after disconnection.
|
||||
|
||||
|
||||
Can I use the collections feature of the SONY reader?
|
||||
@ -149,6 +149,21 @@ How do I use |app| with my Android phone?
|
||||
|
||||
First install the WordPlayer e-book reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
|
||||
|
||||
Can I access my |app| books using the web browser in my Kindle or other reading device?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|app| has a *Content Server* that exports the books in |app| as a web page. You can turn it on under
|
||||
Preferences->Content Server. Then just point the web browser on your device to the computer running
|
||||
the Content Server and you will be able to browse your book collection. For example, if the computer running
|
||||
the server has IP address 63.45.128.5, in the browser, you would type::
|
||||
|
||||
http://63.45.128.5:8080
|
||||
|
||||
Some devices, like the Kindle, do not allow you to access port 8080 (the default port on which the content
|
||||
server runs. In that case, change the port in the |app| Preferences to 80. (On some operating systems,
|
||||
you may not be able to run the server on a port number less than 1024 because of security settings. In
|
||||
this case the simplest solution is to adjust your router to forward requests on port 80 to port 8080).
|
||||
|
||||
I get the error message "Failed to start content server: Port 8080 not free on '0.0.0.0'"?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user