Merge from trunk

This commit is contained in:
Charles Haley 2012-12-28 09:04:37 +01:00
commit 4ad5c5f8f6
143 changed files with 33140 additions and 24418 deletions

View File

@ -19,6 +19,46 @@
# new recipes:
# - title:
- version: 0.9.12
date: 2012-12-28
new features:
- title: "Drivers for Kibano e-reader and Slick ER-700-2"
tickets: [1093570, 1093732]
- title: "Add support for downloading metadata from Amazon Brazil."
tickets: [1092594]
- title: "Copy to library: Allow specifying the destination library by path."
tickets: [1093231]
- title: "When adding empty books, allow setting of the series for the new books. Also select the newly added book records after adding."
- title: "PDF Output: Add a checkbox to override the page size defined by the output profile. This allows you to specify a custom page size even if the output profile is not set to default."
- title: "Add usb ids for newer kindle fire to the linux mtp driver"
bug fixes:
- title: "Linux: Temporarily redirect stdout to get rid of the annoying and pointless message about mtpz during libmtp initialization"
- title: "Fix multiple 'All column' coloring rules not being applied"
tickets: [1093574]
- title: "Use custom icons in the content server as well."
tickets: [1092098]
improved recipes:
- La Voce
- Harpers Magazine (printed edition)
- Pajamas Media
- NSFW corp
- The Hindu
- Nikkei News
new recipes:
- title: Various Ukranian news sources
author: rpalyvoda
- version: 0.9.11
date: 2012-12-21

View File

