Merge remote-tracking branch 'kovid/master'

This commit is contained in:
Tomasz Długosz 2014-05-07 23:07:41 +02:00
commit 6748b97ea0
29 changed files with 669 additions and 1104 deletions

View File

@ -400,6 +400,17 @@ If you use this tool multiple times, each invocation will cause the previously
created inline Table of Contents to be replaced. The tool can be accessed via
:guilabel:`Tools->Table of Contents->Insert inline Table of Contents`.
Filter style information
^^^^^^^^^^^^^^^^^^^^^^^^^^^
This tool can be used to easily remove specified CSS style properties from the
entire book. You can tell it what properties you want removed, for example,
``color, background-color, line-height`` and it will remove them from
everywhere they occur --- stylesheets, ``<style>`` tags and inline ``style``
attributes. After removing the style information, a summary of all the changes
made is displayed so you can see exactly what was changed. The tool can be
accessed via :guilabel:`Tools->Filter style information`.
.. _checkpoints:
Checkpoints

View File

@ -250,6 +250,11 @@ The following functions are available in addition to those described in single-f
* ``divide(x, y)`` -- returns x / y. Throws an exception if either x or y are not numbers.
* ``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. Because the `{` and `}` characters are special, you must use `[[` for the `{` character and `]]` for the '}' character; they are converted automatically. Note also that prefixes and suffixes (the `|prefix|suffix` syntax) cannot be used in the argument to this function when using template program mode.
* ``field(name)`` -- returns the metadata field named by ``name``.
* ``first_matching_cmp(val, cmp1, result1, cmp2, r2, ..., else_result)`` -- compares "val < cmpN" in sequence, returning resultN for the first comparison that succeeds. Returns else_result if no comparison succeeds. Example::
``first_matching_cmp(10,5,"small",10,"middle",15,"large","giant")``
returns "middle". The same example with a first value of 16 returns "giant".
* ``first_non_empty(value, value, ...)`` -- returns the first value that is not empty. If all values are empty, then the empty value is returned. You can have as many values as you want.
* ``format_date(x, date_format)`` -- format_date(val, format_string) -- format the value, which must be a date field, using the format_string, returning a string. The formatting codes are::

View File

@ -1,37 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
http://ameblo.jp/
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class SakuraBlog(BasicNewsRecipe):
title = u'chou chou blog'
__author__ = 'Hiroshi Miura'
oldest_article = 4
publication_type = 'blog'
max_articles_per_feed = 20
description = 'Japanese popular dog blog'
publisher = ''
category = 'dog, pet, japan'
language = 'ja'
encoding = 'utf-8'
use_embedded_content = True
feeds = [(u'blog', u'http://feedblog.ameba.jp/rss/ameblo/chouchou1218/rss20.xml')]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
for curfeed in feeds:
delList = []
for a,curarticle in enumerate(curfeed.articles):
if re.search(r'rssad.jp', curarticle.url):
delList.append(curarticle)
if len(delList)>0:
for d in delList:
index = curfeed.articles.index(d)
curfeed.articles[index:index+1] = []
return feeds

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__copyright__ = '2010,2014, Hiroshi Miura <miurahr@linux.com>'
'''
japan.engadget.com
'''
@ -20,19 +20,20 @@ class EndgadgetJapan(BasicNewsRecipe):
index = 'http://japanese.engadget.com/'
remove_javascript = True
remove_tags_before = dict(name="h1", attrs={'class':"post_title"})
remove_tags_after = dict(name='div', attrs={'class':'post_body'})
remove_tags_before = dict(name="header", attrs={'class':"header"})
remove_tags_after = dict(name='div', attrs={'class':'post-meta'})
def parse_index(self):
feeds = []
newsarticles = []
soup = self.index_to_soup(self.index)
for topstories in soup.findAll('div',attrs={'class':'post_content'}):
itt = topstories.find('h4')
for topstories in soup.findAll('header',attrs={'class':'post-header'}):
itt = topstories.find('h2')
itema = itt.find('a',href=True)
itemtime = topstories.find('span',attrs={'class':'time'})
newsarticles.append({
'title' :itema.string
,'date' :''
,'date' :itemtime.string
,'url' :itema['href']
,'description':''
})

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__copyright__ = '2010,2014, Hiroshi Miura <miurahr@linux.com>'
'''
www.kahoku.co.jp
'''
@ -21,11 +21,5 @@ class KahokuShinpoNews(BasicNewsRecipe):
feeds = [(u'news', u'http://www.kahoku.co.jp/rss/index_thk.xml')]
keep_only_tags = [ dict(id="page_title"),
dict(id="news_detail"),
dict(id="bt_title"),
{'class':"photoLeft"},
dict(id="bt_body")
]
remove_tags = [ {'class':"button"}]
keep_only_tags = [ {'class':"category"},{'class':"ttl"},{'class':'photoimg'},{'class':"txt"},{'class':"data"}]

View File

@ -1,6 +1,6 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__copyright__ = '2010,2014, Hiroshi Miura <miurahr@linux.com>'
'''
sankei.jp.msn.com
'''
@ -20,5 +20,4 @@ class MSNSankeiNewsProduct(BasicNewsRecipe):
feeds = [(u'\u65b0\u5546\u54c1', u'http://sankei.jp.msn.com/rss/news/release.xml')]
remove_tags_before = dict(id="NewsTitle")
remove_tags_after = dict(id="RelatedTitle")
keep_only_tags = [dict(id=['MainContent'])]

View File

@ -15,16 +15,10 @@ class NationalGeographicNews(BasicNewsRecipe):
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
auto_cleanup = True
feeds = [(u'news', u'http://feeds.nationalgeographic.com/ng/News/News_Main')]
remove_tags_before = dict(id='page_head')
remove_tags_after = [dict(id='social_buttons'),{'class':'aside'}]
remove_tags = [
{'class':'hidden'}
]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
for curfeed in feeds:

View File

