mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Sync to trunk.
This commit is contained in:
commit
ad5d8fc4f2
@ -11,7 +11,7 @@
|
|||||||
- title: "Page turn animations in the e-book viewer"
|
- title: "Page turn animations in the e-book viewer"
|
||||||
type: major
|
type: major
|
||||||
description: >
|
description: >
|
||||||
"Now when you use the Page Down/Page Up keys or the next/previous page buttons in the viewer, page turning will be animated. The duration of the animation can be controlled in the viewer preferences. Setting it to o disables the animation completely."
|
"Now when you use the Page Down/Page Up keys or the next/previous page buttons in the viewer, page turning will be animated. The duration of the animation can be controlled in the viewer preferences. Setting it to 0 disables the animation completely."
|
||||||
|
|
||||||
- title: "Conversion pipeline: Add an option to set the minimum line height of all elemnts as a percentage of the computed font size. By default, calibre now sets the line height to 120% of the computed font size."
|
- title: "Conversion pipeline: Add an option to set the minimum line height of all elemnts as a percentage of the computed font size. By default, calibre now sets the line height to 120% of the computed font size."
|
||||||
|
|
||||||
|
@ -41,6 +41,20 @@ series_index_auto_increment = 'next'
|
|||||||
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
|
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
|
||||||
author_sort_copy_method = 'invert'
|
author_sort_copy_method = 'invert'
|
||||||
|
|
||||||
|
# Set which author field to display in the tags pane (the list of authors,
|
||||||
|
# series, publishers etc on the left hand side). The choices are author and
|
||||||
|
# author_sort. This tweak affects only the tags pane, and only what is displayed
|
||||||
|
# under the authors category. Please note that if you set this to author_sort,
|
||||||
|
# it is very possible to see duplicate names in the list becasue although it is
|
||||||
|
# guaranteed that author names are unique, there is no such guarantee for
|
||||||
|
# author_sort values. Showing duplicates won't break anything, but it could
|
||||||
|
# lead to some confusion. When using 'author_sort', the tooltip will show the
|
||||||
|
# author's name.
|
||||||
|
# Examples:
|
||||||
|
# tags_pane_use_field_for_author_name = 'author'
|
||||||
|
# tags_pane_use_field_for_author_name = 'author_sort'
|
||||||
|
tags_pane_use_field_for_author_name = 'author'
|
||||||
|
|
||||||
|
|
||||||
# Set whether boolean custom columns are two- or three-valued.
|
# Set whether boolean custom columns are two- or three-valued.
|
||||||
# Two-values for true booleans
|
# Two-values for true booleans
|
||||||
|
@ -1,78 +1,72 @@
|
|||||||
# -*- coding: utf-8 -*-
|
import re
|
||||||
|
|
||||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
class JournalofHospitalMedicine(BasicNewsRecipe):
|
class JournalofHospitalMedicine(BasicNewsRecipe):
|
||||||
|
|
||||||
title = 'Journal of Hospital Medicine'
|
title = 'Journal of Hospital Medicine'
|
||||||
__author__ = 'Krittika Goyal'
|
__author__ = 'Kovid Goyal'
|
||||||
description = 'Medical news'
|
description = 'Medical news'
|
||||||
timefmt = ' [%d %b, %Y]'
|
timefmt = ' [%d %b, %Y]'
|
||||||
needs_subscription = True
|
needs_subscription = True
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
keep_only_tags = [dict(id=['articleTitle', 'articleMeta', 'fulltext'])]
|
||||||
|
remove_tags = [dict(attrs={'class':'licensedContent'})]
|
||||||
|
|
||||||
|
|
||||||
# TO LOGIN
|
# TO LOGIN
|
||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser()
|
||||||
br.open('http://www3.interscience.wiley.com/cgi-bin/home')
|
br.open('http://www3.interscience.wiley.com/cgi-bin/home')
|
||||||
br.select_form(name='siteLogin')
|
br.select_form(nr=0)
|
||||||
br['LoginName'] = self.username
|
br['j_username'] = self.username
|
||||||
br['Password'] = self.password
|
br['j_password'] = self.password
|
||||||
response = br.submit()
|
response = br.submit()
|
||||||
raw = response.read()
|
raw = response.read()
|
||||||
if 'userName = ""' in raw:
|
if '<h2>LOGGED IN</h2>' not in raw:
|
||||||
raise Exception('Login failed. Check your username and password')
|
raise Exception('Login failed. Check your username and password')
|
||||||
return br
|
return br
|
||||||
|
|
||||||
#TO GET ARTICLE TOC
|
#TO GET ARTICLE TOC
|
||||||
def johm_get_index(self):
|
def johm_get_index(self):
|
||||||
return self.index_to_soup('http://www3.interscience.wiley.com/journal/111081937/home')
|
return self.index_to_soup('http://onlinelibrary.wiley.com/journal/10.1002/(ISSN)1553-5606/currentissue')
|
||||||
|
|
||||||
# To parse artice toc
|
# To parse artice toc
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
parse_soup = self.johm_get_index()
|
soup = self.johm_get_index()
|
||||||
|
toc = soup.find(id='issueTocGroups')
|
||||||
div = parse_soup.find(id='contentCell')
|
|
||||||
|
|
||||||
current_section = None
|
|
||||||
current_articles = []
|
|
||||||
feeds = []
|
feeds = []
|
||||||
for x in div.findAll(True):
|
for group in toc.findAll('li', id=re.compile(r'group\d+')):
|
||||||
if x.name == 'h4':
|
gtitle = group.find(attrs={'class':'subSectionHeading'})
|
||||||
# Section heading found
|
if gtitle is None:
|
||||||
if current_articles and current_section:
|
continue
|
||||||
feeds.append((current_section, current_articles))
|
gtitle = self.tag_to_string(gtitle)
|
||||||
current_section = self.tag_to_string(x)
|
arts = group.find(attrs={'class':'articles'})
|
||||||
current_articles = []
|
if arts is None:
|
||||||
self.log('\tFound section:', current_section)
|
continue
|
||||||
if current_section is not None and x.name == 'strong':
|
self.log('Found section:', gtitle)
|
||||||
title = self.tag_to_string(x)
|
articles = []
|
||||||
p = x.parent.parent.find('a', href=lambda x: x and '/HTMLSTART' in x)
|
for art in arts.findAll(attrs={'class':lambda x: x and 'tocArticle'
|
||||||
if p is None:
|
in x}):
|
||||||
continue
|
a = art.find('a', href=True)
|
||||||
url = p.get('href', False)
|
if a is None:
|
||||||
if not url or not title:
|
|
||||||
continue
|
continue
|
||||||
|
url = a.get('href')
|
||||||
if url.startswith('/'):
|
if url.startswith('/'):
|
||||||
url = 'http://www3.interscience.wiley.com'+url
|
url = 'http://onlinelibrary.wiley.com' + url
|
||||||
url = url.replace('/HTMLSTART', '/main.html,ftx_abs')
|
url = url.replace('/abstract', '/full')
|
||||||
self.log('\t\tFound article:', title)
|
title = self.tag_to_string(a)
|
||||||
self.log('\t\t\t', url)
|
a.extract()
|
||||||
#if url.startswith('/'):
|
pm = art.find(attrs={'class':'productMenu'})
|
||||||
#url = 'http://online.wsj.com'+url
|
if pm is not None:
|
||||||
current_articles.append({'title': title, 'url':url,
|
pm.extract()
|
||||||
'description':'', 'date':''})
|
desc = self.tag_to_string(art)
|
||||||
|
self.log('\tFound article:', title, 'at', url)
|
||||||
if current_articles and current_section:
|
articles.append({'title':title, 'url':url, 'description':desc,
|
||||||
feeds.append((current_section, current_articles))
|
'date':''})
|
||||||
|
if articles:
|
||||||
|
feeds.append((gtitle, articles))
|
||||||
|
|
||||||
return feeds
|
return feeds
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
for img in soup.findAll('img', src=True):
|
|
||||||
img['src'] = img['src'].replace('tfig', 'nfig')
|
|
||||||
return soup
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
|
|||||||
class NYTimes(BasicNewsRecipe):
|
class NYTimes(BasicNewsRecipe):
|
||||||
|
|
||||||
title = 'New England Journal of Medicine'
|
title = 'New England Journal of Medicine'
|
||||||
__author__ = 'Krittika Goyal'
|
__author__ = 'Kovid Goyal'
|
||||||
description = 'Medical news'
|
description = 'Medical news'
|
||||||
timefmt = ' [%d %b, %Y]'
|
timefmt = ' [%d %b, %Y]'
|
||||||
needs_subscription = True
|
needs_subscription = True
|
||||||
|
@ -36,6 +36,16 @@ Install BeautifulSoup 3.0.x manually into site-packages (3.1.x parses broken HTM
|
|||||||
|
|
||||||
Install pywin32 and edit win32com\__init__.py setting _frozen = True and
|
Install pywin32 and edit win32com\__init__.py setting _frozen = True and
|
||||||
__gen_path__ to a temp dir (otherwise it tries to set it to a dir in the install tree which leads to permission errors)
|
__gen_path__ to a temp dir (otherwise it tries to set it to a dir in the install tree which leads to permission errors)
|
||||||
|
Note that you should use::
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
__gen_path__ = os.path.join(
|
||||||
|
tempfile.gettempdir(), "gen_py",
|
||||||
|
"%d.%d" % (sys.version_info[0], sys.version_info[1]))
|
||||||
|
|
||||||
|
Use gettempdir instead of the win32 api method as gettempdir returns a temp dir that is guaranteed to actually work.
|
||||||
|
|
||||||
|
|
||||||
Also edit win32com\client\gencache.py and change the except IOError on line 57 to catch all exceptions.
|
Also edit win32com\client\gencache.py and change the except IOError on line 57 to catch all exceptions.
|
||||||
|
|
||||||
SQLite
|
SQLite
|
||||||
|
@ -28,7 +28,7 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
# Motorola
|
# Motorola
|
||||||
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
||||||
0x4285 : [0x216]},
|
0x4285 : [0x216], 0x42a3 : [0x216] },
|
||||||
|
|
||||||
# Sony Ericsson
|
# Sony Ericsson
|
||||||
0xfce : { 0xd12e : [0x0100]},
|
0xfce : { 0xd12e : [0x0100]},
|
||||||
|
@ -62,9 +62,9 @@ class SWEEX(USBMS):
|
|||||||
# Ordered list of supported formats
|
# Ordered list of supported formats
|
||||||
FORMATS = ['epub', 'prc', 'fb2', 'html', 'rtf', 'chm', 'pdf', 'txt']
|
FORMATS = ['epub', 'prc', 'fb2', 'html', 'rtf', 'chm', 'pdf', 'txt']
|
||||||
|
|
||||||
VENDOR_ID = [0x0525]
|
VENDOR_ID = [0x0525, 0x177f]
|
||||||
PRODUCT_ID = [0xa4a5]
|
PRODUCT_ID = [0xa4a5, 0x300]
|
||||||
BCD = [0x0319]
|
BCD = [0x0319, 0x110]
|
||||||
|
|
||||||
VENDOR_NAME = 'SWEEX'
|
VENDOR_NAME = 'SWEEX'
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOKREADER'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOKREADER'
|
||||||
|
@ -245,7 +245,7 @@ class RTFInput(InputFormatPlugin):
|
|||||||
from calibre.ebooks.metadata.meta import get_metadata
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
from calibre.ebooks.metadata.opf2 import OPFCreator
|
from calibre.ebooks.metadata.opf2 import OPFCreator
|
||||||
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
|
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
|
||||||
self.options = options
|
self.opts = options
|
||||||
self.log = log
|
self.log = log
|
||||||
self.log('Converting RTF to XML...')
|
self.log('Converting RTF to XML...')
|
||||||
#Name of the preprocesssed RTF file
|
#Name of the preprocesssed RTF file
|
||||||
@ -290,12 +290,12 @@ class RTFInput(InputFormatPlugin):
|
|||||||
res = transform.tostring(result)
|
res = transform.tostring(result)
|
||||||
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
|
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
|
||||||
# Replace newlines inserted by the 'empty_paragraphs' option in rtf2xml with html blank lines
|
# Replace newlines inserted by the 'empty_paragraphs' option in rtf2xml with html blank lines
|
||||||
if not getattr(self.options, 'remove_paragraph_spacing', False):
|
if not getattr(self.opts, 'remove_paragraph_spacing', False):
|
||||||
res = re.sub('\s*<body>', '<body>', res)
|
res = re.sub('\s*<body>', '<body>', res)
|
||||||
res = re.sub('(?<=\n)\n{2}',
|
res = re.sub('(?<=\n)\n{2}',
|
||||||
u'<p>\u00a0</p>\n'.encode('utf-8'), res)
|
u'<p>\u00a0</p>\n'.encode('utf-8'), res)
|
||||||
if self.options.preprocess_html:
|
if self.opts.preprocess_html:
|
||||||
preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None))
|
preprocessor = PreProcessor(self.opts, log=getattr(self, 'log', None))
|
||||||
res = preprocessor(res)
|
res = preprocessor(res)
|
||||||
f.write(res)
|
f.write(res)
|
||||||
self.write_inline_css(inline_class, border_styles)
|
self.write_inline_css(inline_class, border_styles)
|
||||||
|
@ -18,6 +18,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \
|
|||||||
from calibre.ebooks.metadata import title_sort
|
from calibre.ebooks.metadata import title_sort
|
||||||
from calibre.gui2 import config, NONE
|
from calibre.gui2 import config, NONE
|
||||||
from calibre.library.field_metadata import TagsIcons, category_icon_map
|
from calibre.library.field_metadata import TagsIcons, category_icon_map
|
||||||
|
from calibre.utils.config import tweaks
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.search_query_parser import saved_searches
|
from calibre.utils.search_query_parser import saved_searches
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
@ -409,17 +410,31 @@ class TagTreeItem(object): # {{{
|
|||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
def tag_data(self, role):
|
def tag_data(self, role):
|
||||||
|
tag = self.tag
|
||||||
|
if tag.category == 'authors' and \
|
||||||
|
tweaks['tags_pane_use_field_for_author_name'] == 'author_sort':
|
||||||
|
name = tag.sort
|
||||||
|
tt_author = True
|
||||||
|
else:
|
||||||
|
name = tag.name
|
||||||
|
tt_author = False
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
if self.tag.count == 0:
|
if tag.count == 0:
|
||||||
return QVariant('%s'%(self.tag.name))
|
return QVariant('%s'%(name))
|
||||||
else:
|
else:
|
||||||
return QVariant('[%d] %s'%(self.tag.count, self.tag.name))
|
return QVariant('[%d] %s'%(tag.count, name))
|
||||||
if role == Qt.EditRole:
|
if role == Qt.EditRole:
|
||||||
return QVariant(self.tag.name)
|
return QVariant(tag.name)
|
||||||
if role == Qt.DecorationRole:
|
if role == Qt.DecorationRole:
|
||||||
return self.icon_state_map[self.tag.state]
|
return self.icon_state_map[tag.state]
|
||||||
if role == Qt.ToolTipRole and self.tag.tooltip is not None:
|
if role == Qt.ToolTipRole:
|
||||||
return QVariant(self.tag.tooltip)
|
if tt_author:
|
||||||
|
if tag.tooltip is not None:
|
||||||
|
return QVariant('(%s) %s'%(tag.name, tag.tooltip))
|
||||||
|
else:
|
||||||
|
return QVariant(tag.name)
|
||||||
|
if tag.tooltip is not None:
|
||||||
|
return QVariant(tag.tooltip)
|
||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
def toggle(self):
|
def toggle(self):
|
||||||
|
@ -1128,6 +1128,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
for l in list:
|
for l in list:
|
||||||
(id, val, sort_val) = (l[0], l[1], l[2])
|
(id, val, sort_val) = (l[0], l[1], l[2])
|
||||||
tids[category][val] = (id, sort_val)
|
tids[category][val] = (id, sort_val)
|
||||||
|
elif cat['datatype'] == 'rating':
|
||||||
|
for l in list:
|
||||||
|
(id, val) = (l[0], l[1])
|
||||||
|
tids[category][val] = (id, '{0:05.2f}'.format(val))
|
||||||
else:
|
else:
|
||||||
for l in list:
|
for l in list:
|
||||||
(id, val) = (l[0], l[1])
|
(id, val) = (l[0], l[1])
|
||||||
@ -1256,12 +1260,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
|
|
||||||
# sort the list
|
# sort the list
|
||||||
if sort == 'name':
|
if sort == 'name':
|
||||||
def get_sort_key(x):
|
kf = lambda x :sort_key(x.s)
|
||||||
sk = x.s
|
|
||||||
if isinstance(sk, unicode):
|
|
||||||
sk = sort_key(sk)
|
|
||||||
return sk
|
|
||||||
kf = get_sort_key
|
|
||||||
reverse=False
|
reverse=False
|
||||||
elif sort == 'popularity':
|
elif sort == 'popularity':
|
||||||
kf = lambda x: x.c
|
kf = lambda x: x.c
|
||||||
|
@ -541,7 +541,9 @@ Use the options to remove headers and footers to mitigate this issue. If the hea
|
|||||||
removed from the text it can throw off the paragraph unwrapping.
|
removed from the text it can throw off the paragraph unwrapping.
|
||||||
|
|
||||||
Some limitations of PDF input is complex, multi-column, and image based documents are not supported.
|
Some limitations of PDF input is complex, multi-column, and image based documents are not supported.
|
||||||
Extraction of vector images and tables from within the document is also not supported.
|
Extraction of vector images and tables from within the document is also not supported. Some PDFs use special glyphs to
|
||||||
|
represent double ll or doubfle ff or fi,etc. Conversion of these may or may not work depending on jusy how they are
|
||||||
|
represented internally in the PDF.
|
||||||
|
|
||||||
Comic Book Collections
|
Comic Book Collections
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Loading…
x
Reference in New Issue
Block a user