@ -1,18 +1,22 @@
__license__ = 'GPL v3'
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2008-2012, Darko Miletic <darko.miletic at gmail.com>'
'''
harpers.org - paid subscription/ printed issue articles
This recipe only get's article's published in text format
images and pdf's are ignored
If you have institutional subscription based on access IP you do not need to enter
anything in username/password fields
'''
import time
import urllib
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class Harpers_full(BasicNewsRecipe):
title = "Harper's Magazine - articles from printed edition"
__author__ = 'Darko Miletic'
description = "Harper's Magazine: Founded June 1850."
description = "Harper's Magazine, the oldest general-interest monthly in America, explores the issues that drive our national conversation, through long-form narrative journalism and essays, and such celebrated features as the iconic Harper's Index."
publisher = "Harpers's"
category = 'news, politics, USA'
oldest_article = 30
@ -21,52 +25,69 @@ class Harpers_full(BasicNewsRecipe):
use_embedded_content = False
delay = 1
language = 'en'
needs_subscription = True
masthead_url = 'http://www.harpers.org/media/image/Harpers_305x100.gif'
publication_type = 'magazine'
INDEX = strftime('http://www.harpers.org/archive/%Y/%m')
LOGIN = 'http://www.harpers.org'
cover_url = strftime('http://www.harpers.org/media/pages/%Y/%m/gif/0001.gif')
extra_css = ' body{font-family: "Georgia",serif} '
encoding = 'utf8'
needs_subscription = 'optional'
masthead_url = 'http://harpers.org/wp-content/themes/harpers/images/pheader.gif'
publication_type = 'magazine'
INDEX = strftime('http://harpers.org/archive/%Y/%m')
LOGIN = 'http://harpers.org/wp-content/themes/harpers/ajax_login.php'
extra_css = """
body{font-family: adobe-caslon-pro,serif}
.category{font-size: small}
.articlePost p:first-letter{display: inline; font-size: xx-large; font-weight: bold}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
keep_only_tags = [ dict(name='div', attrs={'id':'cached'}) ]
keep_only_tags = [ dict(name='div', attrs={'class':['postdetailFull','articlePost']}) ]
remove_tags = [
dict(name='table', attrs={'class':['rcnt','rcnt topline']})
,dict(name='link')
dict(name='div', attrs={'class':'fRight rightDivPad'})
,dict(name=['link','meta','object','embed','iframe'])
]
remove_attributes=['xmlns']
remove_attributes=['xmlns']
def get_browser(self):
br = BasicNewsRecipe.get_browser()
br.open('http://harpers.org/')
if self.username is not None and self.password is not None:
br.open(self.LOGIN)
br.select_form(nr=1)
br['handle' ] = self.username
br['password'] = self.password
br.submit()
tt = time.localtime()*1000
data = urllib.urlencode({ 'm':self.username
,'p':self.password
,'rt':'http://harpers.org/'
,'tt':tt
})
br.open(self.LOGIN, data)
return br
def parse_index(self):
articles = []
print 'Processing ' + self.INDEX
soup = self.index_to_soup(self.INDEX)
for item in soup.findAll('div', attrs={'class':'title'}):
text_link = item.parent.find('img',attrs={'alt':'Text'})
if text_link:
url = self.LOGIN + item.a['href']
title = item.a.contents[0]
date = strftime(' %B %Y')
articles.append({
'title' :title
,'date' :date
,'url' :url
,'description':''
})
count = 0
for item in soup.findAll('div', attrs={'class':'articleData'}):
text_links = item.findAll('h2')
for text_link in text_links:
if count == 0:
lcover_url = item.find(attrs={'class':'dwpdf'})
if lcover_url:
self.cover_url = lcover_url.a['href']
count = 1
else:
url = text_link.a['href']
title = text_link.a.contents[0]
date = strftime(' %B %Y')
articles.append({
'title' :title
,'date' :date
,'url' :url
,'description':''
})
return [(soup.head.title.string, articles)]
def print_version(self, url):
return url + '?single=1'

View File

@ -7,9 +7,9 @@ class AdvancedUserRecipe1324114228(BasicNewsRecipe):
max_articles_per_feed = 100
auto_cleanup = True
masthead_url = 'http://www.lavoce.info/binary/la_voce/testata/lavoce.1184661635.gif'
feeds = [(u'La Voce', u'http://www.lavoce.info/feed_rss.php?id_feed=1')]
feeds = [(u'La Voce', u'http://www.lavoce.info/feed/')]
__author__ = 'faber1971'
description = 'Italian website on Economy - v1.01 (17, December 2011)'
description = 'Italian website on Economy - v1.02 (27, December 2012)'
language = 'it'

View File

@ -0,0 +1,12 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1356270446(BasicNewsRecipe):
title = u'\u041b\u044c\u0432\u0456\u0432\u0441\u044c\u043a\u0430 \u0433\u0430\u0437\u0435\u0442\u0430'
__author__ = 'rpalyvoda'
oldest_article = 7
max_articles_per_feed = 100
language = 'uk'
cover_url = 'http://lvivska.com/sites/all/themes/biblos/images/logo.png'
masthead_url = 'http://lvivska.com/sites/all/themes/biblos/images/logo.png'
auto_cleanup = True
feeds = [(u'\u041d\u043e\u0432\u0438\u043d\u0438', u'http://lvivska.com/rss/news.xml'), (u'\u041f\u043e\u043b\u0456\u0442\u0438\u043a\u0430', u'http://lvivska.com/rss/politic.xml'), (u'\u0415\u043a\u043e\u043d\u043e\u043c\u0456\u043a\u0430', u'http://lvivska.com/rss/economic.xml'), (u'\u041f\u0440\u0430\u0432\u043e', u'http://lvivska.com/rss/law.xml'), (u'\u0421\u0432\u0456\u0442', u'http://lvivska.com/rss/world.xml'), (u'\u0416\u0438\u0442\u0442\u044f', u'http://lvivska.com/rss/life.xml'), (u'\u041a\u0443\u043b\u044c\u0442\u0443\u0440\u0430', u'http://lvivska.com/rss/culture.xml'), (u'\u041b\u0430\u0441\u0443\u043d', u'http://lvivska.com/rss/cooking.xml'), (u'\u0421\u0442\u0438\u043b\u044c', u'http://lvivska.com/rss/style.xml'), (u'Galicia Incognita', u'http://lvivska.com/rss/galiciaincognita.xml'), (u'\u0421\u043f\u043e\u0440\u0442', u'http://lvivska.com/rss/sport.xml'), (u'\u0415\u043a\u043e\u043b\u043e\u0433\u0456\u044f', u'http://lvivska.com/rss/ecology.xml'), (u"\u0417\u0434\u043e\u0440\u043e\u0432'\u044f", u'http://lvivska.com/rss/health.xml'), (u'\u0410\u0432\u0442\u043e', u'http://lvivska.com/rss/auto.xml'), (u'\u0411\u043b\u043e\u0433\u0438', u'http://lvivska.com/rss/blog.xml')]

View File

@ -0,0 +1,13 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1356283265(BasicNewsRecipe):
title = u'\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0438\u0439 \u0422\u0438\u0436\u0434\u0435\u043d\u044c'
__author__ = 'rpalyvoda'
oldest_article = 7
max_articles_per_feed = 100
language = 'uk'
cover_url = 'http://tyzhden.ua/Images/Style1/tyzhden.ua-logo2.gif'
masthead_url = 'http://tyzhden.ua/Images/Style1/tyzhden.ua-logo2.gif'
auto_cleanup = True
feeds = [(u'\u041d\u043e\u0432\u0438\u043d\u0438', u'http://tyzhden.ua/RSS/News/'), (u'\u041e\u0440\u0438\u0433\u0456\u043d\u0430\u043b\u044c\u043d\u0456 \u043d\u043e\u0432\u0438\u043d\u0438', u'http://tyzhden.ua/RSS/News.Original/'), (u'\u041f\u0443\u0431\u043b\u0456\u043a\u0430\u0446\u0456\u0457', u'http://tyzhden.ua/RSS/Publications/')]

13
recipes/zaxid_net.recipe Normal file
View File

@ -0,0 +1,13 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1356281741(BasicNewsRecipe):
title = u'Zaxid.net'
__author__ = 'rpalyvoda'
oldest_article = 7
max_articles_per_feed = 100
language = 'uk'
cover_url = 'http://upload.wikimedia.org/wikipedia/uk/b/bc/Zaxid-net.jpg'
masthead_url = 'http://upload.wikimedia.org/wikipedia/uk/b/bc/Zaxid-net.jpg'
auto_cleanup = True
feeds = [(u'\u0422\u043e\u043f \u043d\u043e\u0432\u0438\u043d\u0438', u'http://feeds.feedburner.com/zaxid/topNews'), (u'\u0421\u0442\u0440\u0456\u0447\u043a\u0430 \u043d\u043e\u0432\u0438\u043d', u'http://feeds.feedburner.com/zaxid/AllNews'), (u'\u041d\u043e\u0432\u0438\u043d\u0438 \u041b\u044c\u0432\u043e\u0432\u0430', u'http://feeds.feedburner.com/zaxid/Lviv'), (u'\u041d\u043e\u0432\u0438\u043d\u0438 \u0423\u043a\u0440\u0430\u0457\u043d\u0438', u'http://feeds.feedburner.com/zaxid/Ukraine'), (u'\u041d\u043e\u0432\u0438\u043d\u0438 \u0441\u0432\u0456\u0442\u0443', u'http://feeds.feedburner.com/zaxid/World'), (u'\u041d\u043e\u0432\u0438\u043d\u0438 - \u0420\u0430\u0434\u0456\u043e 24', u'\u0420\u0430\u0434\u0456\u043e 24'), (u'\u0411\u043b\u043e\u0433\u0438', u'http://feeds.feedburner.com/zaxid/Blogs'), (u"\u041f\u0443\u0431\u043b\u0456\u043a\u0430\u0446\u0456\u0457 - \u0406\u043d\u0442\u0435\u0440\u0432'\u044e", u'http://feeds.feedburner.com/zaxid/Interview'), (u'\u041f\u0443\u0431\u043b\u0456\u043a\u0430\u0446\u0456\u0457 - \u0421\u0442\u0430\u0442\u0442\u0456', u'http://feeds.feedburner.com/zaxid/Articles'), (u'\u0410\u0444\u0456\u0448\u0430', u'http://zaxid.net/rss/subcategory/140.xml'), (u'\u0413\u0430\u043b\u0438\u0447\u0438\u043d\u0430', u'http://feeds.feedburner.com/zaxid/Galicia'), (u'\u041a\u0443\u043b\u044c\u0442\u0443\u0440\u0430.NET', u'http://feeds.feedburner.com/zaxid/KulturaNET'), (u"\u043d\u0435\u0412\u0456\u0434\u043e\u043c\u0456 \u043b\u044c\u0432\u0456\u0432'\u044f\u043d\u0438", u'http://feeds.feedburner.com/zaxid/UnknownLviv'), (u'\u041b\u0435\u043e\u043f\u043e\u043b\u0456\u0441 MULTIPLEX', u'http://feeds.feedburner.com/zaxid/LeopolisMULTIPLEX'), (u'\u0411\u0438\u0442\u0432\u0430 \u0437\u0430 \u043c\u043e\u0432\u0443', u'http://zaxid.net/rss/subcategory/138.xml'), (u'\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u0430 \u0441\u0445\u0435\u043c\u0430 \u041b\u044c\u0432\u043e\u0432\u0430', u'http://zaxid.net/rss/subcategory/132.xml'), (u'\u0414\u0435\u043c\u0456\u0444\u043e\u043b\u043e\u0433\u0456\u0437\u0430\u0446\u0456\u044f', u'http://zaxid.net/rss/subcategory/130.xml'), (u"\u041c\u0438 \u043f\u0430\u043c'\u044f\u0442\u0430\u0454\u043c\u043e", u'http://feeds.feedburner.com/zaxid/WeRemember'), (u'20 \u0440\u043e\u043a\u0456\u0432 \u041d\u0435\u0437\u0430\u043b\u0435\u0436\u043d\u043e\u0441\u0442\u0456', u'http://zaxid.net/rss/subcategory/129.xml'), (u'\u041f\u0440\u0430\u0432\u043e \u043d\u0430 \u0434\u0438\u0442\u0438\u043d\u0441\u0442\u0432\u043e', u'http://feeds.feedburner.com/zaxid/Childhood'), (u'\u0410\u043d\u043e\u043d\u0441\u0438', u'http://feeds.feedburner.com/zaxid/Announcements')]

Binary file not shown.

View File

@ -12,6 +12,7 @@ let g:syntastic_cpp_include_dirs = [
\'/usr/include/fontconfig',
\'src/qtcurve/common', 'src/qtcurve',
\'src/unrar',
\'src/qt-harfbuzz/src',
\'/usr/include/ImageMagick',
\]
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs

View File

@ -6,12 +6,13 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, socket, struct, subprocess, sys, glob
import os, socket, struct, subprocess, glob
from distutils.spawn import find_executable
from PyQt4 import pyqtconfig
from setup import isosx, iswindows, islinux, is64bit
is64bit
OSX_SDK = '/Developer/SDKs/MacOSX10.5.sdk'
@ -81,6 +82,7 @@ def consolidate(envvar, default):
pyqt = pyqtconfig.Configuration()
qt_inc = pyqt.qt_inc_dir
qt_private_inc = []
qt_lib = pyqt.qt_lib_dir
ft_lib_dirs = []
ft_libs = []
@ -140,6 +142,8 @@ elif isosx:
png_libs = ['png12']
ft_libs = ['freetype']
ft_inc_dirs = ['/sw/include/freetype2']
bq = glob.glob('/sw/build/qt-*/include')[-1]
qt_private_inc = ['%s/%s'%(bq, m) for m in ('QtGui', 'QtCore')]
else:
# Include directories
png_inc_dirs = pkgconfig_include_dirs('libpng', 'PNG_INC_DIR',

View File

@ -102,7 +102,8 @@ class Check(Command):
errors = True
if errors:
cPickle.dump(cache, open(self.CACHE, 'wb'), -1)
subprocess.call(['gvim', '-f', f])
subprocess.call(['gvim', '-S',
self.j(self.SRC, '../session.vim'), '-f', f])
raise SystemExit(1)
cache[f] = mtime
for x in builtins:

View File

@ -18,7 +18,7 @@ from setup.build_environment import (chmlib_inc_dirs,
msvc, MT, win_inc, win_lib, win_ddk, magick_inc_dirs, magick_lib_dirs,
magick_libs, chmlib_lib_dirs, sqlite_inc_dirs, icu_inc_dirs,
icu_lib_dirs, win_ddk_lib_dirs, ft_libs, ft_lib_dirs, ft_inc_dirs,
zlib_libs, zlib_lib_dirs, zlib_inc_dirs, is64bit)
zlib_libs, zlib_lib_dirs, zlib_inc_dirs, is64bit, qt_private_inc)
MT
isunix = islinux or isosx or isbsd
@ -183,6 +183,13 @@ extensions = [
sip_files = ['calibre/gui2/progress_indicator/QProgressIndicator.sip']
),
Extension('qt_hack',
['calibre/ebooks/pdf/render/qt_hack.cpp'],
inc_dirs = qt_private_inc + ['calibre/ebooks/pdf/render', 'qt-harfbuzz/src'],
headers = ['calibre/ebooks/pdf/render/qt_hack.h'],
sip_files = ['calibre/ebooks/pdf/render/qt_hack.sip']
),
Extension('unrar',
['unrar/%s.cpp'%(x.partition('.')[0]) for x in '''
rar.o strlist.o strfn.o pathfn.o savepos.o smallfn.o global.o file.o
@ -545,6 +552,9 @@ class Build(Command):
VERSION = 1.0.0
CONFIG += %s
''')%(ext.name, ' '.join(ext.headers), ' '.join(ext.sources), archs)
if ext.inc_dirs:
idir = ' '.join(ext.inc_dirs)
pro += 'INCLUDEPATH = %s\n'%idir
pro = pro.replace('\\', '\\\\')
open(ext.name+'.pro', 'wb').write(pro)
qmc = [QMAKE, '-o', 'Makefile']

View File

@ -12,14 +12,14 @@ msgstr ""
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
"devel@lists.alioth.debian.org>\n"
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
"PO-Revision-Date: 2012-12-16 13:27+0000\n"
"PO-Revision-Date: 2012-12-22 17:18+0000\n"
"Last-Translator: Ferran Rius <frius64@hotmail.com>\n"
"Language-Team: Catalan <linux@softcatala.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-17 04:42+0000\n"
"X-Generator: Launchpad (build 16372)\n"
"X-Launchpad-Export-Date: 2012-12-23 04:38+0000\n"
"X-Generator: Launchpad (build 16378)\n"
"Language: ca\n"
#. name for aaa
@ -19044,19 +19044,19 @@ msgstr "Nshi"
#. name for nsd
msgid "Nisu; Southern"
msgstr ""
msgstr "Nisu; Meridional"
#. name for nse
msgid "Nsenga"
msgstr ""
msgstr "Nsenga"
#. name for nsg
msgid "Ngasa"
msgstr ""
msgstr "Ngasa"
#. name for nsh
msgid "Ngoshie"
msgstr ""
msgstr "Ngishe"
#. name for nsi
msgid "Nigerian Sign Language"
@ -19064,7 +19064,7 @@ msgstr "Llenguatge de signes nigerià"
#. name for nsk
msgid "Naskapi"
msgstr ""
msgstr "Naskapi"
#. name for nsl
msgid "Norwegian Sign Language"
@ -19076,7 +19076,7 @@ msgstr "Naga; Sumi"
#. name for nsn
msgid "Nehan"
msgstr ""
msgstr "Nehan"
#. name for nso
msgid "Sotho; Northern"
@ -19096,7 +19096,7 @@ msgstr "Llenguatge de signes marítim"
#. name for nss
msgid "Nali"
msgstr ""
msgstr "Nali"
#. name for nst
msgid "Naga; Tase"
@ -19108,15 +19108,15 @@ msgstr "Nàhuatl; Sierra Negra"
#. name for nsv
msgid "Nisu; Southwestern"
msgstr ""
msgstr "Nisu; Sudoccidental"
#. name for nsw
msgid "Navut"
msgstr ""
msgstr "Navut"
#. name for nsx
msgid "Nsongo"
msgstr ""
msgstr "Nsongo"
#. name for nsy
msgid "Nasal"
@ -19124,19 +19124,19 @@ msgstr ""
#. name for nsz
msgid "Nisenan"
msgstr ""
msgstr "Nisenan"
#. name for nte
msgid "Nathembo"
msgstr ""
msgstr "Nathembo"
#. name for nti
msgid "Natioro"
msgstr ""
msgstr "Natioro"
#. name for ntj
msgid "Ngaanyatjarra"
msgstr ""
msgstr "Ngaanyatjarra"
#. name for ntk
msgid "Ikoma-Nata-Isenye"
@ -19144,11 +19144,11 @@ msgstr ""
#. name for ntm
msgid "Nateni"
msgstr ""
msgstr "Nateni"
#. name for nto
msgid "Ntomba"
msgstr ""
msgstr "Ntomba"
#. name for ntp
msgid "Tepehuan; Northern"
@ -19160,15 +19160,15 @@ msgstr ""
#. name for nts
msgid "Natagaimas"
msgstr ""
msgstr "Natagaimas"
#. name for ntu
msgid "Natügu"
msgstr ""
msgstr "Santa Cruz: Septentrional"
#. name for ntw
msgid "Nottoway"
msgstr ""
msgstr "Nottoway"
#. name for nty
msgid "Mantsi"
@ -19176,7 +19176,7 @@ msgstr ""
#. name for ntz
msgid "Natanzi"
msgstr ""
msgstr "Natanzi"
#. name for nua
msgid "Yuaga"
@ -19184,35 +19184,35 @@ msgstr ""
#. name for nuc
msgid "Nukuini"
msgstr ""
msgstr "Nukini"
#. name for nud
msgid "Ngala"
msgstr ""
msgstr "Ngala"
#. name for nue
msgid "Ngundu"
msgstr ""
msgstr "Ngundu"
#. name for nuf
msgid "Nusu"
msgstr ""
msgstr "Nusu"
#. name for nug
msgid "Nungali"
msgstr ""
msgstr "Nungali"
#. name for nuh
msgid "Ndunda"
msgstr ""
msgstr "Ndunda"
#. name for nui
msgid "Ngumbi"
msgstr ""
msgstr "Ngumbi"
#. name for nuj
msgid "Nyole"
msgstr ""
msgstr "Nyole"
#. name for nuk
msgid "Nuu-chah-nulth"
@ -19220,11 +19220,11 @@ msgstr ""
#. name for nul
msgid "Nusa Laut"
msgstr ""
msgstr "Nusa Laut"
#. name for num
msgid "Niuafo'ou"
msgstr ""
msgstr "Niuafo'ou"
#. name for nun
msgid "Anong"
@ -19232,31 +19232,31 @@ msgstr ""
#. name for nuo
msgid "Nguôn"
msgstr ""
msgstr "Nguon"
#. name for nup
msgid "Nupe-Nupe-Tako"
msgstr ""
msgstr "Nupe"
#. name for nuq
msgid "Nukumanu"
msgstr ""
msgstr "Nukumanu"
#. name for nur
msgid "Nukuria"
msgstr ""
msgstr "Nuguria"
#. name for nus
msgid "Nuer"
msgstr ""
msgstr "Nuer"
#. name for nut
msgid "Nung (Viet Nam)"
msgstr ""
msgstr "Nung (VietNam)"
#. name for nuu
msgid "Ngbundu"
msgstr ""
msgstr "Ngbundu"
#. name for nuv
msgid "Nuni; Northern"
@ -19264,7 +19264,7 @@ msgstr "Nuni; Septentrional"
#. name for nuw
msgid "Nguluwan"
msgstr ""
msgstr "Nguluwà"
#. name for nux
msgid "Mehek"
@ -19272,7 +19272,7 @@ msgstr ""
#. name for nuy
msgid "Nunggubuyu"
msgstr ""
msgstr "Nunggubuyu"
#. name for nuz
msgid "Nahuatl; Tlamacazapa"
@ -19280,27 +19280,27 @@ msgstr "Nàhuatl; Tlamacazapa"
#. name for nvh
msgid "Nasarian"
msgstr ""
msgstr "Nasarià"
#. name for nvm
msgid "Namiae"
msgstr ""
msgstr "Namiae"
#. name for nwa
msgid "Nawathinehena"
msgstr ""
msgstr "Nawathinahana"
#. name for nwb
msgid "Nyabwa"
msgstr ""
msgstr "Nyabwa-Nyédébwa"
#. name for nwc
msgid "Newari; Old"
msgstr ""
msgstr "Newar; Antic"
#. name for nwe
msgid "Ngwe"
msgstr ""
msgstr "Ngwe"
#. name for nwi
msgid "Tanna; Southwest"
@ -19308,23 +19308,23 @@ msgstr ""
#. name for nwm
msgid "Nyamusa-Molo"
msgstr ""
msgstr "Nyamusa-Molo"
#. name for nwr
msgid "Nawaru"
msgstr ""
msgstr "Nawaru"
#. name for nwx
msgid "Newar; Middle"
msgstr ""
msgstr "Newar; Mitjà"
#. name for nwy
msgid "Nottoway-Meherrin"
msgstr ""
msgstr "Nottoway"
#. name for nxa
msgid "Nauete"
msgstr ""
msgstr "Naueti"
#. name for nxd
msgid "Ngando (Democratic Republic of Congo)"
@ -19332,7 +19332,7 @@ msgstr "Ngando (República Democràtica del Congo)"
#. name for nxe
msgid "Nage"
msgstr ""
msgstr "Nage"
#. name for nxg
msgid "Ngad'a"
@ -19340,7 +19340,7 @@ msgstr "Ngada; Central"
#. name for nxi
msgid "Nindi"
msgstr ""
msgstr "Nindi"
#. name for nxl
msgid "Nuaulu; South"
@ -19348,39 +19348,39 @@ msgstr "Nuaulu; Meridional"
#. name for nxm
msgid "Numidian"
msgstr ""
msgstr "Líbic"
#. name for nxn
msgid "Ngawun"
msgstr ""
msgstr "Ngawun"
#. name for nxq
msgid "Naxi"
msgstr ""
msgstr "Naxi"
#. name for nxr
msgid "Ninggerum"
msgstr ""
msgstr "Ninggirum"
#. name for nxu
msgid "Narau"
msgstr ""
msgstr "Narau"
#. name for nxx
msgid "Nafri"
msgstr ""
msgstr "Nafri"
#. name for nya
msgid "Nyanja"
msgstr ""
msgstr "Nyanja"
#. name for nyb
msgid "Nyangbo"
msgstr ""
msgstr "Nyangbo"
#. name for nyc
msgid "Nyanga-li"
msgstr ""
msgstr "Nyanga-li"
#. name for nyd
msgid "Nyore"
@ -19388,7 +19388,7 @@ msgstr ""
#. name for nye
msgid "Nyengo"
msgstr ""
msgstr "Nyengo"
#. name for nyf
msgid "Giryama"
@ -19396,11 +19396,11 @@ msgstr ""
#. name for nyg
msgid "Nyindu"
msgstr ""
msgstr "Nyindu"
#. name for nyh
msgid "Nyigina"
msgstr ""
msgstr "Nyigina"
#. name for nyi
msgid "Ama (Sudan)"
@ -19408,35 +19408,35 @@ msgstr ""
#. name for nyj
msgid "Nyanga"
msgstr ""
msgstr "Nyanga"
#. name for nyk
msgid "Nyaneka"
msgstr ""
msgstr "Nyaneka"
#. name for nyl
msgid "Nyeu"
msgstr ""
msgstr "Nyeu"
#. name for nym
msgid "Nyamwezi"
msgstr ""
msgstr "Nyamwesi"
#. name for nyn
msgid "Nyankole"
msgstr ""
msgstr "Nyankore"
#. name for nyo
msgid "Nyoro"
msgstr ""
msgstr "Nyoro"
#. name for nyp
msgid "Nyang'i"
msgstr ""
msgstr "Nyangi"
#. name for nyq
msgid "Nayini"
msgstr ""
msgstr "Nayini"
#. name for nyr
msgid "Nyiha (Malawi)"
@ -19444,31 +19444,31 @@ msgstr ""
#. name for nys
msgid "Nyunga"
msgstr ""
msgstr "Nyunga"
#. name for nyt
msgid "Nyawaygi"
msgstr ""
msgstr "Nyawaygi"
#. name for nyu
msgid "Nyungwe"
msgstr ""
msgstr "Nyungwe"
#. name for nyv
msgid "Nyulnyul"
msgstr ""
msgstr "Nyulnyui"
#. name for nyw
msgid "Nyaw"
msgstr ""
msgstr "Nyaw"
#. name for nyx
msgid "Nganyaywana"
msgstr ""
msgstr "Nganyaywana"
#. name for nyy
msgid "Nyakyusa-Ngonde"
msgstr ""
msgstr "Nyakyusa-Ngonde"
#. name for nza
msgid "Mbembe; Tigon"
@ -19476,15 +19476,15 @@ msgstr "Mbembe Tigon"
#. name for nzb
msgid "Njebi"
msgstr ""
msgstr "Njebi"
#. name for nzi
msgid "Nzima"
msgstr ""
msgstr "Nzema"
#. name for nzk
msgid "Nzakara"
msgstr ""
msgstr "Nzakara"
#. name for nzm
msgid "Naga; Zeme"
@ -19500,7 +19500,7 @@ msgstr "Teke; Nzikou"
#. name for nzy
msgid "Nzakambay"
msgstr ""
msgstr "Nzakambay"
#. name for nzz
msgid "Dogon; Nanga Dama"
@ -19508,11 +19508,11 @@ msgstr "Dogon; Nanga Dama"
#. name for oaa
msgid "Orok"
msgstr ""
msgstr "Orok"
#. name for oac
msgid "Oroch"
msgstr ""
msgstr "Orotx"
#. name for oar
msgid "Aramaic; Old (up to 700 BCE)"
@ -29600,7 +29600,7 @@ msgstr ""
#. name for yiv
msgid "Nisu; Northern"
msgstr ""
msgstr "Yi; Eshan-Xinping"
#. name for yix
msgid "Yi; Axi"

View File

@ -9,14 +9,14 @@ msgstr ""
"Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
"PO-Revision-Date: 2012-08-15 10:30+0000\n"
"Last-Translator: Jellby <Unknown>\n"
"PO-Revision-Date: 2012-12-24 08:05+0000\n"
"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
"Language-Team: Español; Castellano <>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-16 04:40+0000\n"
"X-Generator: Launchpad (build 15810)\n"
"X-Launchpad-Export-Date: 2012-12-25 04:46+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. name for aaa
msgid "Ghotuo"
@ -9584,27 +9584,27 @@ msgstr "Holikachuk"
#. name for hoj
msgid "Hadothi"
msgstr ""
msgstr "Hadothi"
#. name for hol
msgid "Holu"
msgstr ""
msgstr "Holu"
#. name for hom
msgid "Homa"
msgstr ""
msgstr "Homa"
#. name for hoo
msgid "Holoholo"
msgstr ""
msgstr "Holoholo"
#. name for hop
msgid "Hopi"
msgstr ""
msgstr "Hopi"
#. name for hor
msgid "Horo"
msgstr ""
msgstr "Horo"
#. name for hos
msgid "Ho Chi Minh City Sign Language"
@ -9612,27 +9612,27 @@ msgstr "Lengua de signos de Ho Chi Minh"
#. name for hot
msgid "Hote"
msgstr ""
msgstr "Hote"
#. name for hov
msgid "Hovongan"
msgstr ""
msgstr "Hovongan"
#. name for how
msgid "Honi"
msgstr ""
msgstr "Honi"
#. name for hoy
msgid "Holiya"
msgstr ""
msgstr "Holiya"
#. name for hoz
msgid "Hozo"
msgstr ""
msgstr "Hozo"
#. name for hpo
msgid "Hpon"
msgstr ""
msgstr "Hpon"
#. name for hps
msgid "Hawai'i Pidgin Sign Language"
@ -9640,15 +9640,15 @@ msgstr "Lengua de signos pidyin hawaiana"
#. name for hra
msgid "Hrangkhol"
msgstr ""
msgstr "Hrangkhol"
#. name for hre
msgid "Hre"
msgstr ""
msgstr "Hre"
#. name for hrk
msgid "Haruku"
msgstr ""
msgstr "Haruku"
#. name for hrm
msgid "Miao; Horned"
@ -9656,19 +9656,19 @@ msgstr ""
#. name for hro
msgid "Haroi"
msgstr ""
msgstr "Haroi"
#. name for hrr
msgid "Horuru"
msgstr ""
msgstr "Horuru"
#. name for hrt
msgid "Hértevin"
msgstr ""
msgstr "Hértevin"
#. name for hru
msgid "Hruso"
msgstr ""
msgstr "Hruso"
#. name for hrv
msgid "Croatian"

View File

@ -9,14 +9,14 @@ msgstr ""
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
"devel@lists.alioth.debian.org>\n"
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
"PO-Revision-Date: 2012-12-14 00:48+0000\n"
"PO-Revision-Date: 2012-12-21 03:31+0000\n"
"Last-Translator: Fábio Malcher Miranda <mirand863@hotmail.com>\n"
"Language-Team: Brazilian Portuguese\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-15 05:02+0000\n"
"X-Generator: Launchpad (build 16372)\n"
"X-Launchpad-Export-Date: 2012-12-22 04:59+0000\n"
"X-Generator: Launchpad (build 16378)\n"
"Language: \n"
#. name for aaa
@ -1189,7 +1189,7 @@ msgstr ""
#. name for anz
msgid "Anem"
msgstr ""
msgstr "Anem"
#. name for aoa
msgid "Angolar"
@ -1197,27 +1197,27 @@ msgstr ""
#. name for aob
msgid "Abom"
msgstr ""
msgstr "Abom"
#. name for aoc
msgid "Pemon"
msgstr ""
msgstr "Pemon"
#. name for aod
msgid "Andarum"
msgstr ""
msgstr "Andarum"
#. name for aoe
msgid "Angal Enen"
msgstr ""
msgstr "Angal Enen"
#. name for aof
msgid "Bragat"
msgstr ""
msgstr "Bragat"
#. name for aog
msgid "Angoram"
msgstr ""
msgstr "Angoram"
#. name for aoh
msgid "Arma"

View File

@ -4,7 +4,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = u'calibre'
numeric_version = (0, 9, 11)
numeric_version = (0, 9, 12)
__version__ = u'.'.join(map(unicode, numeric_version))
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
@ -100,6 +100,7 @@ class Plugins(collections.Mapping):
'freetype',
'woff',
'unrar',
'qt_hack',
]
if iswindows:
plugins.extend(['winutil', 'wpd', 'winfonts'])

View File

@ -661,7 +661,7 @@ from calibre.devices.nuut2.driver import NUUT2
from calibre.devices.iriver.driver import IRIVER_STORY
from calibre.devices.binatone.driver import README
from calibre.devices.hanvon.driver import (N516, EB511, ALEX, AZBOOKA, THEBOOK,
LIBREAIR, ODYSSEY)
LIBREAIR, ODYSSEY, KIBANO)
from calibre.devices.edge.driver import EDGE
from calibre.devices.teclast.driver import (TECLAST_K3, NEWSMY, IPAPYRUS,
SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O, STASH, WEXLER)
@ -712,7 +712,7 @@ plugins += [
BOOQ,
EB600,
README,
N516,
N516, KIBANO,
THEBOOK, LIBREAIR,
EB511,
ELONEX,

View File

@ -41,6 +41,20 @@ class N516(USBMS):
def can_handle(self, device_info, debug=False):
return not is_alex(device_info)
class KIBANO(N516):
name = 'Kibano driver'
gui_name = 'Kibano'
description = _('Communicate with the Kibano eBook reader.')
FORMATS = ['epub', 'pdf', 'txt']
BCD = [0x323]
VENDOR_NAME = 'EBOOK'
# We use EXTERNAL_SD_CARD for main mem as some devices have not working
# main memories
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['INTERNAL_SD_CARD',
'EXTERNAL_SD_CARD']
class THEBOOK(N516):
name = 'The Book driver'
gui_name = 'The Book'

View File

@ -13,6 +13,7 @@ const calibre_device_entry_t calibre_mtp_device_table[] = {
// Amazon Kindle Fire HD
, { "Amazon", 0x1949, "Fire HD", 0x0007, DEVICE_FLAGS_ANDROID_BUGS}
, { "Amazon", 0x1949, "Fire HD", 0x0008, DEVICE_FLAGS_ANDROID_BUGS}
, { "Amazon", 0x1949, "Fire HD", 0x000a, DEVICE_FLAGS_ANDROID_BUGS}
// Nexus 10

View File

@ -8,7 +8,9 @@
#define UNICODE
#include <Python.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <libmtp.h>
@ -728,7 +730,20 @@ initlibmtp(void) {
if (MTPError == NULL) return;
PyModule_AddObject(m, "MTPError", MTPError);
// Redirect stdout to get rid of the annoying message about mtpz. Really,
// who designs a library without anyway to control/redirect the debugging
// output, and hardcoded paths that cannot be changed?
int bak, new;
fflush(stdout);
bak = dup(STDOUT_FILENO);
new = open("/dev/null", O_WRONLY);
dup2(new, STDOUT_FILENO);
close(new);
LIBMTP_Init();
fflush(stdout);
dup2(bak, STDOUT_FILENO);
close(bak);
LIBMTP_Set_Debug(LIBMTP_DEBUG_NONE);
Py_INCREF(&DeviceType);

View File

@ -20,9 +20,9 @@ class TECLAST_K3(USBMS):
BCD = [0x0000, 0x0100]
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B', 'BEBOOK',
'RK2728']
'RK2728', 'MR700']
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['DIGITAL_PLAYER', 'TL-K5',
'EREADER', 'USB-MSC', 'PER3274B', 'BEBOOK']
'EREADER', 'USB-MSC', 'PER3274B', 'BEBOOK', 'USER']
MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card'

View File

@ -18,8 +18,8 @@ from calibre.ptempfile import TemporaryDirectory
UNITS = ['millimeter', 'centimeter', 'point', 'inch' , 'pica' , 'didot',
'cicero', 'devicepixel']
PAPER_SIZES = ['b2', 'b4', 'b5', 'b6', 'b0', 'b1', 'letter', 'b3', 'a3', 'a1',
'a0', 'legal', 'a6', 'a2', 'a5', 'a4']
PAPER_SIZES = [u'a0', u'a1', u'a2', u'a3', u'a4', u'a5', u'a6', u'b0', u'b1',
u'b2', u'b3', u'b4', u'b5', u'b6', u'legal', u'letter']
class PDFMetadata(object): # {{{
def __init__(self, oeb_metadata=None):
@ -91,11 +91,12 @@ class PDFOutput(OutputFormatPlugin):
OptionRecommendation(name='pdf_mono_font_size',
recommended_value=16, help=_(
'The default font size for monospaced text')),
OptionRecommendation(name='uncompressed_pdf',
recommended_value=False, help=_(
'Generate an uncompressed PDF (useful for debugging)')),
OptionRecommendation(name='old_pdf_engine', recommended_value=False,
help=_('Use the old, less capable engine to generate the PDF')),
# OptionRecommendation(name='old_pdf_engine', recommended_value=False,
# help=_('Use the old, less capable engine to generate the PDF')),
# OptionRecommendation(name='uncompressed_pdf',
# recommended_value=False, help=_(
# 'Generate an uncompressed PDF, useful for debugging, '
# 'only works with the new PDF engine.')),
])
def convert(self, oeb_book, output_path, input_plugin, opts, log):
@ -189,11 +190,12 @@ class PDFOutput(OutputFormatPlugin):
val[i].value = family_map[k]
def convert_text(self, oeb_book):
if self.opts.old_pdf_engine:
from calibre.ebooks.pdf.writer import PDFWriter
from calibre.utils.config import tweaks
if tweaks.get('new_pdf_engine', False):
from calibre.ebooks.pdf.render.from_html import PDFWriter
PDFWriter
else:
from calibre.ebooks.pdf.render.from_html import PDFWriter
from calibre.ebooks.pdf.writer import PDFWriter
from calibre.ebooks.metadata.opf2 import OPF
self.log.debug('Serializing oeb input to disk for processing...')

View File

@ -75,6 +75,20 @@ class Worker(Thread): # Get details {{{
9: ['sept'],
12: ['déc'],
},
'br': {
1: ['janeiro'],
2: ['fevereiro'],
3: ['março'],
4: ['abril'],
5: ['maio'],
6: ['junho'],
7: ['julho'],
8: ['agosto'],
9: ['setembro'],
10: ['outubro'],
11: ['novembro'],
12: ['dezembro'],
},
'es': {
1: ['enero'],
2: ['febrero'],
@ -89,7 +103,7 @@ class Worker(Thread): # Get details {{{
11: ['noviembre'],
12: ['diciembre'],
},
'jp': {
'jp': {
1: [u'1月'],
2: [u'2月'],
3: [u'3月'],
@ -117,6 +131,7 @@ class Worker(Thread): # Get details {{{
text()="Product details" or \
text()="Détails sur le produit" or \
text()="Detalles del producto" or \
text()="Detalhes do produto" or \
text()="登録情報"]/../div[@class="content"]
'''
# Editor: is for Spanish
@ -126,6 +141,7 @@ class Worker(Thread): # Get details {{{
starts-with(text(), "Editore:") or \
starts-with(text(), "Editeur") or \
starts-with(text(), "Editor:") or \
starts-with(text(), "Editora:") or \
starts-with(text(), "出版社:")]
'''
self.language_xpath = '''
@ -141,7 +157,7 @@ class Worker(Thread): # Get details {{{
'''
self.ratings_pat = re.compile(
r'([0-9.]+) ?(out of|von|su|étoiles sur|つ星のうち|de un máximo de) ([\d\.]+)( (stars|Sternen|stelle|estrellas)){0,1}')
r'([0-9.]+) ?(out of|von|su|étoiles sur|つ星のうち|de un máximo de|de) ([\d\.]+)( (stars|Sternen|stelle|estrellas|estrelas)){0,1}')
lm = {
'eng': ('English', 'Englisch'),
@ -150,6 +166,7 @@ class Worker(Thread): # Get details {{{
'deu': ('German', 'Deutsch'),
'spa': ('Spanish', 'Espa\xf1ol', 'Espaniol'),
'jpn': ('Japanese', u'日本語'),
'por': ('Portuguese', 'Português'),
}
self.lang_map = {}
for code, names in lm.iteritems():
@ -505,6 +522,7 @@ class Amazon(Source):
'it' : _('Italy'),
'jp' : _('Japan'),
'es' : _('Spain'),
'br' : _('Brazil'),
}
options = (
@ -570,6 +588,8 @@ class Amazon(Source):
url = 'http://amzn.com/'+asin
elif domain == 'uk':
url = 'http://www.amazon.co.uk/dp/'+asin
elif domain == 'br':
url = 'http://www.amazon.com.br/dp/'+asin
else:
url = 'http://www.amazon.%s/dp/%s'%(domain, asin)
if url:
@ -629,7 +649,7 @@ class Amazon(Source):
q['field-isbn'] = isbn
else:
# Only return book results
q['search-alias'] = 'stripbooks'
q['search-alias'] = 'digital-text' if domain == 'br' else 'stripbooks'
if title:
title_tokens = list(self.get_title_tokens(title))
if title_tokens:
@ -661,6 +681,8 @@ class Amazon(Source):
udomain = 'co.uk'
elif domain == 'jp':
udomain = 'co.jp'
elif domain == 'br':
udomain = 'com.br'
url = 'http://www.amazon.%s/s/?'%udomain + urlencode(encoded_q)
return url, domain
@ -978,6 +1000,16 @@ if __name__ == '__main__': # tests {{{
),
] # }}}
br_tests = [ # {{{
(
{'title':'Guerra dos Tronos'},
[title_test('A Guerra dos Tronos - As Crônicas de Gelo e Fogo',
exact=True), authors_test(['George R. R. Martin'])
]
),
] # }}}
def do_test(domain, start=0, stop=None):
tests = globals().get(domain+'_tests')
if stop is None:
@ -988,7 +1020,7 @@ if __name__ == '__main__': # tests {{{
do_test('com')
#do_test('de')
# do_test('de')
# }}}

View File

@ -92,6 +92,31 @@ class BookIndexing
this.last_check = [body.scrollWidth, body.scrollHeight]
return ans
all_links_and_anchors: () ->
body = document.body
links = []
anchors = {}
for a in document.querySelectorAll("body a[href], body [id], body a[name]")
if window.paged_display?.in_paged_mode
geom = window.paged_display.column_location(a)
else
br = a.getBoundingClientRect()
[left, top] = viewport_to_document(br.left, br.top, a.ownerDocument)
geom = {'left':left, 'top':top, 'width':br.right-br.left, 'height':br.bottom-br.top}
href = a.getAttribute('href')
if href
links.push([href, geom])
id = a.getAttribute("id")
if id and id not in anchors
anchors[id] = geom
if a.tagName in ['A', "a"]
name = a.getAttribute("name")
if name and name not in anchors
anchors[name] = geom
return {'links':links, 'anchors':anchors}
if window?
window.book_indexing = new BookIndexing()

View File

@ -242,6 +242,18 @@ class PagedDisplay
# Return the number of the column that contains xpos
return Math.floor(xpos/this.page_width)
column_location: (elem) ->
# Return the location of elem relative to its containing column
br = elem.getBoundingClientRect()
[left, top] = calibre_utils.viewport_to_document(br.left, br.top, elem.ownerDocument)
c = this.column_at(left)
width = Math.min(br.right, (c+1)*this.page_width) - br.left
if br.bottom < br.top
br.bottom = window.innerHeight
height = Math.min(br.bottom, window.innerHeight) - br.top
left -= c*this.page_width
return {'column':c, 'left':left, 'top':top, 'width':width, 'height':height}
column_boundaries: () ->
# Return the column numbers at the left edge and after the right edge
# of the viewport

View File

@ -7,15 +7,17 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, traceback, unicodedata
import sys, traceback
from math import sqrt
from collections import namedtuple
from functools import wraps
from functools import wraps, partial
import sip
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
QTransform, QPainterPath, QTextOption, QTextLayout,
QImage, QByteArray, QBuffer, qRgba)
QTransform, QPainterPath, QImage, QByteArray, QBuffer,
qRgba)
from calibre.constants import plugins
from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path)
from calibre.ebooks.pdf.render.common import inch, A4
from calibre.utils.fonts.sfnt.container import Sfnt
@ -215,14 +217,15 @@ class PdfEngine(QPaintEngine):
self.graphics_state = GraphicsState()
self.errors_occurred = False
self.errors, self.debug = errors, debug
self.text_option = QTextOption()
self.text_option.setWrapMode(QTextOption.NoWrap)
self.fonts = {}
i = QImage(1, 1, QImage.Format_ARGB32)
i.fill(qRgba(0, 0, 0, 255))
self.alpha_bit = i.constBits().asstring(4).find(b'\xff')
self.current_page_num = 1
self.current_page_inited = False
self.qt_hack, err = plugins['qt_hack']
if err:
raise RuntimeError('Failed to load qt_hack with err: %s'%err)
def init_page(self):
self.pdf.transform(self.pdf_system)
@ -421,98 +424,48 @@ class PdfEngine(QPaintEngine):
self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(),
stroke=self.do_stroke, fill=self.do_fill)
def get_text_layout(self, text_item, text):
tl = QTextLayout(text, text_item.font(), self.paintDevice())
self.text_option.setTextDirection(Qt.RightToLeft if
text_item.renderFlags() & text_item.RightToLeft else Qt.LeftToRight)
tl.setTextOption(self.text_option)
return tl
def update_glyph_map(self, text, indices, text_item, glyph_map):
'''
Map glyphs back to the unicode text they represent.
'''
pos = 0
tl = self.get_text_layout(text_item, '')
indices = list(indices)
def get_glyphs(string):
tl.setText(string)
tl.beginLayout()
line = tl.createLine()
if not line.isValid():
tl.endLayout()
return []
line.setLineWidth(int(1e12))
tl.endLayout()
ans = []
for run in tl.glyphRuns():
ans.extend(run.glyphIndexes())
return ans
ipos = 0
while ipos < len(indices):
if indices[ipos] in glyph_map:
t = glyph_map[indices[ipos]]
if t == text[pos:pos+len(t)]:
pos += len(t)
ipos += 1
continue
found = False
for l in xrange(1, 10):
string = text[pos:pos+l]
g = get_glyphs(string)
if g and g[0] == indices[ipos]:
found = True
glyph_map[g[0]] = string
break
if not found:
self.debug(
'Failed to find glyph->unicode mapping for text: %s'%text)
break
ipos += 1
pos += l
return text[pos:]
def create_sfnt(self, text_item):
get_table = partial(self.qt_hack.get_sfnt_table, text_item)
ans = Font(Sfnt(get_table))
glyph_map = self.qt_hack.get_glyph_map(text_item)
gm = {}
for uc, glyph_id in enumerate(glyph_map):
if glyph_id not in gm:
gm[glyph_id] = unichr(uc)
ans.full_glyph_map = gm
return ans
@store_error
def drawTextItem(self, point, text_item):
# super(PdfEngine, self).drawTextItem(point, text_item)
self.graphics_state(self)
text = type(u'')(text_item.text()).replace('\n', ' ')
text = unicodedata.normalize('NFKC', text)
tl = self.get_text_layout(text_item, text)
tl.setPosition(point)
tl.beginLayout()
line = tl.createLine()
if not line.isValid():
tl.endLayout()
gi = self.qt_hack.get_glyphs(point, text_item)
if not gi.indices:
sip.delete(gi)
return
line.setLineWidth(int(1e12))
tl.endLayout()
for run in tl.glyphRuns():
rf = run.rawFont()
name = hash(bytes(rf.fontTable('name')))
if name not in self.fonts:
self.fonts[name] = Font(Sfnt(rf))
metrics = self.fonts[name]
indices = run.glyphIndexes()
text = self.update_glyph_map(text, indices, text_item, metrics.glyph_map)
glyphs = []
pdf_pos = point
first_baseline = None
for i, pos in enumerate(run.positions()):
if first_baseline is None:
first_baseline = pos.y()
glyph_pos = point + pos
delta = glyph_pos - pdf_pos
glyphs.append((delta.x(), pos.y()-first_baseline, indices[i]))
pdf_pos = glyph_pos
self.pdf.draw_glyph_run([1, 0, 0, -1, point.x(),
point.y()], rf.pixelSize(), metrics, glyphs)
name = hash(bytes(gi.name))
if name not in self.fonts:
self.fonts[name] = self.create_sfnt(text_item)
metrics = self.fonts[name]
for glyph_id in gi.indices:
try:
metrics.glyph_map[glyph_id] = metrics.full_glyph_map[glyph_id]
except (KeyError, ValueError):
pass
glyphs = []
pdf_pos = point
first_baseline = None
for i, pos in enumerate(gi.positions):
if first_baseline is None:
first_baseline = pos.y()
glyph_pos = pos
delta = glyph_pos - pdf_pos
glyphs.append((delta.x(), pos.y()-first_baseline, gi.indices[i]))
pdf_pos = glyph_pos
self.pdf.draw_glyph_run([1, 0, 0, -1, point.x(),
point.y()], gi.size, metrics, glyphs)
sip.delete(gi)
@store_error
def drawPolygon(self, points, mode):
@ -645,12 +598,12 @@ if __name__ == '__main__':
# f.setUnderline(True)
# f.setOverline(True)
# f.setStrikeOut(True)
f.setFamily('DejaVu Sans')
f.setFamily('Calibri')
p.setFont(f)
# p.setPen(QColor(0, 0, 255))
# p.scale(2, 2)
# p.rotate(45)
p.drawText(QPoint(0, 300), 'Some—text not Bys ū --- Д AV ff ff')
p.drawText(QPoint(300, 300), 'Some—text not Bys ū --- Д AV ff ff')
finally:
p.end()
if dev.engine.errors_occurred:

View File

@ -17,14 +17,15 @@ from PyQt4.QtWebKit import QWebView, QWebPage, QWebSettings
from calibre import fit_image
from calibre.ebooks.oeb.display.webview import load_html
from calibre.ebooks.pdf.render.engine import PdfDevice
from calibre.ebooks.pdf.render.common import (inch, cm, mm, pica, cicero,
didot, PAPER_SIZES)
from calibre.ebooks.pdf.outline_writer import Outline
from calibre.ebooks.pdf.render.engine import PdfDevice
from calibre.ebooks.pdf.render.links import Links
def get_page_size(opts, for_comic=False): # {{{
use_profile = not (opts.override_profile_size or
opts.output_profile.short_name == 'default')
opts.output_profile.short_name == 'default' or
opts.output_profile.width > 9999)
if use_profile:
w = (opts.output_profile.comic_screen_size[0] if for_comic else
opts.output_profile.width)
@ -142,10 +143,10 @@ class PDFWriter(QObject):
self.view.page().mainFrame().setScrollBarPolicy(x,
Qt.ScrollBarAlwaysOff)
self.report_progress = lambda x, y: x
self.links = Links()
def dump(self, items, out_stream, pdf_metadata):
opts = self.opts
self.outline = Outline(self.toc, items)
page_size = get_page_size(self.opts)
xdpi, ydpi = self.view.logicalDpiX(), self.view.logicalDpiY()
ml, mr = opts.margin_left, opts.margin_right
@ -161,7 +162,6 @@ class PDFWriter(QObject):
self.render_queue = items
self.total_items = len(items)
# TODO: Test margins
mt, mb = map(self.doc.to_px, (opts.margin_top, opts.margin_bottom))
ms = self.doc.to_px(margin_side, vertical=False)
self.margin_top, self.margin_size, self.margin_bottom = map(
@ -255,11 +255,16 @@ class PDFWriter(QObject):
paged_display.set_geometry(1, %d, %d, %d);
paged_display.layout();
paged_display.fit_images();
py_bridge.value = book_indexing.all_links_and_anchors();
'''%(self.margin_top, self.margin_size, self.margin_bottom))
amap = self.bridge_value
if not isinstance(amap, dict):
amap = {'links':[], 'anchors':{}} # Some javascript error occurred
self.links.add(self.current_item, self.current_page_num, amap['links'],
amap['anchors'])
mf = self.view.page().mainFrame()
start_page = self.current_page_num
dx = 0
while True:
self.doc.init_page()
self.painter.save()
@ -269,18 +274,7 @@ class PDFWriter(QObject):
self.doc.end_page()
if not nsl[1] or nsl[0] <= 0:
break
dx = nsl[0]
evaljs('window.scrollTo(%d, 0)'%dx)
evaljs('window.scrollTo(%d, 0)'%nsl[0])
if self.doc.errors_occurred:
break
self.bridge_value = tuple(self.outline.anchor_map[self.current_item])
evaljs('py_bridge.value = book_indexing.anchor_positions(py_bridge.value)')
amap = self.bridge_value
if not isinstance(amap, dict):
amap = {} # Some javascript error occurred
self.outline.set_pos(self.current_item, None, start_page, 0)
for anchor, x in amap.iteritems():
pagenum, ypos = x
self.outline.set_pos(self.current_item, anchor, start_page + pagenum, ypos)

View File

@ -0,0 +1,32 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
from calibre.ebooks.pdf.render.common import Array, Name
class Destination(Array):
def __init__(self, start_page, pos):
super(Destination, self).__init__(
[start_page + pos['column'], Name('FitH'), pos['y']])
class Links(object):
def __init__(self):
self.anchors = {}
def add(self, base_path, start_page, links, anchors):
path = os.path.normcase(os.path.abspath(base_path))
self.anchors[path] = a = {}
a[None] = Destination(start_page, {'y':0, 'column':0})
for anchor, pos in anchors.iteritems():
a[anchor] = Destination(start_page, pos)

View File

@ -0,0 +1,66 @@
/*
* qt_hack.cpp
* Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "qt_hack.h"
#include <QtEndian>
#include "private/qtextengine_p.h"
#include "private/qfontengine_p.h"
GlyphInfo* get_glyphs(QPointF &p, const QTextItem &text_item) {
QTextItemInt ti = static_cast<const QTextItemInt &>(text_item);
QFontEngine *fe = ti.fontEngine;
qreal size = ti.fontEngine->fontDef.pixelSize;
#ifdef Q_WS_WIN
if (ti.fontEngine->type() == QFontEngine::Win) {
QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
size = fe->tm.tmHeight;
}
#endif
QVarLengthArray<glyph_t> glyphs;
QVarLengthArray<QFixedPoint> positions;
QTransform m = QTransform::fromTranslate(p.x(), p.y());
fe->getGlyphPositions(ti.glyphs, m, ti.flags, glyphs, positions);
QVector<QPointF> points = QVector<QPointF>(positions.count());
for (int i = 0; i < positions.count(); i++) {
points[i].setX(positions[i].x.toReal());
points[i].setY(positions[i].y.toReal());
}
QVector<unsigned int> indices = QVector<unsigned int>(glyphs.count());
for (int i = 0; i < glyphs.count(); i++)
indices[i] = (unsigned int)glyphs[i];
const quint32 *tag = reinterpret_cast<const quint32 *>("name");
return new GlyphInfo(fe->getSfntTable(qToBigEndian(*tag)), size, points, indices);
}
GlyphInfo::GlyphInfo(const QByteArray& name, qreal size, const QVector<QPointF> &positions, const QVector<unsigned int> &indices) :name(name), positions(positions), size(size), indices(indices) {
}
QByteArray get_sfnt_table(const QTextItem &text_item, const char* tag_name) {
QTextItemInt ti = static_cast<const QTextItemInt &>(text_item);
const quint32 *tag = reinterpret_cast<const quint32 *>(tag_name);
return ti.fontEngine->getSfntTable(qToBigEndian(*tag));
}
QVector<unsigned int>* get_glyph_map(const QTextItem &text_item) {
QTextItemInt ti = static_cast<const QTextItemInt &>(text_item);
QVector<unsigned int> *ans = new QVector<unsigned int>(0x10000);
QGlyphLayoutArray<10> glyphs;
int nglyphs = 10;
for (uint uc = 0; uc < 0x10000; ++uc) {
QChar ch(uc);
ti.fontEngine->stringToCMap(&ch, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly);
(*ans)[uc] = glyphs.glyphs[0];
}
return ans;
}

View File

@ -0,0 +1,34 @@
/*
* qt_hack.h
* Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include <QGlyphRun>
#include <QTextItem>
#include <QPointF>
class GlyphInfo {
public:
QByteArray name;
QVector<QPointF> positions;
qreal size;
QVector<unsigned int> indices;
GlyphInfo(const QByteArray &name, qreal size, const QVector<QPointF> &positions, const QVector<unsigned int> &indices);
private:
GlyphInfo(const GlyphInfo&);
GlyphInfo &operator=(const GlyphInfo&);
};
GlyphInfo* get_glyphs(QPointF &p, const QTextItem &text_item);
QByteArray get_sfnt_table(const QTextItem &text_item, const char* tag_name);
QVector<unsigned int>* get_glyph_map(const QTextItem &text_item);

View File

@ -0,0 +1,28 @@
//Define the SIP wrapper to the qt_hack code
//Author - Kovid Goyal <kovid@kovidgoyal.net>
%Module(name=qt_hack, version=1)
%Import QtCore/QtCoremod.sip
%Import QtGui/QtGuimod.sip
class GlyphInfo {
%TypeHeaderCode
#include <qt_hack.h>
%End
public:
QByteArray name;
qreal size;
QVector<QPointF> &positions;
QVector<unsigned int> indices;
GlyphInfo(const QByteArray &name, qreal size, const QVector<QPointF> &positions, const QVector<unsigned int> &indices);
private:
GlyphInfo(const GlyphInfo& g);
};
GlyphInfo* get_glyphs(QPointF &p, const QTextItem &text_item);
QByteArray get_sfnt_table(const QTextItem &text_item, const char* tag_name);
QVector<unsigned int>* get_glyph_map(const QTextItem &text_item);

View File

@ -133,7 +133,7 @@ class Page(Stream):
self.page_dict['Contents'] = contents
self.add_resources()
ret = objects.add(self.page_dict)
objects.commit(ret, stream)
# objects.commit(ret, stream)
return ret
class Path(object):

View File

@ -50,7 +50,7 @@ def get_pdf_printer(opts, for_comic=False, output_file_name=None): # {{{
printer.setPaperSize(paper_size(opts.paper_size))
else:
if opts.output_profile.short_name == 'default' or \
opts.output_profile.width > 9999:
opts.output_profile.width > 9999 or opts.override_profile_size:
if custom_size is None:
printer.setPaperSize(paper_size(opts.paper_size))
else:

View File

@ -10,12 +10,14 @@ from functools import partial
from threading import Thread
from contextlib import closing
from PyQt4.Qt import QToolButton
from PyQt4.Qt import (QToolButton, QDialog, QGridLayout, QIcon, QLabel,
QCheckBox, QDialogButtonBox)
from calibre.gui2.actions import InterfaceAction
from calibre.gui2 import (error_dialog, Dispatcher, warning_dialog, gprefs,
info_dialog)
info_dialog, choose_dir)
from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.widgets import HistoryLineEdit
from calibre.utils.config import prefs, tweaks
from calibre.utils.date import now
@ -125,6 +127,45 @@ class Worker(Thread): # {{{
# }}}
class ChooseLibrary(QDialog): # {{{
def __init__(self, parent):
super(ChooseLibrary, self).__init__(parent)
d = self
d.l = l = QGridLayout()
d.setLayout(l)
d.setWindowTitle(_('Choose library'))
la = d.la = QLabel(_('Library &path:'))
l.addWidget(la, 0, 0)
le = d.le = HistoryLineEdit(d)
le.initialize('choose_library_for_copy')
l.addWidget(le, 0, 1)
la.setBuddy(le)
b = d.b = QToolButton(d)
b.setIcon(QIcon(I('document_open.png')))
b.setToolTip(_('Browse for library'))
b.clicked.connect(self.browse)
l.addWidget(b, 0, 2)
self.c = c = QCheckBox(_('&Delete after copy'))
l.addWidget(c, 1, 0, 1, 3)
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
l.addWidget(bb, 2, 0, 1, 3)
le.setMinimumWidth(350)
self.resize(self.sizeHint())
def browse(self):
d = choose_dir(self, 'choose_library_for_copy',
_('Choose Library'))
if d:
self.le.setText(d)
@property
def args(self):
return (unicode(self.le.text()), self.c.isChecked())
# }}}
class CopyToLibraryAction(InterfaceAction):
name = 'Copy To Library'
@ -166,8 +207,20 @@ class CopyToLibraryAction(InterfaceAction):
partial(self.copy_to_library, loc, delete_after=True))
self.menu.addSeparator()
self.menu.addAction(_('Choose library by path...'), self.choose_library)
self.qaction.setVisible(bool(locations))
def choose_library(self):
d = ChooseLibrary(self.gui)
if d.exec_() == d.Accepted:
path, delete_after = d.args
db = self.gui.library_view.model().db
current = os.path.normcase(os.path.abspath(db.library_path))
if current == os.path.normcase(os.path.abspath(path)):
return error_dialog(self.gui, _('Cannot copy'),
_('Cannot copy to current library.'), show=True)
self.copy_to_library(path, delete_after)
def copy_to_library(self, loc, delete_after=False):
rows = self.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:

View File

@ -544,7 +544,7 @@ class DocumentView(QWebView): # {{{
self.goto_location_action.setMenu(self.goto_location_menu)
self.grabGesture(Qt.SwipeGesture)
self.restore_fonts_action = QAction(_('Normal font size'), self)
self.restore_fonts_action = QAction(_('Default font size'), self)
self.restore_fonts_action.setCheckable(True)
self.restore_fonts_action.triggered.connect(self.restore_font_size)

View File

@ -697,11 +697,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.view.shrink_fonts()
def magnification_changed(self, val):
tt = _('Make font size %(which)s\nCurrent magnification: %(mag).1f')
tt = _('%(which)s font size\nCurrent magnification: %(mag).1f')
self.action_font_size_larger.setToolTip(
tt %dict(which=_('larger'), mag=val))
tt %dict(which=_('Increase'), mag=val))
self.action_font_size_smaller.setToolTip(
tt %dict(which=_('smaller'), mag=val))
tt %dict(which=_('Decrease'), mag=val))
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)

View File

@ -198,7 +198,7 @@
<normaloff>:/images/font_size_larger.png</normaloff>:/images/font_size_larger.png</iconset>
</property>
<property name="text">
<string>Font size larger</string>
<string>Increase font size</string>
</property>
</action>
<action name="action_font_size_smaller">
@ -207,7 +207,7 @@
<normaloff>:/images/font_size_smaller.png</normaloff>:/images/font_size_smaller.png</iconset>
</property>
<property name="text">
<string>Font size smaller</string>
<string>Decrease font size</string>
</property>
</action>
<action name="action_table_of_contents">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More