@ -1,110 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.nikkei.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
import mechanize
from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_economy(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(\u7d4c\u6e08)'
__author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
masthead_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
needs_subscription = True
oldest_article = 2
max_articles_per_feed = 20
language = 'ja'
remove_javascript = False
temp_files = []
remove_tags_before = {'class':"cmn-section cmn-indent"}
remove_tags = [
{'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"},
{'class':"cmn-article_keyword cmn-clearfix"},
{'class':"cmn-print_headline cmn-clearfix"},
{'class':"cmn-article_list"},
dict(id="ABOUT-NIKKEI"),
{'class':"cmn-sub_market"},
]
remove_tags_after = {'class':"cmn-pr_list"}
feeds = [ (u'\u653f\u6cbb', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=seiji'),
(u'\u8ca1\u52d9', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=zaimu'),
(u'\u7d4c\u6e08', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=keizai'),
(u'\u30de\u30fc\u30b1\u30c3\u30c8', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=market'),
(u'\u96c7\u7528', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=koyou'),
(u'\u6559\u80b2', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=kyouiku'),
(u'\u304a\u304f\u3084\u307f', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=okuyami'),
(u'\u4eba\u4e8b', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=zinzi'),
]
def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
cj = mechanize.LWPCookieJar()
br.set_cookiejar(cj)
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if self.username is not None and self.password is not None:
#print "----------------------------get login form--------------------------------------------"
# open login form
br.open('https://id.nikkei.com/lounge/nl/base/LA0010.seam')
response = br.response()
#print "----------------------------get login form---------------------------------------------"
#print "----------------------------set login form---------------------------------------------"
# remove disabled input which brings error on mechanize
response.set_data(response.get_data().replace("<input id=\"j_id48\"", "<!-- "))
response.set_data(response.get_data().replace("gm_home_on.gif\" />", " -->"))
br.set_response(response)
br.select_form(name='LA0010Form01')
br['LA0010Form01:LA0010Email'] = self.username
br['LA0010Form01:LA0010Password'] = self.password
br.form.find_control(id='LA0010Form01:LA0010AutoLoginOn',type="checkbox").get(nr=0).selected = True
br.submit()
br.response()
#print "----------------------------send login form---------------------------------------------"
#print "----------------------------open news main page-----------------------------------------"
# open news site
br.open('http://www.nikkei.com/')
br.response()
#print "----------------------------www.nikkei.com BODY --------------------------------------"
#print response2.get_data()
#print "-------------------------^^-got auto redirect form----^^--------------------------------"
# forced redirect in default
br.select_form(nr=0)
br.submit()
response3 = br.response()
# return some cookie which should be set by Javascript
#print response3.geturl()
raw = response3.get_data()
#print "---------------------------response to form --------------------------------------------"
# grab cookie from JS and set it
redirectflag = re.search(r"var checkValue = '(\d+)';", raw, re.M).group(1)
br.select_form(nr=0)
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write("#LWP-Cookies-2.0\n")
self.temp_files[-1].write("Set-Cookie3: Cookie-dummy=Cookie-value; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].write("Set-Cookie3: redirectFlag="+redirectflag+"; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].close()
cj.load(self.temp_files[-1].name)
br.submit()
#br.set_debug_http(False)
#br.set_debug_redirects(False)
#br.set_debug_responses(False)
return br

View File

@ -1,107 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.nikkei.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
import mechanize
from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_industory(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(\u7523\u696d)'
__author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
masthead_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
needs_subscription = True
oldest_article = 2
max_articles_per_feed = 20
language = 'ja'
remove_javascript = False
temp_files = []
remove_tags_before = {'class':"cmn-section cmn-indent"}
remove_tags = [
{'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"},
{'class':"cmn-article_keyword cmn-clearfix"},
{'class':"cmn-print_headline cmn-clearfix"},
]
remove_tags_after = {'class':"cmn-pr_list"}
feeds = [ (u'\u65e5\u7d4c\u4f01\u696d', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=sangyo'),
(u'\u65e5\u7d4c\u88fd\u54c1', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=newpro'),
(u'internet', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=internet'),
(u'\u56fd\u969b', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=kaigai'),
(u'\u79d1\u5b66', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=kagaku'),
]
def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
cj = mechanize.LWPCookieJar()
br.set_cookiejar(cj)
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if self.username is not None and self.password is not None:
#print "----------------------------get login form--------------------------------------------"
# open login form
br.open('https://id.nikkei.com/lounge/nl/base/LA0010.seam')
response = br.response()
#print "----------------------------get login form---------------------------------------------"
#print "----------------------------set login form---------------------------------------------"
# remove disabled input which brings error on mechanize
response.set_data(response.get_data().replace("<input id=\"j_id48\"", "<!-- "))
response.set_data(response.get_data().replace("gm_home_on.gif\" />", " -->"))
br.set_response(response)
br.select_form(name='LA0010Form01')
br['LA0010Form01:LA0010Email'] = self.username
br['LA0010Form01:LA0010Password'] = self.password
br.form.find_control(id='LA0010Form01:LA0010AutoLoginOn',type="checkbox").get(nr=0).selected = True
br.submit()
br.response()
#print "----------------------------send login form---------------------------------------------"
#print "----------------------------open news main page-----------------------------------------"
# open news site
br.open('http://www.nikkei.com/')
br.response()
#print "----------------------------www.nikkei.com BODY --------------------------------------"
#print response2.get_data()
#print "-------------------------^^-got auto redirect form----^^--------------------------------"
# forced redirect in default
br.select_form(nr=0)
br.submit()
response3 = br.response()
# return some cookie which should be set by Javascript
#print response3.geturl()
raw = response3.get_data()
#print "---------------------------response to form --------------------------------------------"
# grab cookie from JS and set it
redirectflag = re.search(r"var checkValue = '(\d+)';", raw, re.M).group(1)
br.select_form(nr=0)
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write("#LWP-Cookies-2.0\n")
self.temp_files[-1].write("Set-Cookie3: Cookie-dummy=Cookie-value; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].write("Set-Cookie3: redirectFlag="+redirectflag+"; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].close()
cj.load(self.temp_files[-1].name)
br.submit()
#br.set_debug_http(False)
#br.set_debug_redirects(False)
#br.set_debug_responses(False)
return br

View File

@ -1,104 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.nikkei.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
import mechanize
from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_life(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(\u751f\u6d3b)'
__author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
masthead_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
needs_subscription = True
oldest_article = 2
max_articles_per_feed = 20
language = 'ja'
remove_javascript = False
temp_files = []
remove_tags_before = {'class':"cmn-section cmn-indent"}
remove_tags = [
{'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"},
{'class':"cmn-article_keyword cmn-clearfix"},
{'class':"cmn-print_headline cmn-clearfix"},
]
remove_tags_after = {'class':"cmn-pr_list"}
feeds = [ (u'\u304f\u3089\u3057', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=kurashi'),
(u'\u30a8\u30b3', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=eco'),
(u'\u5065\u5eb7', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=kenkou'),
(u'\u7279\u96c6', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=special')
]
def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
cj = mechanize.LWPCookieJar()
br.set_cookiejar(cj)
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if self.username is not None and self.password is not None:
#print "----------------------------get login form--------------------------------------------"
# open login form
br.open('https://id.nikkei.com/lounge/nl/base/LA0010.seam')
response = br.response()
#print "----------------------------get login form---------------------------------------------"
#print "----------------------------set login form---------------------------------------------"
# remove disabled input which brings error on mechanize
response.set_data(response.get_data().replace("<input id=\"j_id48\"", "<!-- "))
response.set_data(response.get_data().replace("gm_home_on.gif\" />", " -->"))
br.set_response(response)
br.select_form(name='LA0010Form01')
br['LA0010Form01:LA0010Email'] = self.username
br['LA0010Form01:LA0010Password'] = self.password
br.form.find_control(id='LA0010Form01:LA0010AutoLoginOn',type="checkbox").get(nr=0).selected = True
br.submit()
br.response()
#print "----------------------------send login form---------------------------------------------"
#print "----------------------------open news main page-----------------------------------------"
# open news site
br.open('http://www.nikkei.com/')
br.response()
#print "----------------------------www.nikkei.com BODY --------------------------------------"
#print response2.get_data()
#print "-------------------------^^-got auto redirect form----^^--------------------------------"
# forced redirect in default
br.select_form(nr=0)
br.submit()
response3 = br.response()
# return some cookie which should be set by Javascript
#print response3.geturl()
raw = response3.get_data()
#print "---------------------------response to form --------------------------------------------"
# grab cookie from JS and set it
redirectflag = re.search(r"var checkValue = '(\d+)';", raw, re.M).group(1)
br.select_form(nr=0)
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write("#LWP-Cookies-2.0\n")
self.temp_files[-1].write("Set-Cookie3: Cookie-dummy=Cookie-value; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].write("Set-Cookie3: redirectFlag="+redirectflag+"; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].close()
cj.load(self.temp_files[-1].name)
br.submit()
#br.set_debug_http(False)
#br.set_debug_redirects(False)
#br.set_debug_responses(False)
return br

View File

@ -1,103 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.nikkei.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
import mechanize
from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_main(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(\u7dcf\u5408)'
__author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
masthead_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
needs_subscription = True
oldest_article = 2
max_articles_per_feed = 20
language = 'ja'
remove_javascript = False
temp_files = []
remove_tags_before = {'class':"cmn-section cmn-indent"}
remove_tags = [
{'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"},
{'class':"cmn-article_keyword cmn-clearfix"},
{'class':"cmn-print_headline cmn-clearfix"},
{'class':"cmn-article_list"},
{'class':"cmn-dashedline"},
{'class':"cmn-hide"},
]
remove_tags_after = {'class':"cmn-pr_list"}
feeds = [ (u'NIKKEI', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=main')]
def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
cj = mechanize.LWPCookieJar()
br.set_cookiejar(cj)
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if self.username is not None and self.password is not None:
#print "----------------------------get login form--------------------------------------------"
# open login form
br.open('https://id.nikkei.com/lounge/nl/base/LA0010.seam')
response = br.response()
#print "----------------------------get login form---------------------------------------------"
#print "----------------------------set login form---------------------------------------------"
# remove disabled input which brings error on mechanize
response.set_data(response.get_data().replace("<input id=\"j_id48\"", "<!-- "))
response.set_data(response.get_data().replace("gm_home_on.gif\" />", " -->"))
br.set_response(response)
br.select_form(name='LA0010Form01')
br['LA0010Form01:LA0010Email'] = self.username
br['LA0010Form01:LA0010Password'] = self.password
br.form.find_control(id='LA0010Form01:LA0010AutoLoginOn',type="checkbox").get(nr=0).selected = True
br.submit()
br.response()
#print "----------------------------send login form---------------------------------------------"
#print "----------------------------open news main page-----------------------------------------"
# open news site
br.open('http://www.nikkei.com/')
br.response()
#print "----------------------------www.nikkei.com BODY --------------------------------------"
#print response2.get_data()
#print "-------------------------^^-got auto redirect form----^^--------------------------------"
# forced redirect in default
br.select_form(nr=0)
br.submit()
response3 = br.response()
# return some cookie which should be set by Javascript
#print response3.geturl()
raw = response3.get_data()
#print "---------------------------response to form --------------------------------------------"
# grab cookie from JS and set it
redirectflag = re.search(r"var checkValue = '(\d+)';", raw, re.M).group(1)
br.select_form(nr=0)
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write("#LWP-Cookies-2.0\n")
self.temp_files[-1].write("Set-Cookie3: Cookie-dummy=Cookie-value; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].write("Set-Cookie3: redirectFlag="+redirectflag+"; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].close()
cj.load(self.temp_files[-1].name)
br.submit()
#br.set_debug_http(False)
#br.set_debug_redirects(False)
#br.set_debug_responses(False)
return br

View File

@ -1,102 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.nikkei.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
import mechanize
from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_shakai(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(Social)'
__author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
masthead_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
needs_subscription = True
oldest_article = 2
max_articles_per_feed = 20
language = 'ja'
remove_javascript = False
temp_files = []
remove_tags_before = {'class':"cmn-section cmn-indent"}
remove_tags = [
{'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"},
{'class':"cmn-article_keyword cmn-clearfix"},
{'class':"cmn-print_headline cmn-clearfix"},
]
remove_tags_after = {'class':"cmn-pr_list"}
feeds = [
(u'\u793e\u4f1a', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=shakai')
]
def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
cj = mechanize.LWPCookieJar()
br.set_cookiejar(cj)
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if self.username is not None and self.password is not None:
#print "----------------------------get login form--------------------------------------------"
# open login form
br.open('https://id.nikkei.com/lounge/nl/base/LA0010.seam')
response = br.response()
#print "----------------------------get login form---------------------------------------------"
#print "----------------------------set login form---------------------------------------------"
# remove disabled input which brings error on mechanize
response.set_data(response.get_data().replace("<input id=\"j_id48\"", "<!-- "))
response.set_data(response.get_data().replace("gm_home_on.gif\" />", " -->"))
br.set_response(response)
br.select_form(name='LA0010Form01')
br['LA0010Form01:LA0010Email'] = self.username
br['LA0010Form01:LA0010Password'] = self.password
br.form.find_control(id='LA0010Form01:LA0010AutoLoginOn',type="checkbox").get(nr=0).selected = True
br.submit()
br.response()
#print "----------------------------send login form---------------------------------------------"
#print "----------------------------open news main page-----------------------------------------"
# open news site
br.open('http://www.nikkei.com/')
br.response()
#print "----------------------------www.nikkei.com BODY --------------------------------------"
#print response2.get_data()
#print "-------------------------^^-got auto redirect form----^^--------------------------------"
# forced redirect in default
br.select_form(nr=0)
br.submit()
response3 = br.response()
# return some cookie which should be set by Javascript
#print response3.geturl()
raw = response3.get_data()
#print "---------------------------response to form --------------------------------------------"
# grab cookie from JS and set it
redirectflag = re.search(r"var checkValue = '(\d+)';", raw, re.M).group(1)
br.select_form(nr=0)
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write("#LWP-Cookies-2.0\n")
self.temp_files[-1].write("Set-Cookie3: Cookie-dummy=Cookie-value; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].write("Set-Cookie3: redirectFlag="+redirectflag+"; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].close()
cj.load(self.temp_files[-1].name)
br.submit()
#br.set_debug_http(False)
#br.set_debug_redirects(False)
#br.set_debug_responses(False)
return br

View File

@ -1,108 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.nikkei.com
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
import mechanize
from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_sports(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(\u30b9\u30dd\u30fc\u30c4)'
__author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
masthead_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'
needs_subscription = True
oldest_article = 2
max_articles_per_feed = 20
language = 'ja'
remove_javascript = False
temp_files = []
remove_tags_before = {'class':"cmn-section cmn-indent"}
remove_tags = [
{'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"},
{'class':"cmn-article_keyword cmn-clearfix"},
{'class':"cmn-print_headline cmn-clearfix"},
]
remove_tags_after = {'class':"cmn-pr_list"}
feeds = [
(u'\u30b9\u30dd\u30fc\u30c4\uff1a\u30d7\u30ed\u91ce\u7403', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=baseball'),
(u'\u30b9\u30dd\u30fc\u30c4\uff1a\u5927\u30ea\u30fc\u30b0', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=mlb'),
(u'\u30b9\u30dd\u30fc\u30c4\uff1a\u30b5\u30c3\u30ab\u30fc', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=soccer'),
(u'\u30b9\u30dd\u30fc\u30c4\uff1a\u30b4\u30eb\u30d5', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=golf'),
(u'\u30b9\u30dd\u30fc\u30c4\uff1a\u76f8\u64b2', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=sumou'),
(u'\u30b9\u30dd\u30fc\u30c4\uff1a\u7af6\u99ac', u'http://www.zou3.net/php/rss/nikkei2rss.php?head=keiba')
]
def get_browser(self):
br = BasicNewsRecipe.get_browser(self)
cj = mechanize.LWPCookieJar()
br.set_cookiejar(cj)
#br.set_debug_http(True)
#br.set_debug_redirects(True)
#br.set_debug_responses(True)
if self.username is not None and self.password is not None:
#print "----------------------------get login form--------------------------------------------"
# open login form
br.open('https://id.nikkei.com/lounge/nl/base/LA0010.seam')
response = br.response()
#print "----------------------------get login form---------------------------------------------"
#print "----------------------------set login form---------------------------------------------"
# remove disabled input which brings error on mechanize
response.set_data(response.get_data().replace("<input id=\"j_id48\"", "<!-- "))
response.set_data(response.get_data().replace("gm_home_on.gif\" />", " -->"))
br.set_response(response)
br.select_form(name='LA0010Form01')
br['LA0010Form01:LA0010Email'] = self.username
br['LA0010Form01:LA0010Password'] = self.password
br.form.find_control(id='LA0010Form01:LA0010AutoLoginOn',type="checkbox").get(nr=0).selected = True
br.submit()
br.response()
#print "----------------------------send login form---------------------------------------------"
#print "----------------------------open news main page-----------------------------------------"
# open news site
br.open('http://www.nikkei.com/')
br.response()
#print "----------------------------www.nikkei.com BODY --------------------------------------"
#print response2.get_data()
#print "-------------------------^^-got auto redirect form----^^--------------------------------"
# forced redirect in default
br.select_form(nr=0)
br.submit()
response3 = br.response()
# return some cookie which should be set by Javascript
#print response3.geturl()
raw = response3.get_data()
#print "---------------------------response to form --------------------------------------------"
# grab cookie from JS and set it
redirectflag = re.search(r"var checkValue = '(\d+)';", raw, re.M).group(1)
br.select_form(nr=0)
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write("#LWP-Cookies-2.0\n")
self.temp_files[-1].write("Set-Cookie3: Cookie-dummy=Cookie-value; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].write("Set-Cookie3: redirectFlag="+redirectflag+"; domain=\".nikkei.com\"; path=\"/\"; path_spec; secure; expires=\"2029-12-21 05:07:59Z\"; version=0\n")
self.temp_files[-1].close()
cj.load(self.temp_files[-1].name)
br.submit()
#br.set_debug_http(False)
#br.set_debug_redirects(False)
#br.set_debug_responses(False)
return br

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__copyright__ = '2010,2014, Hiroshi Miura <miurahr@linux.com>'
'''
http://ameblo.jp/sauta19/
'''
@ -18,7 +18,7 @@ class UniNoHimituKichiBlog(BasicNewsRecipe):
category = 'cat, pet, japan'
language = 'ja'
encoding = 'utf-8'
keep_only_tags = [{'class':'entry_head'},{'class':'subContentsInner'}]
feeds = [(u'blog', u'http://feedblog.ameba.jp/rss/ameblo/sauta19/rss20.xml')]
def parse_feeds(self):

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__copyright__ = '2010,2014, Hiroshi Miura <miurahr@linux.com>'
'''
www.yomiuri.co.jp
'''
@ -16,16 +16,13 @@ class YOLNews(BasicNewsRecipe):
publisher = 'Yomiuri Online News'
category = 'news, japan'
language = 'ja'
encoding = 'Shift_JIS'
encoding = 'UTF-8'
index = 'http://www.yomiuri.co.jp/latestnews/'
remove_javascript = True
masthead_title = u'YOMIURI ONLINE'
keep_only_tags = [{'class':"article-def"}]
remove_tags = [{'class':"RelatedArticle"},
{'class':"sbtns"}
]
remove_tags_after = {'class':"date-def"}
keep_only_tags = [{'class':"article text-resizeable"}]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
@ -42,22 +39,22 @@ class YOLNews(BasicNewsRecipe):
def parse_index(self):
feeds = []
newsarticles = []
soup = self.index_to_soup(self.index)
topstories = soup.find('ul',attrs={'class':'list-def'})
if topstories:
newsarticles = []
for itt in topstories.findAll('li'):
itema = itt.find('a',href=True)
if itema:
itd1 = itema.findNextSibling(text = True)
itd2 = itd1.findNextSibling(text = True)
itd3 = itd2.findNextSibling(text = True)
newsarticles.append({
'title' :itema.string
,'date' :''.join([itd1, itd2, itd3])
,'url' :'http://www.yomiuri.co.jp' + itema['href']
,'description':''
})
feeds.append(('latest', newsarticles))
listlatest = soup.find('ul', attrs={'class':'list-common list-common-latest'})
if listlatest:
for itt in listlatest.findAll('li'):
itema = itt.find('a',href=True)
if itema:
item_headline = itema.find('span',attrs={'class':'headline'})
item_date = item_headline.find('span',attrs={'class':'update'})
newsarticles.append({
'title' :item_headline.contents[0]
,'date' :item_date
,'url' :itema['href']
,'description':''
})
feeds.append(('latest', newsarticles))
return feeds

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
__copyright__ = '2010,2014, Hiroshi Miura <miurahr@linux.com>'
'''
www.yomiuri.co.jp
'''
@ -16,16 +16,12 @@ class YOLNews(BasicNewsRecipe):
publisher = 'Yomiuri Online News'
category = 'news, japan'
language = 'ja'
encoding = 'Shift_JIS'
encoding = 'UTF-8'
index = 'http://www.yomiuri.co.jp/world/'
remove_javascript = True
masthead_title = u"YOMIURI ONLINE"
keep_only_tags = [{'class':"article-def"}]
remove_tags = [{'class':"RelatedArticle"},
{'class':"sbtns"}
]
remove_tags_after = {'class':"date-def"}
keep_only_tags = [{'class':"article text-resizeable"}]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
@ -42,20 +38,36 @@ class YOLNews(BasicNewsRecipe):
def parse_index(self):
feeds = []
newsarticles = []
soup = self.index_to_soup(self.index)
topstories = soup.find('ul',attrs={'class':'list-def'})
if topstories:
newsarticles = []
for itt in topstories.findAll('li'):
itema = itt.find('a',href=True)
if itema:
itd1 = itema.findNextSibling(text = True)
newsarticles.append({
'title' :itema.string
,'date' :''.join([itd1])
,'url' :'http://www.yomiuri.co.jp' + itema['href']
,'description':''
})
feeds.append(('World', newsarticles))
mainspan = soup.find('div', attrs={'class':'pbNested span-main-inr'})
if mainspan:
topstories = mainspan.find('ul',attrs={'class':'list-top'})
if topstories:
for itt in topstories.findAll('li'):
itema = itt.find('a',href=True)
if itema:
item_headline = itema.find('span',attrs={'class':'headline'})
item_date = item_headline.find('span',attrs={'class':'update'})
newsarticles.append({
'title' :item_headline.contents[0]
,'date' :item_date
,'url' :itema['href']
,'description':''
})
secondstories = mainspan.find('ul', attrs={'class':'list-common'})
if secondstories:
for itt in secondstories.findAll('li'):
itema = itt.find('a',href=True)
if itema:
item_headline = itema.find('span',attrs={'class':'headline'})
item_date = item_headline.find('span',attrs={'class':'update'})
newsarticles.append({
'title' :item_headline.contents[0]
,'date' :item_date
,'url' :itema['href']
,'description':''
})
feeds.append(('World', newsarticles))
return feeds

View File

@ -1382,3 +1382,4 @@ application/vnd.ms-opentype otf
application/font-woff woff
application/x-font-truetype ttf
text/xml plist
application/x-ibooks+zip ibook

View File

@ -41,10 +41,10 @@ Cygwin
This is needed for automation of the build process, and the ease of use of the
unix shell (bash).
Install, vim, rsync, openssh, unzip, wget, make at a minimum.
Install vim, dos2unix, rsync, openssh, unzip, wget, make, zsh, bash-completion, curl at a minimum.
After installing python run::
python setup/vcvars.py && echo 'source ~/.vcvars' >> ~/.bash_profile
python setup/vcvars.py && echo 'source ~/.vcvars' >> ~/.zshrc
To allow you to use the visual studio tools in the cygwin shell.
@ -147,39 +147,334 @@ Put sqlite3*.h from the sqlite windows amalgamation in ~/sw/include
APSW
-----
Download source from http://code.google.com/p/apsw/downloads/list and run in visual studio prompt
Download source from http://code.google.com/p/apsw/downloads/list and run
python setup.py fetch --all --missing-checksum-ok build --enable-all-extensions install test
Build requirements
-------------------
Install perl and ruby (needed to build openssl and qt):
Perl: http://www.activestate.com/activeperl
Ruby: http://rubyinstaller.org/
Put both perl.exe and ruby.exe in the PATH
Get nasm.exe from (needed for openssl and libjpeg-turbo)
http://www.nasm.us/pub/nasm/releasebuilds/2.11/win32/nasm-2.11-win32.zip
and put it in ~/sw/bin (which must be in PATH)
OpenSSL
--------
First install ActiveState Perl if you dont already have perl in windows
Then, get nasm.exe from
http://www.nasm.us/pub/nasm/releasebuilds/2.05/nasm-2.05-win32.zip and put it
somewhere on your PATH (I chose ~/sw/bin)
Download and untar the openssl tarball, follow the instructions in INSTALL.(W32|W64)
to install use prefix q:\openssl
Download and untar the openssl tarball.
To install use a private prefix: --prefix=C:/cygwin64/home/kovid/sw/private/openssl
The following *MUST BE RUN* in a Visual Studio Command prompt and not in a cygwin
environment.
For 32-bit::
perl Configure VC-WIN32 no-asm enable-static-engine --prefix=Q:/openssl
perl Configure VC-WIN32 no-asm enable-static-engine --prefix=C:/cygwin64/home/kovid/sw/private/openssl
ms\do_ms.bat
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak test
nmake -f ms\ntdll.mak install
For 64-bit::
perl Configure VC-WIN64A no-asm enable-static-engine --prefix=C:/cygwin/home/kovid/sw/private/openssl
perl Configure VC-WIN64A no-asm enable-static-engine --prefix=C:/cygwin64/home/kovid/sw/private/openssl
ms\do_win64a.bat
nmake -f ms\ntdll.mak
nmake -f ms\ntdll.mak test
nmake -f ms\ntdll.mak install
ICU
-------
Download the win32 *source* .zip from http://www.icu-project.org/download
Extract to C:\cygwin64\home\kovid\sw\private\icu
The following must be run in the VS Command Prompt, not the cygwin ssh shell
cd to <ICU>\source::
set PATH=%PATH%;C:\cygwin64\bin
dos2unix runConfigureICU
bash ./runConfigureICU Cygwin/MSVC
make
zlib
------
http://www.zlib.net/
Build with::
nmake -f win32/Makefile.msc
nmake -f win32/Makefile.msc test
cp zlib1.dll* ~/sw/bin && cp zlib.lib zdll.* ~/sw/lib/ && cp zconf.h zlib.h ~/sw/include/
jpeg-8
-------
Get the source code from: http://sourceforge.net/projects/libjpeg-turbo/files/
Run::
chmod +x cmakescripts/* && mkdir -p build && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DWITH_JPEG8=1 ..
nmake
cp sharedlib/jpeg8.dll* ~/sw/bin/ && cp sharedlib/jpeg.lib ~/sw/lib/ && cp jconfig.h ../jerror.h ../jpeglib.h ../jmorecfg.h ~/sw/include
libpng
---------
Download the libpng .zip source file from:
http://www.libpng.org/pub/png/libpng.html
Run::
mkdir -p build && cd build
cmake -G "NMake Makefiles" -DPNG_SHARED=1 -DCMAKE_BUILD_TYPE=Release -DZLIB_INCLUDE_DIR=C:/cygwin64/home/kovid/sw/include -DZLIB_LIBRARY=C:/cygwin64/home/kovid/sw/lib/zdll.lib ..
nmake
cp libpng*.dll ~/sw/bin/ && cp libpng*.lib ~/sw/lib/ && cp pnglibconf.h ../png.h ../pngconf.h ~/sw/include/
freetype
-----------
Get the .zip source from: http://download.savannah.gnu.org/releases/freetype/
Edit *all copies* of the file ftoption.h and add to generate a .lib
and a correct dll
#define FT_EXPORT(return_type) __declspec(dllexport) return_type
#define FT_EXPORT_DEF(return_type) __declspec(dllexport) return_type
VS 2008 .sln file is present, open it
* If you are doing x64 build, click the Win32 dropdown, select
Configuration manager->Active solution platform -> New -> x64
* Change active build type to release multithreaded
* Project->Properties->Configuration Properties change configuration type
to dll and build solution
cp "`find . -name freetype.dll`" ~/sw/bin/ && cp "`find . -name freetype.lib`" ~/sw/lib/
Now change configuration back to static for .lib and build solution
cp "`find . -name 'freetype*MT.lib'`" ~/sw/lib/
cp -rf include ~/sw/include/freetype2 && rm -rf ~/sw/include/freetype2/internal
TODO: Test if this bloody thing actually works on 64 bit (apparently freetype
assumes sizeof(long) == sizeof(ptr) which is not true in Win64. See for
example: http://forum.openscenegraph.org/viewtopic.php?t=2880
expat
--------
Get from: http://sourceforge.net/projects/expat/files/expat/
Apparently expat requires stdint.h which VS 2008 does not have. So we get our
own.
Run::
cd lib && wget http://msinttypes.googlecode.com/svn/trunk/stdint.h && cd ..
mkdir -p build && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ..
nmake
cp expat.dll ~/sw/bin/ && cp expat.lib ~/sw/lib/
cp ../lib/expat.h ../lib/expat_external.h ~/sw/include
libiconv
----------
Run::
mkdir vs2008 && cd vs2008
Then follow these instructions:
http://www.codeproject.com/Articles/302012/How-to-Build-libiconv-with-Microsoft-Visual-Studio
NOTE: Built as MT rather than MD so no manifest
Change the type to Release and config to x64 or Win32 and Build solution and
then::
cp "`find . -name '*.dll'`" ~/sw/bin/
cp "`find . -name '*.lib'`" ~/sw/lib/iconv.lib
cp "`find . -name iconv.h`" ~/sw/include/
Information for using a static version of libiconv is at the link above.
libxml2
-------------
Get it from: ftp://xmlsoft.org/libxml2/
Run::
cd win32
cscript.exe configure.js include=C:/cygwin64/home/kovid/sw/include lib=C:/cygwin64/home/kovid/sw/lib prefix=C:/cygwin64/home/kovid/sw zlib=yes iconv=yes
nmake /f Makefile.msvc
cd ..
mkdir -p ~/sw/include/libxml2/libxml && cp include/libxml/*.h ~/sw/include/libxml2/libxml/
find . -type f \( -name "*.dll" -o -name "*.dll.manifest" \) -exec cp "{}" ~/sw/bin/ \;
find . -name libxml2.lib -exec cp "{}" ~/sw/lib/ \;
libxslt
---------
Get it from: ftp://xmlsoft.org/libxml2/
Run::
cd win32
cscript.exe configure.js include=C:/cygwin64/home/kovid/sw/include include=C:/cygwin64/home/kovid/sw/include/libxml2 lib=C:/cygwin64/home/kovid/sw/lib prefix=C:/cygwin64/home/kovid/sw zlib=yes iconv=yes
nmake /f Makefile.msvc
mkdir -p ~/sw/include/libxslt ~/sw/include/libexslt
cd ..
cp libxslt/*.h ~/sw/include/libxslt/
cp libexslt/*.h ~/sw/include/libexslt/
find . -type f \( -name "*.dll" -o -name "*.dll.manifest" \) -exec cp "{}" ~/sw/bin/ \;
find . -name 'lib*xslt.lib' -exec cp "{}" ~/sw/lib/ \;
lxml
------
Get the source from: http://pypi.python.org/pypi/lxml
Change the include dirs and lib dirs by editing setupinfo.py and changing the
library_dirs() function to return::
return ['C:/cygwin64/home/kovid/sw/lib']
and the include_dirs() function to return
return ['C:/cygwin64/home/kovid/sw/include/libxml2', 'C:/cygwin64/home/kovid/sw/include']
Run::
python setup.py install
Python Imaging Library
------------------------
Download from http://pypi.python.org/pypi/Pillow/
Edit setup.py setting the ROOT values, like this::
SW = r'C:\cygwin64\home\kovid\sw'
JPEG_ROOT = ZLIB_ROOT = FREETYPE_ROOT = (SW+r'\lib', SW+r'\include')
Set zip_safe=False
Build and install with::
python setup.py install
poppler
-------------
mkdir build
Run the cmake GUI which will find the various dependencies automatically.
On 64 bit cmake might not let you choose Visual Studio 2008, in whcih case
leave the source field blank, click configure choose Visual Studio 2008 and
then enter the source field.
In cmake: disable GTK, Qt, openjpeg, cpp, lcms, gtk_tests, qt_tests. Enable
jpeg, png and zlib::
cp build/utils/Release/*.exe ~/sw/bin
podofo
----------
Download from http://podofo.sourceforge.net/download.html
mkdir build
Add the following three lines near the top of CMakeLists.txt
SET(WANT_LIB64 FALSE)
SET(PODOFO_BUILD_SHARED TRUE)
SET(PODOFO_BUILD_STATIC FALSE)
PoDoFo's CMakeLists.txt is pretty bad. Run the cmake-gui and fill in values for
freetype2 and open ssl (choose any one .lib for the libcrypto variable, you
will have to fix it manually in Visual Studio later anyway). Then generate the
VisualStudio solution. In the solution. In the Solution got to
Project->Properties->Linker->Input and add the second ssl library. And in
C++->General add the openssl include dir.
Now build only the project podofo_shared (release mode)
Run::
cp "`find . -name '*.dll'`" ~/sw/bin/
cp "`find . -name '*.lib'`" ~/sw/lib/
mkdir ~/sw/include/podofo
cp build/podofo_config.h ~/sw/include/podofo
cp -r src/* ~/sw/include/podofo/
ImageMagick
--------------
Get the source from: http://www.imagemagick.org/download/windows/ImageMagick-windows.zip
Unzip it and then run::
chmod +x `find . -name '*.exe'`
Edit VisualMagick/configure/configure.cpp to set
int projectType = MULTITHREADEDDLL;
Open configure.sln and build it to create configure.exe
Run configure.exe set 32/64 bit disable X11 and OpenMPI and click the Edit
magick-baseconfig.h button
Undefine ProvideDllMain
Now open VisualMagick/VisualDynamicMT.sln set to Release
Remove the UTIL_IMdisplay and CORE_Magick++ projects.
F7 for build solution.
netifaces
------------
Download the source tarball from http://alastairs-place.net/projects/netifaces/
Rename netifaces.c to netifaces.cpp and make the same change in setup.py
Run::
python setup.py build
cp `find build/ -name *.pyd` /cygdrive/c/Python27/Lib/site-packages/
psutil
--------
Download the source tarball
Run
Python setup.py build
cp -r build/lib.win32-*/* /cygdrive/c/Python27/Lib/site-packages/
easylzma
----------
This is only needed to build the portable installer.
Get it from http://lloyd.github.com/easylzma/ (use the trunk version)
Run cmake and build the Visual Studio solution (generates CLI tools and dll and
static lib automatically)
chmlib
-------
Download the zip source code from: http://www.jedrea.com/chmlib/
Run::
cd src && unzip ./ChmLib-ds6.zip
Then open ChmLib.dsw in Visual Studio, change the configuration to Release
(Win32|x64) and build solution, this will generate a static library in
Release/ChmLib.lib
Qt
--------
Download Qt sourcecode (.zip) from: http://download.qt-project.org/official_releases/qt/
@ -225,328 +520,6 @@ Compiling instructions::
nmake
nmake install
ICU
-------
Download the win32 source .zip from http://www.icu-project.org/download
Extract to q:\icu
Add Q:\icu\bin to PATH and reboot
In a Visual Studio Command Prompt
cd to <ICU>\source
Run set PATH=%PATH%;c:\cygwin\bin
Run dos2unix on configure and runConfigureICU
Run bash ./runConfigureICU Cygwin/MSVC
Run make (note that you must have GNU make installed in cygwin)
Optionally run make check
zlib
------
Build with::
nmake -f win32/Makefile.msc
nmake -f win32/Makefile.msc test
cp zlib1.dll* ../../bin
cp zlib.lib zdll.* ../../lib
cp zconf.h zlib.h ../../include
jpeg-8
-------
Get the source code from: http://sourceforge.net/projects/libjpeg-turbo/files/
Run::
chmod +x cmakescripts/* && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DWITH_JPEG8=1 ..
nmake
cp sharedlib/jpeg8.dll* ~/sw/bin/
cp sharedlib/jpeg.lib ~/sw/lib/
cp jconfig.h ../jerror.h ../jpeglib.h ../jmorecfg.h ~/sw/include
libpng
---------
Download the libpng .zip source file from:
http://www.libpng.org/pub/png/libpng.html
Run::
mkdir build && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DZLIB_INCLUDE_DIR=C:/cygwin/home/kovid/sw/include -DZLIB_LIBRARY=C:/cygwin/home/kovid/sw/lib/zdll.lib ..
nmake
cp libpng*.dll ~/sw/bin/
cp libpng*.lib ~/sw/lib/
cp pnglibconf.h ../png.h ../pngconf.h ~/sw/include/
freetype
-----------
Get the .zip source from: http://download.savannah.gnu.org/releases/freetype/
Edit *all copies* of the file ftoption.h and add to generate a .lib
and a correct dll
#define FT_EXPORT(return_type) __declspec(dllexport) return_type
#define FT_EXPORT_DEF(return_type) __declspec(dllexport) return_type
VS 2008 .sln file is present, open it
* If you are doing x64 build, click the Win32 dropdown, select
Configuration manager->Active solution platform -> New -> x64
* Change active build type to release mutithreaded
* Project->Properties->Configuration Properties change configuration type
to dll and build solution
cp "`find . -name *.dll`" ~/sw/bin/
cp "`find . -name freetype.lib`" ~/sw/lib/
Now change configuration back to static for .lib and build solution
cp "`find . -name freetype*MT.lib`" ~/sw/lib/
cp build/freetype-2.3.9/objs/win32/vc2008/freetype239MT.lib lib/
cp -rf include/* ~/sw/include/
TODO: Test if this bloody thing actually works on 64 bit (apparently freetype
assumes sizeof(long) == sizeof(ptr) which is not true in Win64. See for
example: http://forum.openscenegraph.org/viewtopic.php?t=2880
expat
--------
Get from: http://sourceforge.net/projects/expat/files/expat/
Apparently expat requires stdint.h which VS 2008 does not have. So we get our
own.
Run::
cd lib
wget http://msinttypes.googlecode.com/svn/trunk/stdint.h
mkdir build && cd build
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ..
nmake
cp expat.dll ~/sw/bin/ && cp expat.lib ~/sw/lib/
cp ../lib/expat.h ../lib/expat_external.h ~/sw/include
libiconv
----------
Run::
mkdir vs2008 && cd vs2008
Then follow these instructions:
http://www.codeproject.com/Articles/302012/How-to-Build-libiconv-with-Microsoft-Visual-Studio
Change the type to Release and config to x64 or Win32 and Build solution and
then::
cp "`find . -name *.dll`" ~/sw/bin/
cp "`find . -name *.dll.manifest`" ~/sw/bin/
cp "`find . -name *.lib`" ~/sw/lib/iconv.lib
cp "`find . -name iconv.h`" ~/sw/include/
Information for using a static version of libiconv is at the link above.
libxml2
-------------
Get it from: ftp://xmlsoft.org/libxml2/
Run::
cd win32
cscript.exe configure.js include=C:/cygwin/home/kovid/sw/include lib=C:/cygwin/home/kovid/sw/lib prefix=C:/cygwin/home/kovid/sw zlib=yes iconv=yes
nmake /f Makefile.msvc
mkdir -p ~/sw/include/libxml2/libxml
cp include/libxml/*.h ~/sw/include/libxml2/libxml/
find . -type f \( -name "*.dll" -o -name "*.dll.manifest" \) -exec cp "{}" ~/sw/bin/ \;
find . -name libxml2.lib -exec cp "{}" ~/sw/lib/ \;
libxslt
---------
Get it from: ftp://xmlsoft.org/libxml2/
Run::
cd win32
cscript.exe configure.js include=C:/cygwin/home/kovid/sw/include include=C:/cygwin/home/kovid/sw/include/libxml2 lib=C:/cygwin/home/kovid/sw/lib prefix=C:/cygwin/home/kovid/sw zlib=yes iconv=yes
nmake /f Makefile.msvc
mkdir -p ~/sw/include/libxslt ~/sw/include/libexslt
cp libxslt/*.h ~/sw/include/libxslt/
cp libexslt/*.h ~/sw/include/libexslt/
find . -type f \( -name "*.dll" -o -name "*.dll.manifest" \) -exec cp "{}" ~/sw/bin/ \;
find . -name lib*xslt.lib -exec cp "{}" ~/sw/lib/ \;
lxml
------
Get the source from: http://pypi.python.org/pypi/lxml
Change the include dirs and lib dirs by editing setupinfo.py and changing the
library_dirs() function to return::
return ['C:/cygwin/home/kovid/sw/lib']
and the include_dirs() function to return
return ['C:/cygwin/home/kovid/sw/include/libxml2', 'C:/cygwin/home/kovid/sw/include']
Run::
python setup.py install
Python Imaging Library
------------------------
For 32-bit:
Install as normal using installer at http://www.lfd.uci.edu/~gohlke/pythonlibs/
For 64-bit:
Download from http://pypi.python.org/pypi/Pillow/
Edit setup.py setting the ROOT values, like this::
SW = r'C:\cygwin\home\kovid\sw'
JPEG_ROOT = ZLIB_ROOT = FREETYPE_ROOT = (SW+r'\lib', SW+r'\include')
Build and install with::
python setup.py build
python setup.py install
Note that the lcms module will not be built. PIL requires lcms-1.x but only
lcms-2.x can be compiled as a 64 bit library.
Pillow >= 2.2 installs itself as a .egg file. calibre needs it to be a PIL
directory. Extract the PIL directory as follows:
cd /cygdrive/c/Python27/Lib/site-packages
mkdir p && cd p
unzip ../Pillow-*.egg
cd .. && rm Pillow-*.egg && mv p/PIL . && chmod +x PIL/*.pyd
Test it on the target system with
calibre-debug -c "from PIL import Image; import _imaging, _imagingmath, _imagingft"
kdewin32-msvc
----------------
I dont think this is needed any more, I've left it here just in case I'm wrong.
Get it from http://www.winkde.org/pub/kde/ports/win32/repository/kdesupport/
mkdir build
Run cmake
Set build type to release and configuration to dll
Build
cp build/kdewin32-msvc-0.3.9/build/include/* include/
cp build/kdewin32-msvc-0.3.9/build/bin/Release/*.dll bin/
cp build/kdewin32-msvc-0.3.9/build/bin/Release/*.lib lib/
cp build/kdewin32-msvc-0.3.9/build/bin/Release/*.exp lib/
cp -r build/kdewin32-msvc-0.3.9/include/msvc/ include/
cp build/kdewin32-msvc-0.3.9/include/*.h include/
poppler
-------------
mkdir build
Run the cmake GUI which will find the various dependencies automatically.
On 64 bit cmake might not let you choose Visual Studio 2008, in whcih case
leave the source field blank, click configure choose Visual Studio 2008 and
then enter the source field.
In Cmake: disable GTK, Qt, OPenjpeg, cpp, lcms, gtk_tests, qt_tests. Enable
jpeg, png and zlib::
cp build/utils/Release/*.exe ../../bin/
podofo
----------
Download from http://podofo.sourceforge.net/download.html
Add the following three lines near the top of CMakeLists.txt
SET(WANT_LIB64 FALSE)
SET(PODOFO_BUILD_SHARED TRUE)
SET(PODOFO_BUILD_STATIC FALSE)
Run::
cp "`find . -name *.dll`" ~/sw/bin/
cp "`find . -name *.lib`" ~/sw/lib/
mkdir ~/sw/include/podofo
cp build/podofo_config.h ~/sw/include/podofo
cp -r src/* ~/sw/include/podofo/
ImageMagick
--------------
Get the source from: http://www.imagemagick.org/download/windows/ImageMagick-windows.zip
Edit VisualMagick/configure/configure.cpp to set
int projectType = MULTITHREADEDDLL;
Run configure.bat in a visual studio command prompt
Run configure.exe generated by configure.bat
Edit magick/magick-config.h
Undefine ProvideDllMain and MAGICKCORE_X11_DELEGATE
Now open VisualMagick/VisualDynamicMT.sln set to Release
Remove the CORE_xlib, UTIL_Imdisplay and CORE_Magick++ projects.
F7 for build solution, you will get one error due to the removal of xlib, ignore
it.
netifaces
------------
Download the source tarball from http://alastairs-place.net/projects/netifaces/
Rename netifaces.c to netifaces.cpp and make the same change in setup.py
Run::
python setup.py build
cp `find build/ -name *.pyd` /cygdrive/c/Python27/Lib/site-packages/
psutil
--------
Download the source tarball
Run
Python setup.py build
cp -r build/lib.win32-*/* /cygdrive/c/Python27/Lib/site-packages/
easylzma
----------
This is only needed to build the portable installer.
Get it from http://lloyd.github.com/easylzma/ (use the trunk version)
Run cmake and build the Visual Studio solution (generates CLI tools and dll and
static lib automatically)
chmlib
-------
Download the zip source code from: http://www.jedrea.com/chmlib/
Run::
cd src && unzip ./ChmLib-ds6.zip
Then open ChmLib.dsw in Visual Studio, change the configuration to Release
(Win32|x64) and build solution, this will generate a static library in
Release/ChmLib.lib
libimobiledevice
------------------

View File

@ -21,7 +21,7 @@ class InvalidPlugin(ValueError):
pass
class Plugin(object): # {{{
class Plugin(object): # {{{
'''
A calibre plugin. Useful members include:
@ -225,7 +225,6 @@ class Plugin(object): # {{{
ans[candidate] = zf.read(candidate)
return ans
def customization_help(self, gui=False):
'''
Return a string giving help on how to customize this plugin.
@ -284,7 +283,6 @@ class Plugin(object): # {{{
sys.path.insert(0, self.sys_insertion_path)
zf.close()
def __exit__(self, *args):
ip, it = getattr(self, 'sys_insertion_path', None), getattr(self,
'_sys_insertion_tdir', None)
@ -304,7 +302,7 @@ class Plugin(object): # {{{
# }}}
class FileTypePlugin(Plugin): # {{{
class FileTypePlugin(Plugin): # {{{
'''
A plugin that is associated with a particular set of file types.
'''
@ -356,13 +354,13 @@ class FileTypePlugin(Plugin): # {{{
:param book_id: Database id of the added book.
:param book_format: The file type of the book that was added.
:param db: Library database.
:param db: Library database.
'''
pass # Default implementation does nothing
pass # Default implementation does nothing
# }}}
class MetadataReaderPlugin(Plugin): # {{{
class MetadataReaderPlugin(Plugin): # {{{
'''
A plugin that implements reading metadata from a set of file types.
'''
@ -392,7 +390,7 @@ class MetadataReaderPlugin(Plugin): # {{{
return None
# }}}
class MetadataWriterPlugin(Plugin): # {{{
class MetadataWriterPlugin(Plugin): # {{{
'''
A plugin that implements reading metadata from a set of file types.
'''
@ -423,7 +421,7 @@ class MetadataWriterPlugin(Plugin): # {{{
# }}}
class CatalogPlugin(Plugin): # {{{
class CatalogPlugin(Plugin): # {{{
'''
A plugin that implements a catalog generator.
'''
@ -516,8 +514,7 @@ class CatalogPlugin(Plugin): # {{{
from calibre.customize.ui import config
from calibre.ptempfile import PersistentTemporaryDirectory
if not type(self) in builtin_plugins and \
not self.name in config['disabled_plugins']:
if not type(self) in builtin_plugins and self.name not in config['disabled_plugins']:
files_to_copy = ["%s.%s" % (self.name.lower(),ext) for ext in ["ui","py"]]
resources = zipfile.ZipFile(self.plugin_path,'r')
@ -553,7 +550,7 @@ class CatalogPlugin(Plugin): # {{{
# }}}
class InterfaceActionBase(Plugin): # {{{
class InterfaceActionBase(Plugin): # {{{
supported_platforms = ['windows', 'osx', 'linux']
author = 'Kovid Goyal'
@ -580,7 +577,7 @@ class InterfaceActionBase(Plugin): # {{{
# }}}
class PreferencesPlugin(Plugin): # {{{
class PreferencesPlugin(Plugin): # {{{
'''
A plugin representing a widget displayed in the Preferences dialog.
@ -639,7 +636,7 @@ class PreferencesPlugin(Plugin): # {{{
# }}}
class StoreBase(Plugin): # {{{
class StoreBase(Plugin): # {{{
supported_platforms = ['windows', 'osx', 'linux']
author = 'John Schember'
@ -688,7 +685,7 @@ class StoreBase(Plugin): # {{{
# }}}
class ViewerPlugin(Plugin): # {{{
class ViewerPlugin(Plugin): # {{{
'''
These plugins are used to add functionality to the calibre viewer.
@ -733,6 +730,7 @@ class ViewerPlugin(Plugin): # {{{
example, you can modify the toolbars via ui.tool_bar and ui.tool_bar2.
'''
pass
def customize_context_menu(self, menu, event, hit_test_result):
'''
This method is called every time the context (right-click) menu is
@ -743,4 +741,3 @@ class ViewerPlugin(Plugin): # {{{
pass
# }}}

View File

@ -14,6 +14,7 @@ from cssselect.xpath import XPathExpr, is_safe_name
from calibre import force_unicode
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS, XPNSMAP, XHTML_NS
from calibre.ebooks.oeb.normalize_css import normalize_filter_css, normalizers
from calibre.ebooks.oeb.stylizer import MIN_SPACE_RE, is_non_whitespace, xpath_lower_case, fix_namespace
from calibre.ebooks.oeb.polish.pretty import pretty_script_or_style
@ -167,3 +168,80 @@ def remove_unused_css(container, report):
else:
report(_('No unused CSS style rules found'))
return num_of_removed_rules > 0
def filter_declaration(style, properties):
changed = False
for prop in properties:
if style.removeProperty(prop) != '':
changed = True
all_props = set(style.keys())
for prop in style.getProperties():
n = normalizers.get(prop.name, None)
if n is not None:
normalized = n(prop.name, prop.propertyValue)
removed = properties.intersection(set(normalized))
if removed:
changed = True
style.removeProperty(prop.name)
for prop in set(normalized) - removed - all_props:
style.setProperty(prop, normalized[prop])
return changed
def filter_sheet(sheet, properties):
from cssutils.css import CSSRule
changed = False
remove = []
for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE):
if filter_declaration(rule.style, properties):
changed = True
if rule.style.length == 0:
remove.append(rule)
for rule in remove:
sheet.cssRules.remove(rule)
return changed
def filter_css(container, properties, names=()):
if not names:
types = OEB_STYLES | OEB_DOCS
names = []
for name, mt in container.mime_map.iteritems():
if mt in types:
names.append(name)
properties = normalize_filter_css(properties)
doc_changed = False
for name in names:
mt = container.mime_map[name]
if mt in OEB_STYLES:
sheet = container.parsed(name)
filtered = filter_sheet(sheet, properties)
if filtered:
container.dirty(name)
doc_changed = True
elif mt in OEB_DOCS:
root = container.parsed(name)
changed = False
for style in root.xpath('//*[local-name()="style"]'):
if style.text and style.get('type', 'text/css') in {None, '', 'text/css'}:
sheet = container.parse_css(style.text)
if filter_sheet(sheet, properties):
changed = True
style.text = force_unicode(sheet.cssText, 'utf-8')
pretty_script_or_style(container, style)
for elem in root.xpath('//*[@style]'):
text = elem.get('style', None)
if text:
style = container.parse_css(text, is_declaration=True)
if filter_declaration(style, properties):
changed = True
if style.length == 0:
del elem.attrib['style']
else:
elem.set('style', force_unicode(style.getCssText(separator=' '), 'utf-8'))
if changed:
container.dirty(name)
doc_changed = True
return doc_changed

View File

@ -128,11 +128,17 @@ def read_words_from_html_tag(tag, words, file_name, parent_locale, locale):
def locale_from_tag(tag):
if 'lang' in tag.attrib:
loc = parse_lang_code(tag.get('lang'))
try:
loc = parse_lang_code(tag.get('lang'))
except ValueError:
loc = None
if loc is not None:
return loc
if '{http://www.w3.org/XML/1998/namespace}lang' in tag.attrib:
loc = parse_lang_code(tag.get('{http://www.w3.org/XML/1998/namespace}lang'))
try:
loc = parse_lang_code(tag.get('{http://www.w3.org/XML/1998/namespace}lang'))
except ValueError:
loc = None
if loc is not None:
return loc

View File

@ -253,6 +253,10 @@ class StatsCollector(object):
src = rule.get('src', None)
if not src:
continue
if src.startswith('url(') and src.endswith(')') and src[4] not in {'"', "'"}:
# Quote the url otherwise cssutils fails to parse it if it has
# ' or " in it
src = "url('" + src[4:-1].replace("'", "\\'") + "')"
style = self.parser.parseStyle('background-image:%s'%src, validate=False)
src = style.getProperty('background-image').propertyValue[0].uri
name = self.href_to_name(src, '@font-face rule')

View File

@ -432,7 +432,20 @@ class PolishAction(InterfaceAction):
return None
db = self.gui.library_view.model().db
ans = (db.id(r) for r in rows)
return self.get_supported_books(ans)
ans = self.get_supported_books(ans)
for fmts in ans.itervalues():
for x in fmts:
if x.startswith('ORIGINAL_'):
from calibre.gui2.dialogs.confirm_delete import confirm
if not confirm(_(
'One of the books you are polishing has an {0} format.'
' Polishing will use this as the source and overwrite'
' any existing {1} format. Are you sure you want to proceed?').format(
x, x[len('ORIGINAL_'):]), 'confirm_original_polish', title=_('Are you sure?'),
confirm_msg=_('Ask for this confirmation again')):
return {}
break
return ans
def get_supported_books(self, book_ids):
from calibre.ebooks.oeb.polish.main import SUPPORTED

View File

@ -18,8 +18,9 @@ from calibre import prints, isbytestring
from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory
from calibre.ebooks.oeb.base import urlnormalize
from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type, OEB_FONTS
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type, OEB_FONTS, OEB_DOCS, OEB_STYLES
from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage
from calibre.ebooks.oeb.polish.css import filter_css
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
from calibre.ebooks.oeb.polish.replace import rename_files, replace_file, get_recommended_folders, rationalize_folders
from calibre.ebooks.oeb.polish.split import split, merge, AbortError, multisplit
@ -41,7 +42,7 @@ from calibre.gui2.tweak_book.search import validate_search_request, run_search
from calibre.gui2.tweak_book.spell import find_next as find_next_word
from calibre.gui2.tweak_book.widgets import (
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
InsertSemantics, BusyCursor, InsertTag)
InsertSemantics, BusyCursor, InsertTag, FilterCSS)
_diff_dialogs = []
@ -674,6 +675,31 @@ class Boss(QObject):
d.apply_changes(current_container())
self.apply_container_update_to_gui()
def filter_css(self):
self.commit_all_editors_to_container()
c = current_container()
ed = self.gui.central.current_editor
for name, q in editors.iteritems():
if ed is q:
current_name = name
break
else:
current_name = None
if current_name and c.mime_map[current_name] not in OEB_DOCS | OEB_STYLES:
current_name = None
d = FilterCSS(current_name=current_name, parent=self.gui)
if d.exec_() == d.Accepted and d.filtered_properties:
self.add_savepoint(_('Before: Filter style information'))
with BusyCursor():
changed = filter_css(current_container(), d.filtered_properties, names=d.filter_names)
if changed:
self.apply_container_update_to_gui()
self.show_current_diff()
else:
self.rewind_savepoint()
return info_dialog(self.gui, _('No matches'), _(
'No matching style rules were found'), show=True)
def show_find(self):
self.gui.central.show_find()
ed = self.gui.central.current_editor

View File

@ -9,7 +9,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import sys
from PyQt4.Qt import (
QIcon, Qt, QSplitter, QListWidget, QTextBrowser, QPalette,
QIcon, Qt, QSplitter, QListWidget, QTextBrowser, QPalette, QMenu,
QListWidgetItem, pyqtSignal, QApplication, QStyledItemDelegate)
from calibre.ebooks.oeb.polish.check.base import WARN, INFO, DEBUG, ERROR, CRITICAL
@ -28,6 +28,19 @@ def icon_for_level(level):
icon = None
return QIcon(I(icon)) if icon else QIcon()
def prefix_for_level(level):
if level > WARN:
text = _('ERROR')
elif level == WARN:
text = _('WARNING')
elif level == INFO:
text = _('INFO')
else:
text = ''
if text:
text += ': '
return text
class Delegate(QStyledItemDelegate):
def initStyleOption(self, option, index):
@ -47,6 +60,8 @@ class Check(QSplitter):
self.setChildrenCollapsible(False)
self.items = i = QListWidget(self)
i.setContextMenuPolicy(Qt.CustomContextMenu)
i.customContextMenuRequested.connect(self.context_menu)
self.items.setSpacing(3)
self.items.itemDoubleClicked.connect(self.current_item_activated)
self.items.currentItemChanged.connect(self.current_item_changed)
@ -66,6 +81,22 @@ class Check(QSplitter):
if state is not None:
self.restoreState(state)
def context_menu(self, pos):
m = QMenu()
if self.items.count() > 0:
m.addAction(QIcon(I('edit-copy.png')), _('Copy list of errors to clipboard'), self.copy_to_clipboard)
if list(m.actions()):
m.exec_(self.mapToGlobal(pos))
def copy_to_clipboard(self):
items = []
for item in (self.items.item(i) for i in xrange(self.items.count())):
msg = unicode(item.text())
msg = prefix_for_level(item.data(Qt.UserRole).toPyObject().level) + msg
items.append(msg)
if items:
QApplication.clipboard().setText('\n'.join(items))
def save_state(self):
tprefs.set('check-book-splitter-state', bytearray(self.saveState()))

View File

@ -349,6 +349,8 @@ class Main(MainWindow):
_('Arrange into folders'))
self.action_set_semantics = reg('tags.png', _('Set &Semantics'), self.boss.set_semantics, 'set-semantics', (),
_('Set Semantics'))
self.action_filter_css = reg('filter.png', _('&Filter style information'), self.boss.filter_css, 'filter-css', (),
_('Filter style information'))
# Polish actions
group = _('Polish Book')
@ -482,6 +484,7 @@ class Main(MainWindow):
e.addAction(self.action_pretty_all)
e.addAction(self.action_rationalize_folders)
e.addAction(self.action_set_semantics)
e.addAction(self.action_filter_css)
e.addAction(self.action_spell_check_book)
e.addAction(self.action_check_book)

View File

@ -15,7 +15,7 @@ from PyQt4.Qt import (
QFormLayout, QHBoxLayout, QToolButton, QIcon, QApplication, Qt, QWidget,
QPoint, QSizePolicy, QPainter, QStaticText, pyqtSignal, QTextOption,
QAbstractListModel, QModelIndex, QVariant, QStyledItemDelegate, QStyle,
QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor)
QListView, QTextDocument, QSize, QComboBox, QFrame, QCursor, QCheckBox)
from calibre import prepare_string_for_xml
from calibre.ebooks.oeb.polish.utils import lead_text
@ -689,7 +689,7 @@ class InsertLink(Dialog):
frag = item.get('id', None) or item.get('name')
text = lead_text(item, num_words=4)
ac.append((text, frag))
ac.sort(key=lambda (text, frag): primary_sort_key(text))
ac.sort(key=lambda text_frag: primary_sort_key(text_frag[0]))
self.anchor_names.model().set_names(self.anchor_cache[name])
self.update_target()
@ -924,6 +924,75 @@ class InsertSemantics(Dialog):
# }}}
class FilterCSS(Dialog): # {{{
def __init__(self, current_name=None, parent=None):
self.current_name = current_name
Dialog.__init__(self, _('Filter Style Information'), 'filter-css', parent=parent)
def setup_ui(self):
from calibre.gui2.convert.look_and_feel_ui import Ui_Form
f, w = Ui_Form(), QWidget()
f.setupUi(w)
self.l = l = QFormLayout(self)
self.setLayout(l)
l.addRow(QLabel(_('Select what style information you want completely removed:')))
self.h = h = QHBoxLayout()
for name, text in {
'fonts':_('&Fonts'), 'margins':_('&Margins'), 'padding':_('&Padding'), 'floats':_('Flo&ats'), 'colors':_('&Colors')}.iteritems():
c = QCheckBox(text)
setattr(self, 'opt_' + name, c)
h.addWidget(c)
c.setToolTip(getattr(f, 'filter_css_' + name).toolTip())
l.addRow(h)
self.others = o = QLineEdit(self)
l.addRow(_('&Other CSS properties:'), o)
o.setToolTip(f.filter_css_others.toolTip())
if self.current_name is not None:
self.filter_current = c = QCheckBox(_('Only filter CSS in the current file (%s)') % self.current_name)
l.addRow(c)
l.addRow(self.bb)
@property
def filter_names(self):
if self.current_name is not None and self.filter_current.isChecked():
return (self.current_name,)
return ()
@property
def filtered_properties(self):
ans = set()
a = ans.add
if self.opt_fonts.isChecked():
a('font-family')
if self.opt_margins.isChecked():
a('margin')
if self.opt_padding.isChecked():
a('padding')
if self.opt_floats.isChecked():
a('float'), a('clear')
if self.opt_colors.isChecked():
a('color'), a('background-color')
for x in unicode(self.others.text()).split(','):
x = x.strip()
if x:
a(x)
return ans
@classmethod
def test(cls):
d = cls()
if d.exec_() == d.Accepted:
print (d.filtered_properties)
# }}}
if __name__ == '__main__':
app = QApplication([])
InsertLink.test()
FilterCSS.test()

View File

@ -178,6 +178,27 @@ class BuiltinCmp(BuiltinFormatterFunction):
return eq
return gt
class BuiltinFirstMatchingCmp(BuiltinFormatterFunction):
name = 'first_matching_cmp'
category = 'Relational'
arg_count = -1
__doc__ = doc = _('first_matching_cmp(val, cmp1, result1, cmp2, r2, ..., else_result) -- '
'compares "val < cmpN" in sequence, returning resultN for '
'the first comparison that succeeds. Returns else_result '
'if no comparison succeeds. Example: '
'first_matching_cmp(10,5,"small",10,"middle",15,"large","giant") '
'returns "middle". The same example with a first value of 16 returns "giant".')
def evaluate(self, formatter, kwargs, mi, locals, *args):
if (len(args) % 2) != 0:
raise ValueError(_('first_matching_cmp requires an even number of arguments'))
val = float(args[0] if args[0] and args[0] != 'None' else 0)
for i in range(1, len(args) - 1, 2):
c = float(args[i] if args[i] and args[i] != 'None' else 0)
if val < c:
return args[i+1]
return args[len(args)-1]
class BuiltinStrcat(BuiltinFormatterFunction):
name = 'strcat'
arg_count = -1
@ -1310,9 +1331,9 @@ _formatter_builtins = [
BuiltinCapitalize(), BuiltinCmp(), BuiltinContains(), BuiltinCount(),
BuiltinCurrentLibraryName(), BuiltinCurrentLibraryPath(),
BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), BuiltinFirstNonEmpty(),
BuiltinField(), BuiltinFinishFormatting(), BuiltinFormatDate(),
BuiltinFormatNumber(), BuiltinFormatsModtimes(), BuiltinFormatsPaths(),
BuiltinFormatsSizes(),
BuiltinField(), BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(),
BuiltinFormatDate(), BuiltinFormatNumber(), BuiltinFormatsModtimes(),
BuiltinFormatsPaths(), BuiltinFormatsSizes(),
BuiltinHasCover(), BuiltinHumanReadable(), BuiltinIdentifierInList(),
BuiltinIfempty(), BuiltinLanguageCodes(), BuiltinLanguageStrings(),
BuiltinInList(), BuiltinListDifference(), BuiltinListEquals(),

View File

@ -140,6 +140,7 @@ class TestICU(unittest.TestCase):
def test_contractions(self):
' Test contractions '
self.skipTest('Skipping as this depends too much on ICU version')
c = icu._icu.Collator('cs')
self.ae(icu.contractions(c), frozenset({u'Z\u030c', u'z\u030c', u'Ch',
u'C\u030c', u'ch', u'cH', u'c\u030c', u's\u030c', u'r\u030c', u'CH',