mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Merge from trunk
This commit is contained in:
commit
e0efbe0f0f
@ -15,7 +15,8 @@ class BusinessStandard(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
delay = 1
|
delay = 1
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'cp1252'
|
auto_cleanup = True
|
||||||
|
encoding = 'utf-8'
|
||||||
publisher = 'Boston'
|
publisher = 'Boston'
|
||||||
category = 'news, boston, usa, world'
|
category = 'news, boston, usa, world'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
@ -30,23 +31,23 @@ class BusinessStandard(BasicNewsRecipe):
|
|||||||
,'publisher' : publisher
|
,'publisher' : publisher
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [dict(attrs={'id':['INDblogEntry','blogEntry','articleHeader','articleGraphs','galleryShell']})]
|
#keep_only_tags = [dict(attrs={'id':['INDblogEntry','blogEntry','articleHeader','articleGraphs','galleryShell']})]
|
||||||
remove_tags = [
|
#remove_tags = [
|
||||||
dict(name=['object','link','script','iframe'])
|
#dict(name=['object','link','script','iframe'])
|
||||||
,dict(attrs={'id':['blogheadTools','bdc_emailWidget','tools','relatedContent']})
|
#,dict(attrs={'id':['blogheadTools','bdc_emailWidget','tools','relatedContent']})
|
||||||
]
|
#]
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Top Stories' , u'http://feeds.boston.com/boston/topstories' )
|
(u'Top Stories' , u'http://feeds.boston.com/boston/topstories' )
|
||||||
,(u'Patriots news', u'http://feeds.boston.com/boston/sports/football/patriots')
|
,(u'Patriots news', u'http://feeds.boston.com/boston/sports/football/patriots/patriots_rss')
|
||||||
,(u'National news', u'http://feeds.boston.com/boston/news/nation' )
|
,(u'National news', u'http://feeds.boston.com/boston/news/nation' )
|
||||||
,(u'World news' , u'http://feeds.boston.com/boston/news/world' )
|
,(u'World news' , u'http://feeds.boston.com/boston/news/world' )
|
||||||
]
|
]
|
||||||
|
|
||||||
def print_version(self, url):
|
#def print_version(self, url):
|
||||||
return url + '?page=full'
|
#return url + '?page=full'
|
||||||
|
|
||||||
def get_article_url(self, article):
|
#def get_article_url(self, article):
|
||||||
rawarticle = article.get('guid', None)
|
#rawarticle = article.get('guid', None)
|
||||||
return rawarticle.rpartition('?')[0]
|
#return rawarticle.rpartition('?')[0]
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
description = 'News as provided by The Daily Mirror -UK'
|
description = 'News as provided by The Daily Mirror -UK'
|
||||||
|
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
# last updated 8/6/12
|
# last updated 19/10/12
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
#cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
#cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
||||||
|
|
||||||
@ -15,10 +15,12 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
max_articles_per_feed = 12
|
max_articles_per_feed = 1
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
ignore_duplicate_articles = {'title'}
|
||||||
|
|
||||||
# auto_cleanup = True
|
# auto_cleanup = True
|
||||||
#conversion_options = { 'linearize_tables' : True }
|
#conversion_options = { 'linearize_tables' : True }
|
||||||
|
|
||||||
@ -60,11 +62,12 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
|
|
||||||
# example of commented out feed not needed ,(u'Travel','http://www.mirror.co.uk/advice/travel/rss.xml')
|
# example of commented out feed not needed ,(u'Travel','http://www.mirror.co.uk/advice/travel/rss.xml')
|
||||||
]
|
]
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
h1{ font-size:medium;}
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
body{ text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
img { display:block}
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
'''#
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
soup = self.index_to_soup('http://www.politicshome.com/uk/latest_frontpage.html')
|
soup = self.index_to_soup('http://www.politicshome.com/uk/latest_frontpage.html')
|
||||||
@ -75,8 +78,10 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
#cov2 now contains url of the page containing pic
|
#cov2 now contains url of the page containing pic
|
||||||
soup = self.index_to_soup(cov2)
|
soup = self.index_to_soup(cov2)
|
||||||
cov = soup.find(attrs={'id' : 'large'})
|
cov = soup.find(attrs={'id' : 'large'})
|
||||||
cov2 = str(cov)
|
cov=str(cov)
|
||||||
cov2=cov2[27:-18]
|
cov2 = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', cov)
|
||||||
|
cov2 = str(cov2)
|
||||||
|
cov2=cov2[2:len(cov2)-2]
|
||||||
#cov2 now is pic url, now go back to original function
|
#cov2 now is pic url, now go back to original function
|
||||||
br = browser()
|
br = browser()
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import random
|
import re, random
|
||||||
|
|
||||||
from calibre import browser
|
from calibre import browser
|
||||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
@ -8,7 +8,7 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
title = u'The Sun UK'
|
title = u'The Sun UK'
|
||||||
description = 'Articles from The Sun tabloid UK'
|
description = 'Articles from The Sun tabloid UK'
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
# last updated 12/10/12 added starsons remove article code
|
# last updated 19/10/12 better cover fetch
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
max_articles_per_feed = 15
|
max_articles_per_feed = 15
|
||||||
@ -19,7 +19,7 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
|
||||||
ignore_duplicate_articles = {'title'}
|
ignore_duplicate_articles = {'title','url'}
|
||||||
|
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
@ -72,9 +72,10 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
#cov2 now contains url of the page containing pic
|
#cov2 now contains url of the page containing pic
|
||||||
soup = self.index_to_soup(cov2)
|
soup = self.index_to_soup(cov2)
|
||||||
cov = soup.find(attrs={'id' : 'large'})
|
cov = soup.find(attrs={'id' : 'large'})
|
||||||
cov2 = str(cov)
|
cov=str(cov)
|
||||||
cov2=cov2[27:-18]
|
cov2 = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', cov)
|
||||||
#cov2 now is pic url, now go back to original function
|
cov2 = str(cov2)
|
||||||
|
cov2=cov2[2:len(cov2)-2]
|
||||||
br = browser()
|
br = browser()
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
try:
|
try:
|
||||||
|
@ -23,16 +23,15 @@ class Time(BasicNewsRecipe):
|
|||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
{
|
{
|
||||||
'class':['tout1', 'entry-content', 'external-gallery-img', 'image-meta']
|
'class':['primary-col', 'tout1']
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
{'class':['thumbnail', 'button']},
|
{'class':['button', 'entry-sharing group', 'wp-paginate',
|
||||||
|
'moving-markup', 'entry-comments']},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
extra_css = '.entry-date { padding-left: 2ex }'
|
||||||
recursions = 10
|
|
||||||
match_regexps = [r'/[0-9,]+-(2|3|4|5|6|7|8|9)(,\d+){0,1}.html',r'http://www.time.com/time/specials/packages/article/.*']
|
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(
|
preprocess_regexps = [(re.compile(
|
||||||
r'<meta .+/>'), lambda m:'')]
|
r'<meta .+/>'), lambda m:'')]
|
||||||
@ -45,7 +44,7 @@ class Time(BasicNewsRecipe):
|
|||||||
br.select_form(predicate=lambda f: 'action' in f.attrs and f.attrs['action'] == 'https://auth.time.com/login.php')
|
br.select_form(predicate=lambda f: 'action' in f.attrs and f.attrs['action'] == 'https://auth.time.com/login.php')
|
||||||
br['username'] = self.username
|
br['username'] = self.username
|
||||||
br['password'] = self.password
|
br['password'] = self.password
|
||||||
br['magcode'] = ['TD']
|
# br['magcode'] = ['TD']
|
||||||
br.find_control('turl').readonly = False
|
br.find_control('turl').readonly = False
|
||||||
br['turl'] = 'http://www.time.com/time/magazine'
|
br['turl'] = 'http://www.time.com/time/magazine'
|
||||||
br.find_control('rurl').readonly = False
|
br.find_control('rurl').readonly = False
|
||||||
@ -104,7 +103,14 @@ class Time(BasicNewsRecipe):
|
|||||||
method='text').strip()
|
method='text').strip()
|
||||||
if not title: continue
|
if not title: continue
|
||||||
url = a[0].get('href')
|
url = a[0].get('href')
|
||||||
url = re.sub('/magazine/article/0,9171','/subscriber/printout/0,8816', url)
|
if url.startswith('/'):
|
||||||
|
url = 'http://www.time.com'+url
|
||||||
|
if '/article/0,' in url:
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
a = soup.find('a', href=lambda x:x and '/printout/' in x)
|
||||||
|
url = a['href'].replace('/printout', '/subscriber/printout')
|
||||||
|
else:
|
||||||
|
url += 'print/' if url.endswith('/') else '/print/'
|
||||||
if url.startswith('/'):
|
if url.startswith('/'):
|
||||||
url = 'http://www.time.com'+url
|
url = 'http://www.time.com'+url
|
||||||
desc = ''
|
desc = ''
|
||||||
@ -112,10 +118,18 @@ class Time(BasicNewsRecipe):
|
|||||||
if p:
|
if p:
|
||||||
desc = html.tostring(p[0], encoding=unicode,
|
desc = html.tostring(p[0], encoding=unicode,
|
||||||
method='text')
|
method='text')
|
||||||
self.log('\t', title, ':\n\t\t', desc)
|
self.log('\t', title, ':\n\t\t', url)
|
||||||
yield {
|
yield {
|
||||||
'title' : title,
|
'title' : title,
|
||||||
'url' : url,
|
'url' : url,
|
||||||
'date' : '',
|
'date' : '',
|
||||||
'description' : desc
|
'description' : desc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for fig in soup.findAll('figure'):
|
||||||
|
img = fig.find('img')
|
||||||
|
if img is not None:
|
||||||
|
fig.replaceWith(img)
|
||||||
|
return soup
|
||||||
|
|
||||||
|
19
recipes/yazihane.recipe
Normal file
19
recipes/yazihane.recipe
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
import re
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1350731826(BasicNewsRecipe):
|
||||||
|
title = u'Yazihane'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
__author__ = 'A Erdogan'
|
||||||
|
description = 'Sports Blog'
|
||||||
|
publisher = 'yazihaneden.com'
|
||||||
|
category = 'sports, basketball, nba, cycling, euroleague'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
masthead_url = 'http://www.yazihaneden.com/wp-content/uploads/Untitled-1.png'
|
||||||
|
language = 'tr'
|
||||||
|
|
||||||
|
keep_only_tags = [ dict(name='div', attrs={'id':re.compile('(^|| )post-($|| )', re.DOTALL)})]
|
||||||
|
remove_tags_after = dict(name='div', attrs={'class':'post-footer clear'})
|
||||||
|
feeds = [(u'Yazihane', u'http://www.yazihaneden.com/feed/')]
|
@ -787,12 +787,10 @@ application/x-font-framemaker
|
|||||||
application/x-font-ghostscript gsf
|
application/x-font-ghostscript gsf
|
||||||
application/x-font-libgrx
|
application/x-font-libgrx
|
||||||
application/x-font-linux-psf psf
|
application/x-font-linux-psf psf
|
||||||
application/x-font-otf otf
|
|
||||||
application/x-font-pcf pcf
|
application/x-font-pcf pcf
|
||||||
application/x-font-snf snf
|
application/x-font-snf snf
|
||||||
application/x-font-speedo
|
application/x-font-speedo
|
||||||
application/x-font-sunos-news
|
application/x-font-sunos-news
|
||||||
application/x-font-ttf ttc ttf
|
|
||||||
application/x-font-type1 afm pfa pfb pfm
|
application/x-font-type1 afm pfa pfb pfm
|
||||||
application/x-font-vfont
|
application/x-font-vfont
|
||||||
application/x-freemind mm
|
application/x-freemind mm
|
||||||
@ -1368,8 +1366,6 @@ text/fb2+xml fb2
|
|||||||
text/x-sony-bbeb+xml lrs
|
text/x-sony-bbeb+xml lrs
|
||||||
application/x-sony-bbeb lrf lrx
|
application/x-sony-bbeb lrf lrx
|
||||||
application/adobe-page-template+xml xpgt
|
application/adobe-page-template+xml xpgt
|
||||||
application/x-font-opentype otf
|
|
||||||
application/x-font-truetype ttf
|
|
||||||
application/x-mobipocket-ebook mobi prc azw
|
application/x-mobipocket-ebook mobi prc azw
|
||||||
application/x-topaz-ebook tpz azw1
|
application/x-topaz-ebook tpz azw1
|
||||||
application/x-mobipocket-subscription pobi
|
application/x-mobipocket-subscription pobi
|
||||||
@ -1381,3 +1377,7 @@ application/x-cb7 cb7
|
|||||||
application/x-koboreader-ebook kobo
|
application/x-koboreader-ebook kobo
|
||||||
image/wmf wmf
|
image/wmf wmf
|
||||||
application/ereader pdb
|
application/ereader pdb
|
||||||
|
# See http://idpf.org/epub/30/spec/epub30-publications.html#sec-core-media-types
|
||||||
|
application/vnd.ms-opentype otf
|
||||||
|
application/font-woff woff
|
||||||
|
application/x-font-truetype ttf
|
||||||
|
@ -8,6 +8,8 @@ let g:syntastic_cpp_include_dirs = [
|
|||||||
\'/usr/include/qt4/QtCore',
|
\'/usr/include/qt4/QtCore',
|
||||||
\'/usr/include/qt4/QtGui',
|
\'/usr/include/qt4/QtGui',
|
||||||
\'/usr/include/qt4',
|
\'/usr/include/qt4',
|
||||||
|
\'/usr/include/freetype2',
|
||||||
|
\'/usr/include/fontconfig',
|
||||||
\'src/qtcurve/common', 'src/qtcurve',
|
\'src/qtcurve/common', 'src/qtcurve',
|
||||||
\'/usr/include/ImageMagick',
|
\'/usr/include/ImageMagick',
|
||||||
\]
|
\]
|
||||||
|
@ -84,6 +84,7 @@ qt_inc = pyqt.qt_inc_dir
|
|||||||
qt_lib = pyqt.qt_lib_dir
|
qt_lib = pyqt.qt_lib_dir
|
||||||
ft_lib_dirs = []
|
ft_lib_dirs = []
|
||||||
ft_libs = []
|
ft_libs = []
|
||||||
|
ft_inc_dirs = []
|
||||||
jpg_libs = []
|
jpg_libs = []
|
||||||
jpg_lib_dirs = []
|
jpg_lib_dirs = []
|
||||||
fc_inc = '/usr/include/fontconfig'
|
fc_inc = '/usr/include/fontconfig'
|
||||||
@ -94,6 +95,9 @@ chmlib_inc_dirs = chmlib_lib_dirs = []
|
|||||||
sqlite_inc_dirs = []
|
sqlite_inc_dirs = []
|
||||||
icu_inc_dirs = []
|
icu_inc_dirs = []
|
||||||
icu_lib_dirs = []
|
icu_lib_dirs = []
|
||||||
|
zlib_inc_dirs = []
|
||||||
|
zlib_lib_dirs = []
|
||||||
|
zlib_libs = ['z']
|
||||||
|
|
||||||
if iswindows:
|
if iswindows:
|
||||||
prefix = r'C:\cygwin\home\kovid\sw'
|
prefix = r'C:\cygwin\home\kovid\sw'
|
||||||
@ -116,6 +120,10 @@ if iswindows:
|
|||||||
jpg_libs = ['jpeg']
|
jpg_libs = ['jpeg']
|
||||||
ft_lib_dirs = [sw_lib_dir]
|
ft_lib_dirs = [sw_lib_dir]
|
||||||
ft_libs = ['freetype']
|
ft_libs = ['freetype']
|
||||||
|
ft_inc_dirs = [sw_inc_dir]
|
||||||
|
zlib_inc_dirs = [sw_inc_dir]
|
||||||
|
zlib_lib_dirs = [sw_lib_dir]
|
||||||
|
zlib_libs = ['zlib']
|
||||||
|
|
||||||
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.7.6')]
|
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.7.6')]
|
||||||
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
||||||
@ -135,6 +143,8 @@ elif isosx:
|
|||||||
png_inc_dirs = consolidate('PNG_INC_DIR', '/sw/include')
|
png_inc_dirs = consolidate('PNG_INC_DIR', '/sw/include')
|
||||||
png_lib_dirs = consolidate('PNG_LIB_DIR', '/sw/lib')
|
png_lib_dirs = consolidate('PNG_LIB_DIR', '/sw/lib')
|
||||||
png_libs = ['png12']
|
png_libs = ['png12']
|
||||||
|
ft_libs = ['freetype']
|
||||||
|
ft_inc_dirs = ['/sw/include/freetype2']
|
||||||
else:
|
else:
|
||||||
# Include directories
|
# Include directories
|
||||||
png_inc_dirs = pkgconfig_include_dirs('libpng', 'PNG_INC_DIR',
|
png_inc_dirs = pkgconfig_include_dirs('libpng', 'PNG_INC_DIR',
|
||||||
@ -150,6 +160,10 @@ else:
|
|||||||
if not magick_libs:
|
if not magick_libs:
|
||||||
magick_libs = ['MagickWand', 'MagickCore']
|
magick_libs = ['MagickWand', 'MagickCore']
|
||||||
png_libs = ['png']
|
png_libs = ['png']
|
||||||
|
ft_inc_dirs = pkgconfig_include_dirs('freetype2', 'FT_INC_DIR',
|
||||||
|
'/usr/include/freetype2')
|
||||||
|
ft_lib_dirs = pkgconfig_lib_dirs('freetype2', 'FT_LIB_DIR', '/usr/lib')
|
||||||
|
ft_libs = pkgconfig_libs('freetype2', '', '')
|
||||||
|
|
||||||
|
|
||||||
fc_inc = os.environ.get('FC_INC_DIR', fc_inc)
|
fc_inc = os.environ.get('FC_INC_DIR', fc_inc)
|
||||||
|
@ -17,7 +17,8 @@ from setup.build_environment import (fc_inc, fc_lib, chmlib_inc_dirs, fc_error,
|
|||||||
podofo_inc, podofo_lib, podofo_error, pyqt, OSX_SDK, NMAKE, QMAKE,
|
podofo_inc, podofo_lib, podofo_error, pyqt, OSX_SDK, NMAKE, QMAKE,
|
||||||
msvc, MT, win_inc, win_lib, win_ddk, magick_inc_dirs, magick_lib_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,
|
magick_libs, chmlib_lib_dirs, sqlite_inc_dirs, icu_inc_dirs,
|
||||||
icu_lib_dirs, win_ddk_lib_dirs)
|
icu_lib_dirs, win_ddk_lib_dirs, ft_libs, ft_lib_dirs, ft_inc_dirs,
|
||||||
|
zlib_libs, zlib_lib_dirs, zlib_inc_dirs)
|
||||||
MT
|
MT
|
||||||
isunix = islinux or isosx or isbsd
|
isunix = islinux or isosx or isbsd
|
||||||
|
|
||||||
@ -50,10 +51,6 @@ class Extension(object):
|
|||||||
reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp'))
|
reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp'))
|
||||||
reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h'))
|
reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h'))
|
||||||
|
|
||||||
pdfreflow_libs = []
|
|
||||||
if iswindows:
|
|
||||||
pdfreflow_libs = ['advapi32', 'User32', 'Gdi32', 'zlib']
|
|
||||||
|
|
||||||
icu_libs = ['icudata', 'icui18n', 'icuuc', 'icuio']
|
icu_libs = ['icudata', 'icui18n', 'icuuc', 'icuio']
|
||||||
icu_cflags = []
|
icu_cflags = []
|
||||||
if iswindows:
|
if iswindows:
|
||||||
@ -119,6 +116,12 @@ extensions = [
|
|||||||
'calibre/utils/lzx/mspack.h'],
|
'calibre/utils/lzx/mspack.h'],
|
||||||
inc_dirs=['calibre/utils/lzx']),
|
inc_dirs=['calibre/utils/lzx']),
|
||||||
|
|
||||||
|
Extension('freetype',
|
||||||
|
['calibre/utils/fonts/freetype.cpp'],
|
||||||
|
inc_dirs = ft_inc_dirs,
|
||||||
|
libraries=ft_libs,
|
||||||
|
lib_dirs=ft_lib_dirs),
|
||||||
|
|
||||||
Extension('fontconfig',
|
Extension('fontconfig',
|
||||||
['calibre/utils/fonts/fontconfig.c'],
|
['calibre/utils/fonts/fontconfig.c'],
|
||||||
inc_dirs = [fc_inc],
|
inc_dirs = [fc_inc],
|
||||||
@ -126,6 +129,18 @@ extensions = [
|
|||||||
lib_dirs=[fc_lib],
|
lib_dirs=[fc_lib],
|
||||||
error=fc_error),
|
error=fc_error),
|
||||||
|
|
||||||
|
Extension('woff',
|
||||||
|
['calibre/utils/fonts/woff/main.c',
|
||||||
|
'calibre/utils/fonts/woff/woff.c'],
|
||||||
|
headers=[
|
||||||
|
'calibre/utils/fonts/woff/woff.h',
|
||||||
|
'calibre/utils/fonts/woff/woff-private.h'],
|
||||||
|
libraries=zlib_libs,
|
||||||
|
lib_dirs=zlib_lib_dirs,
|
||||||
|
inc_dirs=zlib_inc_dirs,
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
Extension('msdes',
|
Extension('msdes',
|
||||||
['calibre/utils/msdes/msdesmodule.c',
|
['calibre/utils/msdes/msdesmodule.c',
|
||||||
'calibre/utils/msdes/des.c'],
|
'calibre/utils/msdes/des.c'],
|
||||||
|
@ -329,6 +329,7 @@ def get_parsed_proxy(typ='http', debug=True):
|
|||||||
ans['port'] = int(ans['port'])
|
ans['port'] = int(ans['port'])
|
||||||
except:
|
except:
|
||||||
if debug:
|
if debug:
|
||||||
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
else:
|
else:
|
||||||
if debug:
|
if debug:
|
||||||
@ -689,26 +690,18 @@ def remove_bracketed_text(src,
|
|||||||
buf.append(char)
|
buf.append(char)
|
||||||
return u''.join(buf)
|
return u''.join(buf)
|
||||||
|
|
||||||
if isosx:
|
|
||||||
import glob, shutil
|
|
||||||
fdir = os.path.expanduser('~/.fonts')
|
|
||||||
try:
|
|
||||||
if not os.path.exists(fdir):
|
|
||||||
os.makedirs(fdir)
|
|
||||||
if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')):
|
|
||||||
base = P('fonts/liberation/*.ttf')
|
|
||||||
for f in glob.glob(base):
|
|
||||||
shutil.copy2(f, fdir)
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
def load_builtin_fonts():
|
def load_builtin_fonts():
|
||||||
import glob
|
if iswindows or isosx:
|
||||||
from PyQt4.Qt import QFontDatabase
|
import glob
|
||||||
base = P('fonts/liberation/*.ttf')
|
from PyQt4.Qt import QFontDatabase
|
||||||
for f in glob.glob(base):
|
for f in glob.glob(P('fonts/liberation/*.ttf')):
|
||||||
QFontDatabase.addApplicationFont(f)
|
QFontDatabase.addApplicationFont(f)
|
||||||
|
else:
|
||||||
|
# On linux these are loaded by fontconfig which means that
|
||||||
|
# they are available to Qt as well, since Qt uses fontconfig
|
||||||
|
from calibre.utils.fonts import fontconfig
|
||||||
|
fontconfig
|
||||||
|
|
||||||
return 'Liberation Serif', 'Liberation Sans', 'Liberation Mono'
|
return 'Liberation Serif', 'Liberation Sans', 'Liberation Mono'
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,6 +89,8 @@ class Plugins(collections.Mapping):
|
|||||||
'chm_extra',
|
'chm_extra',
|
||||||
'icu',
|
'icu',
|
||||||
'speedup',
|
'speedup',
|
||||||
|
'freetype',
|
||||||
|
'woff',
|
||||||
]
|
]
|
||||||
if iswindows:
|
if iswindows:
|
||||||
plugins.extend(['winutil', 'wpd', 'winfonts'])
|
plugins.extend(['winutil', 'wpd', 'winfonts'])
|
||||||
|
@ -178,18 +178,41 @@ def normalize(x):
|
|||||||
|
|
||||||
def calibre_cover(title, author_string, series_string=None,
|
def calibre_cover(title, author_string, series_string=None,
|
||||||
output_format='jpg', title_size=46, author_size=36, logo_path=None):
|
output_format='jpg', title_size=46, author_size=36, logo_path=None):
|
||||||
|
from calibre.utils.config_base import tweaks
|
||||||
title = normalize(title)
|
title = normalize(title)
|
||||||
author_string = normalize(author_string)
|
author_string = normalize(author_string)
|
||||||
series_string = normalize(series_string)
|
series_string = normalize(series_string)
|
||||||
from calibre.utils.magick.draw import create_cover_page, TextLine
|
from calibre.utils.magick.draw import create_cover_page, TextLine
|
||||||
lines = [TextLine(title, title_size), TextLine(author_string, author_size)]
|
text = title + author_string + (series_string or u'')
|
||||||
|
font_path = tweaks['generate_cover_title_font']
|
||||||
|
if font_path is None:
|
||||||
|
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
||||||
|
|
||||||
|
from calibre.utils.fonts.utils import get_font_for_text
|
||||||
|
font = open(font_path, 'rb').read()
|
||||||
|
c = get_font_for_text(text, font)
|
||||||
|
cleanup = False
|
||||||
|
if c is not None and c != font:
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
pt = PersistentTemporaryFile('.ttf')
|
||||||
|
pt.write(c)
|
||||||
|
pt.close()
|
||||||
|
font_path = pt.name
|
||||||
|
cleanup = True
|
||||||
|
|
||||||
|
lines = [TextLine(title, title_size, font_path=font_path),
|
||||||
|
TextLine(author_string, author_size, font_path=font_path)]
|
||||||
if series_string:
|
if series_string:
|
||||||
lines.append(TextLine(series_string, author_size))
|
lines.append(TextLine(series_string, author_size, font_path=font_path))
|
||||||
if logo_path is None:
|
if logo_path is None:
|
||||||
logo_path = I('library.png')
|
logo_path = I('library.png')
|
||||||
return create_cover_page(lines, logo_path, output_format='jpg',
|
try:
|
||||||
|
return create_cover_page(lines, logo_path, output_format='jpg',
|
||||||
texture_opacity=0.3, texture_data=I('cover_texture.png',
|
texture_opacity=0.3, texture_data=I('cover_texture.png',
|
||||||
data=True))
|
data=True))
|
||||||
|
finally:
|
||||||
|
if cleanup:
|
||||||
|
os.remove(font_path)
|
||||||
|
|
||||||
UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|ex|en|px|mm|cm|in|pt|pc)$')
|
UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|ex|en|px|mm|cm|in|pt|pc)$')
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ def add_pipeline_options(parser, plumber):
|
|||||||
_('Options to control the look and feel of the output'),
|
_('Options to control the look and feel of the output'),
|
||||||
[
|
[
|
||||||
'base_font_size', 'disable_font_rescaling',
|
'base_font_size', 'disable_font_rescaling',
|
||||||
'font_size_mapping',
|
'font_size_mapping', 'embed_font_family',
|
||||||
'line_height', 'minimum_line_height',
|
'line_height', 'minimum_line_height',
|
||||||
'linearize_tables',
|
'linearize_tables',
|
||||||
'extra_css', 'filter_css',
|
'extra_css', 'filter_css',
|
||||||
|
@ -193,6 +193,17 @@ OptionRecommendation(name='line_height',
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
OptionRecommendation(name='embed_font_family',
|
||||||
|
recommended_value=None, level=OptionRecommendation.LOW,
|
||||||
|
help=_(
|
||||||
|
'Embed the specified font family into the book. This specifies '
|
||||||
|
'the "base" font used for the book. If the input document '
|
||||||
|
'specifies its own fonts, they may override this base font. '
|
||||||
|
'You can use the filter style information option to remove fonts from the '
|
||||||
|
'input document. Note that font embedding only works '
|
||||||
|
'with some output formats, principally EPUB and AZW3.')
|
||||||
|
),
|
||||||
|
|
||||||
OptionRecommendation(name='linearize_tables',
|
OptionRecommendation(name='linearize_tables',
|
||||||
recommended_value=False, level=OptionRecommendation.LOW,
|
recommended_value=False, level=OptionRecommendation.LOW,
|
||||||
help=_('Some badly designed documents use tables to control the '
|
help=_('Some badly designed documents use tables to control the '
|
||||||
|
@ -379,6 +379,10 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
|||||||
|
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
stream.truncate()
|
stream.truncate()
|
||||||
|
# Apparently there exists FB2 reading software that chokes on the use of
|
||||||
|
# single quotes in xml declaration. Sigh. See
|
||||||
|
# http://www.mobileread.com/forums/showthread.php?p=2273184#post2273184
|
||||||
|
stream.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||||
stream.write(etree.tostring(root, method='xml', encoding='utf-8',
|
stream.write(etree.tostring(root, method='xml', encoding='utf-8',
|
||||||
xml_declaration=True))
|
xml_declaration=False))
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ OPF_MIME = types_map['.opf']
|
|||||||
PAGE_MAP_MIME = 'application/oebps-page-map+xml'
|
PAGE_MAP_MIME = 'application/oebps-page-map+xml'
|
||||||
OEB_DOC_MIME = 'text/x-oeb1-document'
|
OEB_DOC_MIME = 'text/x-oeb1-document'
|
||||||
OEB_CSS_MIME = 'text/x-oeb1-css'
|
OEB_CSS_MIME = 'text/x-oeb1-css'
|
||||||
OPENTYPE_MIME = 'application/x-font-opentype'
|
OPENTYPE_MIME = types_map['.otf']
|
||||||
GIF_MIME = types_map['.gif']
|
GIF_MIME = types_map['.gif']
|
||||||
JPEG_MIME = types_map['.jpeg']
|
JPEG_MIME = types_map['.jpeg']
|
||||||
PNG_MIME = types_map['.png']
|
PNG_MIME = types_map['.png']
|
||||||
|
@ -126,6 +126,17 @@ class CaseInsensitiveAttributesTranslator(HTMLTranslator):
|
|||||||
|
|
||||||
ci_css_to_xpath = CaseInsensitiveAttributesTranslator().css_to_xpath
|
ci_css_to_xpath = CaseInsensitiveAttributesTranslator().css_to_xpath
|
||||||
|
|
||||||
|
NULL_NAMESPACE_REGEX = re.compile(ur'''(name\(\) = ['"])h:''')
|
||||||
|
def fix_namespace(raw):
|
||||||
|
'''
|
||||||
|
cssselect uses name() = 'h:p' to select tags for some CSS selectors (e.g.
|
||||||
|
h|p+h|p).
|
||||||
|
However, since for us the XHTML namespace is the default namespace (with no
|
||||||
|
prefix), name() is the same as local-name(). So this is a hack to
|
||||||
|
workaround the problem.
|
||||||
|
'''
|
||||||
|
return NULL_NAMESPACE_REGEX.sub(ur'\1', raw)
|
||||||
|
|
||||||
class CSSSelector(object):
|
class CSSSelector(object):
|
||||||
|
|
||||||
def __init__(self, css, log=None, namespaces=XPNSMAP):
|
def __init__(self, css, log=None, namespaces=XPNSMAP):
|
||||||
@ -136,7 +147,7 @@ class CSSSelector(object):
|
|||||||
|
|
||||||
def build_selector(self, css, log, func=css_to_xpath):
|
def build_selector(self, css, log, func=css_to_xpath):
|
||||||
try:
|
try:
|
||||||
return etree.XPath(func(css), namespaces=self.namespaces)
|
return etree.XPath(fix_namespace(func(css)), namespaces=self.namespaces)
|
||||||
except:
|
except:
|
||||||
if log is not None:
|
if log is not None:
|
||||||
log.exception('Failed to parse CSS selector: %r'%css)
|
log.exception('Failed to parse CSS selector: %r'%css)
|
||||||
|
@ -14,9 +14,11 @@ from lxml import etree
|
|||||||
import cssutils
|
import cssutils
|
||||||
from cssutils.css import Property
|
from cssutils.css import Property
|
||||||
|
|
||||||
|
from calibre import guess_type
|
||||||
from calibre.ebooks.oeb.base import (XHTML, XHTML_NS, CSS_MIME, OEB_STYLES,
|
from calibre.ebooks.oeb.base import (XHTML, XHTML_NS, CSS_MIME, OEB_STYLES,
|
||||||
namespace, barename, XPath)
|
namespace, barename, XPath)
|
||||||
from calibre.ebooks.oeb.stylizer import Stylizer
|
from calibre.ebooks.oeb.stylizer import Stylizer
|
||||||
|
from calibre.utils.filenames import ascii_filename
|
||||||
|
|
||||||
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
||||||
STRIPNUM = re.compile(r'[-0-9]+$')
|
STRIPNUM = re.compile(r'[-0-9]+$')
|
||||||
@ -144,11 +146,61 @@ class CSSFlattener(object):
|
|||||||
cssutils.replaceUrls(item.data, item.abshref,
|
cssutils.replaceUrls(item.data, item.abshref,
|
||||||
ignoreImportRules=True)
|
ignoreImportRules=True)
|
||||||
|
|
||||||
|
self.body_font_family, self.embed_font_rules = self.get_embed_font_info(
|
||||||
|
self.opts.embed_font_family)
|
||||||
self.stylize_spine()
|
self.stylize_spine()
|
||||||
self.sbase = self.baseline_spine() if self.fbase else None
|
self.sbase = self.baseline_spine() if self.fbase else None
|
||||||
self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
|
self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
|
||||||
self.flatten_spine()
|
self.flatten_spine()
|
||||||
|
|
||||||
|
def get_embed_font_info(self, family, failure_critical=True):
|
||||||
|
efi = []
|
||||||
|
body_font_family = None
|
||||||
|
if not family:
|
||||||
|
return body_font_family, efi
|
||||||
|
from calibre.utils.fonts import fontconfig
|
||||||
|
from calibre.utils.fonts.utils import (get_font_characteristics,
|
||||||
|
panose_to_css_generic_family, get_font_names)
|
||||||
|
faces = fontconfig.fonts_for_family(family)
|
||||||
|
if not faces or not u'normal' in faces:
|
||||||
|
msg = (u'No embeddable fonts found for family: %r'%self.opts.embed_font_family)
|
||||||
|
if failure_critical:
|
||||||
|
raise ValueError(msg)
|
||||||
|
self.oeb.log.warn(msg)
|
||||||
|
return body_font_family, efi
|
||||||
|
|
||||||
|
for k, v in faces.iteritems():
|
||||||
|
ext, data = v[0::2]
|
||||||
|
weight, is_italic, is_bold, is_regular, fs_type, panose = \
|
||||||
|
get_font_characteristics(data)
|
||||||
|
generic_family = panose_to_css_generic_family(panose)
|
||||||
|
family_name, subfamily_name, full_name = get_font_names(data)
|
||||||
|
if k == u'normal':
|
||||||
|
body_font_family = u"'%s',%s"%(family_name, generic_family)
|
||||||
|
if family_name.lower() != family.lower():
|
||||||
|
self.oeb.log.warn(u'Failed to find an exact match for font:'
|
||||||
|
u' %r, using %r instead'%(family, family_name))
|
||||||
|
else:
|
||||||
|
self.oeb.log(u'Embedding font: %s'%family_name)
|
||||||
|
font = {u'font-family':u'"%s"'%family_name}
|
||||||
|
if is_italic:
|
||||||
|
font[u'font-style'] = u'italic'
|
||||||
|
if is_bold:
|
||||||
|
font[u'font-weight'] = u'bold'
|
||||||
|
fid, href = self.oeb.manifest.generate(id=u'font',
|
||||||
|
href=u'%s.%s'%(ascii_filename(full_name).replace(u' ', u'-'), ext))
|
||||||
|
item = self.oeb.manifest.add(fid, href,
|
||||||
|
guess_type(full_name+'.'+ext)[0],
|
||||||
|
data=data)
|
||||||
|
item.unload_data_from_memory()
|
||||||
|
font[u'src'] = u'url(%s)'%item.href
|
||||||
|
rule = '@font-face { %s }'%('; '.join(u'%s:%s'%(k, v) for k, v in
|
||||||
|
font.iteritems()))
|
||||||
|
rule = cssutils.parseString(rule)
|
||||||
|
efi.append(rule)
|
||||||
|
|
||||||
|
return body_font_family, efi
|
||||||
|
|
||||||
def stylize_spine(self):
|
def stylize_spine(self):
|
||||||
self.stylizers = {}
|
self.stylizers = {}
|
||||||
profile = self.context.source
|
profile = self.context.source
|
||||||
@ -170,6 +222,8 @@ class CSSFlattener(object):
|
|||||||
bs.extend(['page-break-before: always'])
|
bs.extend(['page-break-before: always'])
|
||||||
if self.context.change_justification != 'original':
|
if self.context.change_justification != 'original':
|
||||||
bs.append('text-align: '+ self.context.change_justification)
|
bs.append('text-align: '+ self.context.change_justification)
|
||||||
|
if self.body_font_family:
|
||||||
|
bs.append(u'font-family: '+self.body_font_family)
|
||||||
body.set('style', '; '.join(bs))
|
body.set('style', '; '.join(bs))
|
||||||
stylizer = Stylizer(html, item.href, self.oeb, self.context, profile,
|
stylizer = Stylizer(html, item.href, self.oeb, self.context, profile,
|
||||||
user_css=self.context.extra_css,
|
user_css=self.context.extra_css,
|
||||||
@ -450,7 +504,8 @@ class CSSFlattener(object):
|
|||||||
items.sort()
|
items.sort()
|
||||||
css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
|
css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
|
||||||
css = ('@page {\n%s\n}\n'%css) if items else ''
|
css = ('@page {\n%s\n}\n'%css) if items else ''
|
||||||
rules = [r.cssText for r in stylizer.font_face_rules]
|
rules = [r.cssText for r in stylizer.font_face_rules +
|
||||||
|
self.embed_font_rules]
|
||||||
raw = '\n\n'.join(rules)
|
raw = '\n\n'.join(rules)
|
||||||
css += '\n\n' + raw
|
css += '\n\n' + raw
|
||||||
global_css[css].append(item)
|
global_css[css].append(item)
|
||||||
|
@ -73,6 +73,7 @@ class Split(object):
|
|||||||
|
|
||||||
def find_page_breaks(self, item):
|
def find_page_breaks(self, item):
|
||||||
if self.page_break_selectors is None:
|
if self.page_break_selectors is None:
|
||||||
|
from calibre.ebooks.oeb.stylizer import fix_namespace
|
||||||
css_to_xpath = HTMLTranslator().css_to_xpath
|
css_to_xpath = HTMLTranslator().css_to_xpath
|
||||||
self.page_break_selectors = set([])
|
self.page_break_selectors = set([])
|
||||||
stylesheets = [x.data for x in self.oeb.manifest if x.media_type in
|
stylesheets = [x.data for x in self.oeb.manifest if x.media_type in
|
||||||
@ -84,7 +85,7 @@ class Split(object):
|
|||||||
'page-break-after'), 'cssText', '').strip().lower()
|
'page-break-after'), 'cssText', '').strip().lower()
|
||||||
try:
|
try:
|
||||||
if before and before not in {'avoid', 'auto', 'inherit'}:
|
if before and before not in {'avoid', 'auto', 'inherit'}:
|
||||||
self.page_break_selectors.add((XPath(css_to_xpath(rule.selectorText)),
|
self.page_break_selectors.add((XPath(fix_namespace(css_to_xpath(rule.selectorText))),
|
||||||
True))
|
True))
|
||||||
if self.remove_css_pagebreaks:
|
if self.remove_css_pagebreaks:
|
||||||
rule.style.removeProperty('page-break-before')
|
rule.style.removeProperty('page-break-before')
|
||||||
@ -92,7 +93,7 @@ class Split(object):
|
|||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
if after and after not in {'avoid', 'auto', 'inherit'}:
|
if after and after not in {'avoid', 'auto', 'inherit'}:
|
||||||
self.page_break_selectors.add((XPath(css_to_xpath(rule.selectorText)),
|
self.page_break_selectors.add((XPath(fix_namespace(css_to_xpath(rule.selectorText))),
|
||||||
False))
|
False))
|
||||||
if self.remove_css_pagebreaks:
|
if self.remove_css_pagebreaks:
|
||||||
rule.style.removeProperty('page-break-after')
|
rule.style.removeProperty('page-break-after')
|
||||||
|
@ -12,7 +12,7 @@ from PyQt4.Qt import (QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt,
|
|||||||
|
|
||||||
ORG_NAME = 'KovidsBrain'
|
ORG_NAME = 'KovidsBrain'
|
||||||
APP_UID = 'libprs500'
|
APP_UID = 'libprs500'
|
||||||
from calibre import prints
|
from calibre import prints, load_builtin_fonts
|
||||||
from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx,
|
from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx,
|
||||||
plugins, config_dir, filesystem_encoding, DEBUG)
|
plugins, config_dir, filesystem_encoding, DEBUG)
|
||||||
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
|
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
|
||||||
@ -779,6 +779,7 @@ class Application(QApplication):
|
|||||||
qt_app = self
|
qt_app = self
|
||||||
self._file_open_paths = []
|
self._file_open_paths = []
|
||||||
self._file_open_lock = RLock()
|
self._file_open_lock = RLock()
|
||||||
|
load_builtin_fonts()
|
||||||
self.setup_styles(force_calibre_style)
|
self.setup_styles(force_calibre_style)
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
@ -32,6 +32,7 @@ class LookAndFeelWidget(Widget, Ui_Form):
|
|||||||
Widget.__init__(self, parent,
|
Widget.__init__(self, parent,
|
||||||
['change_justification', 'extra_css', 'base_font_size',
|
['change_justification', 'extra_css', 'base_font_size',
|
||||||
'font_size_mapping', 'line_height', 'minimum_line_height',
|
'font_size_mapping', 'line_height', 'minimum_line_height',
|
||||||
|
'embed_font_family',
|
||||||
'smarten_punctuation', 'unsmarten_punctuation',
|
'smarten_punctuation', 'unsmarten_punctuation',
|
||||||
'disable_font_rescaling', 'insert_blank_line',
|
'disable_font_rescaling', 'insert_blank_line',
|
||||||
'remove_paragraph_spacing',
|
'remove_paragraph_spacing',
|
||||||
|
@ -7,27 +7,53 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>655</width>
|
<width>655</width>
|
||||||
<height>522</height>
|
<height>619</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="3" column="4">
|
||||||
<widget class="QCheckBox" name="opt_disable_font_rescaling">
|
<widget class="QDoubleSpinBox" name="opt_line_height">
|
||||||
<property name="text">
|
<property name="suffix">
|
||||||
<string>&Disable font size rescaling</string>
|
<string> pt</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="3" column="3">
|
||||||
<widget class="QLabel" name="label_18">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Base &font size:</string>
|
<string>Line &height:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>opt_base_font_size</cstring>
|
<cstring>opt_line_height</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Minimum &line height:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_minimum_line_height</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="opt_minimum_line_height">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> %</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>900.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -97,49 +123,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimum &line height:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_minimum_line_height</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="opt_minimum_line_height">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> %</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>900.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Line &height:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_line_height</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="opt_line_height">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> pt</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -157,14 +140,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2">
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="opt_remove_paragraph_spacing">
|
<widget class="QCheckBox" name="opt_remove_paragraph_spacing">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Remove &spacing between paragraphs</string>
|
<string>Remove &spacing between paragraphs</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="3">
|
<item row="7" column="3">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Indent size:</string>
|
<string>&Indent size:</string>
|
||||||
@ -177,7 +160,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="4">
|
<item row="7" column="4">
|
||||||
<widget class="QDoubleSpinBox" name="opt_remove_paragraph_spacing_indent_size">
|
<widget class="QDoubleSpinBox" name="opt_remove_paragraph_spacing_indent_size">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><p>When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent.</string>
|
<string><p>When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent.</string>
|
||||||
@ -199,85 +182,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
<item row="12" column="0" colspan="5">
|
||||||
<widget class="QCheckBox" name="opt_insert_blank_line">
|
|
||||||
<property name="text">
|
|
||||||
<string>Insert &blank line between paragraphs</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="3">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Line size:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_insert_blank_line_size</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="4">
|
|
||||||
<widget class="QDoubleSpinBox" name="opt_insert_blank_line_size">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> em</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Text &justification:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_change_justification</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="2" colspan="3">
|
|
||||||
<widget class="QComboBox" name="opt_change_justification"/>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="0">
|
|
||||||
<widget class="QCheckBox" name="opt_smarten_punctuation">
|
|
||||||
<property name="text">
|
|
||||||
<string>Smarten &punctuation</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="1" colspan="4">
|
|
||||||
<widget class="QCheckBox" name="opt_asciiize">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Transliterate unicode characters to ASCII</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="0">
|
|
||||||
<widget class="QCheckBox" name="opt_unsmarten_punctuation">
|
|
||||||
<property name="text">
|
|
||||||
<string>&UnSmarten punctuation</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="1" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="opt_keep_ligatures">
|
|
||||||
<property name="text">
|
|
||||||
<string>Keep &ligatures</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="3">
|
|
||||||
<widget class="QCheckBox" name="opt_linearize_tables">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Linearize tables</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="11" column="0" colspan="5">
|
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -378,10 +283,131 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="8" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_insert_blank_line">
|
||||||
|
<property name="text">
|
||||||
|
<string>Insert &blank line between paragraphs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="4">
|
||||||
|
<widget class="QDoubleSpinBox" name="opt_insert_blank_line_size">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> em</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Text &justification:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_change_justification</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="2" colspan="3">
|
||||||
|
<widget class="QComboBox" name="opt_change_justification"/>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_smarten_punctuation">
|
||||||
|
<property name="text">
|
||||||
|
<string>Smarten &punctuation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1" colspan="4">
|
||||||
|
<widget class="QCheckBox" name="opt_asciiize">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Transliterate unicode characters to ASCII</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_unsmarten_punctuation">
|
||||||
|
<property name="text">
|
||||||
|
<string>&UnSmarten punctuation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_keep_ligatures">
|
||||||
|
<property name="text">
|
||||||
|
<string>Keep &ligatures</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="3">
|
||||||
|
<widget class="QCheckBox" name="opt_linearize_tables">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Linearize tables</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>Base &font size:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_base_font_size</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="3">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Line size:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_insert_blank_line_size</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Embed font family:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_embed_font_family</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="5">
|
||||||
|
<widget class="QCheckBox" name="opt_disable_font_rescaling">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Disable font size rescaling</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1" colspan="2">
|
||||||
|
<widget class="FontFamilyChooser" name="opt_embed_font_family"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
@ -390,6 +416,11 @@
|
|||||||
<extends>QComboBox</extends>
|
<extends>QComboBox</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>FontFamilyChooser</class>
|
||||||
|
<extends>QComboBox</extends>
|
||||||
|
<header>calibre/gui2/font_family_chooser.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
@ -211,6 +211,9 @@ class RegexEdit(QWidget, Ui_Edit):
|
|||||||
self.button.clicked.connect(self.builder)
|
self.button.clicked.connect(self.builder)
|
||||||
|
|
||||||
def builder(self):
|
def builder(self):
|
||||||
|
if self.db is None:
|
||||||
|
self.doc_cache = _('Click the Open button below to open a '
|
||||||
|
'ebook to use for testing.')
|
||||||
bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self.doc_cache, self)
|
bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self.doc_cache, self)
|
||||||
if bld.cancelled:
|
if bld.cancelled:
|
||||||
return
|
return
|
||||||
|
@ -55,6 +55,12 @@ def writing_system_for_font(font):
|
|||||||
class FontFamilyDelegate(QStyledItemDelegate):
|
class FontFamilyDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
def sizeHint(self, option, index):
|
def sizeHint(self, option, index):
|
||||||
|
try:
|
||||||
|
return self.do_size_hint(option, index)
|
||||||
|
except:
|
||||||
|
return QSize(300, 50)
|
||||||
|
|
||||||
|
def do_size_hint(self, option, index):
|
||||||
text = index.data(Qt.DisplayRole).toString()
|
text = index.data(Qt.DisplayRole).toString()
|
||||||
font = QFont(option.font)
|
font = QFont(option.font)
|
||||||
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
||||||
@ -62,6 +68,14 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
return QSize(m.width(text), m.height())
|
return QSize(m.width(text), m.height())
|
||||||
|
|
||||||
def paint(self, painter, option, index):
|
def paint(self, painter, option, index):
|
||||||
|
painter.save()
|
||||||
|
try:
|
||||||
|
self.do_paint(painter, option, index)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
def do_paint(self, painter, option, index):
|
||||||
text = unicode(index.data(Qt.DisplayRole).toString())
|
text = unicode(index.data(Qt.DisplayRole).toString())
|
||||||
font = QFont(option.font)
|
font = QFont(option.font)
|
||||||
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
||||||
@ -75,7 +89,6 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
r = option.rect
|
r = option.rect
|
||||||
|
|
||||||
if option.state & QStyle.State_Selected:
|
if option.state & QStyle.State_Selected:
|
||||||
painter.save()
|
|
||||||
painter.setBrush(option.palette.highlight())
|
painter.setBrush(option.palette.highlight())
|
||||||
painter.setPen(Qt.NoPen)
|
painter.setPen(Qt.NoPen)
|
||||||
painter.drawRect(option.rect)
|
painter.drawRect(option.rect)
|
||||||
@ -102,9 +115,6 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
|
|
||||||
painter.setFont(old)
|
painter.setFont(old)
|
||||||
|
|
||||||
if (option.state & QStyle.State_Selected):
|
|
||||||
painter.restore()
|
|
||||||
|
|
||||||
class FontFamilyChooser(QComboBox):
|
class FontFamilyChooser(QComboBox):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -138,7 +148,10 @@ class FontFamilyChooser(QComboBox):
|
|||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
ans = QComboBox.sizeHint(self)
|
ans = QComboBox.sizeHint(self)
|
||||||
ans.setWidth(QFontMetrics(self.font()).width('m'*14))
|
try:
|
||||||
|
ans.setWidth(QFontMetrics(self.font()).width('m'*14))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
@ -157,12 +170,15 @@ class FontFamilyChooser(QComboBox):
|
|||||||
self.setCurrentIndex(idx)
|
self.setCurrentIndex(idx)
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
|
def test():
|
||||||
if __name__ == '__main__':
|
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
|
app
|
||||||
d = QDialog()
|
d = QDialog()
|
||||||
d.setLayout(QVBoxLayout())
|
d.setLayout(QVBoxLayout())
|
||||||
d.layout().addWidget(FontFamilyChooser(d))
|
d.layout().addWidget(FontFamilyChooser(d))
|
||||||
d.layout().addWidget(QFontComboBox(d))
|
d.layout().addWidget(QFontComboBox(d))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
|
|
||||||
|
@ -1368,6 +1368,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
return QVariant(authors_to_string(au))
|
return QVariant(authors_to_string(au))
|
||||||
elif cname == 'size':
|
elif cname == 'size':
|
||||||
size = self.db[self.map[row]].size
|
size = self.db[self.map[row]].size
|
||||||
|
if not isinstance(size, (float, int)):
|
||||||
|
size = 0
|
||||||
return QVariant(human_readable(size))
|
return QVariant(human_readable(size))
|
||||||
elif cname == 'timestamp':
|
elif cname == 'timestamp':
|
||||||
dt = self.db[self.map[row]].datetime
|
dt = self.db[self.map[row]].datetime
|
||||||
|
@ -139,3 +139,5 @@ class MainWindow(QMainWindow):
|
|||||||
show=True)
|
show=True)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
@ -270,7 +270,7 @@ class AuthorsEdit(EditWithComplete):
|
|||||||
import traceback
|
import traceback
|
||||||
fname = err.filename if err.filename else 'file'
|
fname = err.filename if err.filename else 'file'
|
||||||
error_dialog(self, _('Permission denied'),
|
error_dialog(self, _('Permission denied'),
|
||||||
_('Could not open %s. Is it being used by another'
|
_('Could not open "%s". Is it being used by another'
|
||||||
' program?')%fname, det_msg=traceback.format_exc(),
|
' program?')%fname, det_msg=traceback.format_exc(),
|
||||||
show=True)
|
show=True)
|
||||||
return False
|
return False
|
||||||
|
@ -66,6 +66,8 @@ class SonyStore(BasicStoreConfig, StorePlugin):
|
|||||||
detail_url = ''.join(item.xpath('descendant::h3[@class="item"]'
|
detail_url = ''.join(item.xpath('descendant::h3[@class="item"]'
|
||||||
'/descendant::a[@class="fn" and @href]/@href'))
|
'/descendant::a[@class="fn" and @href]/@href'))
|
||||||
if not detail_url: continue
|
if not detail_url: continue
|
||||||
|
if detail_url.startswith('/'):
|
||||||
|
detail_url = 'http:'+detail_url
|
||||||
s.detail_item = detail_url
|
s.detail_item = detail_url
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
@ -16,7 +16,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
|||||||
|
|
||||||
from calibre.gui2.viewer.flip import SlideFlip
|
from calibre.gui2.viewer.flip import SlideFlip
|
||||||
from calibre.gui2.shortcuts import Shortcuts
|
from calibre.gui2.shortcuts import Shortcuts
|
||||||
from calibre import prints, load_builtin_fonts
|
from calibre import prints
|
||||||
from calibre.customize.ui import all_viewer_plugins
|
from calibre.customize.ui import all_viewer_plugins
|
||||||
from calibre.gui2.viewer.keys import SHORTCUTS
|
from calibre.gui2.viewer.keys import SHORTCUTS
|
||||||
from calibre.gui2.viewer.javascript import JavaScriptLoader
|
from calibre.gui2.viewer.javascript import JavaScriptLoader
|
||||||
@ -86,7 +86,6 @@ class Document(QWebPage): # {{{
|
|||||||
settings = self.settings()
|
settings = self.settings()
|
||||||
|
|
||||||
# Fonts
|
# Fonts
|
||||||
load_builtin_fonts()
|
|
||||||
self.all_viewer_plugins = tuple(all_viewer_plugins())
|
self.all_viewer_plugins = tuple(all_viewer_plugins())
|
||||||
for pl in self.all_viewer_plugins:
|
for pl in self.all_viewer_plugins:
|
||||||
pl.load_fonts()
|
pl.load_fonts()
|
||||||
|
@ -693,16 +693,16 @@ datatype is one of: {0}
|
|||||||
'the data in this column will be interpreted. This is a JSON '
|
'the data in this column will be interpreted. This is a JSON '
|
||||||
' string. For enumeration columns, use '
|
' string. For enumeration columns, use '
|
||||||
'--display="{\\"enum_values\\":[\\"val1\\", \\"val2\\"]}"'
|
'--display="{\\"enum_values\\":[\\"val1\\", \\"val2\\"]}"'
|
||||||
''
|
'\n'
|
||||||
'There are many options that can go into the display variable.'
|
'There are many options that can go into the display variable.'
|
||||||
'The options by column type are:'
|
'The options by column type are:\n'
|
||||||
'composite: composite_template, composite_sort, make_category,'
|
'composite: composite_template, composite_sort, make_category,'
|
||||||
' contains_html, use_decorations'
|
'contains_html, use_decorations\n'
|
||||||
'datetime: date_format'
|
'datetime: date_format\n'
|
||||||
'enumeration: enum_values, enum_colors, use_decorations'
|
'enumeration: enum_values, enum_colors, use_decorations\n'
|
||||||
'int, float: number_format'
|
'int, float: number_format\n'
|
||||||
'text: is_names. use_decorations'
|
'text: is_names, use_decorations\n'
|
||||||
''
|
'\n'
|
||||||
'The best way to find legal combinations is to create a custom'
|
'The best way to find legal combinations is to create a custom'
|
||||||
'column of the appropriate type in the GUI then look at the'
|
'column of the appropriate type in the GUI then look at the'
|
||||||
'backup OPF for a book (ensure that a new OPF has been created'
|
'backup OPF for a book (ensure that a new OPF has been created'
|
||||||
@ -720,7 +720,6 @@ def command_add_custom_column(args, dbpath):
|
|||||||
print
|
print
|
||||||
print >>sys.stderr, _('You must specify label, name and datatype')
|
print >>sys.stderr, _('You must specify label, name and datatype')
|
||||||
return 1
|
return 1
|
||||||
print opts.display
|
|
||||||
do_add_custom_column(get_db(dbpath, opts), args[0], args[1], args[2],
|
do_add_custom_column(get_db(dbpath, opts), args[0], args[1], args[2],
|
||||||
opts.is_multiple, json.loads(opts.display))
|
opts.is_multiple, json.loads(opts.display))
|
||||||
# Re-open the DB so that field_metadata is reflects the column changes
|
# Re-open the DB so that field_metadata is reflects the column changes
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
The database used to store ebook metadata
|
The database used to store ebook metadata
|
||||||
'''
|
'''
|
||||||
import os, sys, shutil, cStringIO, glob, time, functools, traceback, re, \
|
import os, sys, shutil, cStringIO, glob, time, functools, traceback, re, \
|
||||||
json, uuid, hashlib, copy
|
json, uuid, hashlib, copy, errno
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import threading, random
|
import threading, random
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
@ -30,7 +30,8 @@ from calibre.ptempfile import (PersistentTemporaryFile,
|
|||||||
base_dir, SpooledTemporaryFile)
|
base_dir, SpooledTemporaryFile)
|
||||||
from calibre.customize.ui import run_plugins_on_import
|
from calibre.customize.ui import run_plugins_on_import
|
||||||
from calibre import isbytestring
|
from calibre import isbytestring
|
||||||
from calibre.utils.filenames import ascii_filename, samefile
|
from calibre.utils.filenames import (ascii_filename, samefile,
|
||||||
|
windows_is_folder_in_use)
|
||||||
from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp,
|
from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp,
|
||||||
parse_only_date, UNDEFINED_DATE)
|
parse_only_date, UNDEFINED_DATE)
|
||||||
from calibre.utils.config import prefs, tweaks, from_json, to_json
|
from calibre.utils.config import prefs, tweaks, from_json, to_json
|
||||||
@ -649,6 +650,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
spath = os.path.join(self.library_path, *current_path.split('/'))
|
spath = os.path.join(self.library_path, *current_path.split('/'))
|
||||||
|
|
||||||
if current_path and os.path.exists(spath): # Migrate existing files
|
if current_path and os.path.exists(spath): # Migrate existing files
|
||||||
|
if iswindows:
|
||||||
|
uf = windows_is_folder_in_use(spath)
|
||||||
|
if uf is not None:
|
||||||
|
err = IOError(errno.EACCES,
|
||||||
|
_('File is open in another process'))
|
||||||
|
err.filename = uf
|
||||||
|
raise err
|
||||||
cdata = self.cover(id, index_is_id=True)
|
cdata = self.cover(id, index_is_id=True)
|
||||||
if cdata is not None:
|
if cdata is not None:
|
||||||
with lopen(os.path.join(tpath, 'cover.jpg'), 'wb') as f:
|
with lopen(os.path.join(tpath, 'cover.jpg'), 'wb') as f:
|
||||||
|
@ -32,6 +32,11 @@ def test_lxml():
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError('lxml failed')
|
raise RuntimeError('lxml failed')
|
||||||
|
|
||||||
|
def test_freetype():
|
||||||
|
from calibre.utils.fonts.free_type import test
|
||||||
|
test()
|
||||||
|
print ('FreeType OK!')
|
||||||
|
|
||||||
def test_fontconfig():
|
def test_fontconfig():
|
||||||
from calibre.utils.fonts import fontconfig
|
from calibre.utils.fonts import fontconfig
|
||||||
families = fontconfig.find_font_families()
|
families = fontconfig.find_font_families()
|
||||||
@ -103,21 +108,28 @@ def test_icu():
|
|||||||
def test_wpd():
|
def test_wpd():
|
||||||
wpd = plugins['wpd'][0]
|
wpd = plugins['wpd'][0]
|
||||||
try:
|
try:
|
||||||
wpd.init()
|
wpd.init('calibre', 1, 1, 1)
|
||||||
except wpd.NoWPD:
|
except wpd.NoWPD:
|
||||||
print ('This computer does not have WPD')
|
print ('This computer does not have WPD')
|
||||||
else:
|
else:
|
||||||
wpd.uninit()
|
wpd.uninit()
|
||||||
|
|
||||||
|
def test_woff():
|
||||||
|
from calibre.utils.fonts.woff import test
|
||||||
|
test()
|
||||||
|
print ('WOFF ok!')
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
test_plugins()
|
test_plugins()
|
||||||
test_lxml()
|
test_lxml()
|
||||||
|
test_freetype()
|
||||||
test_fontconfig()
|
test_fontconfig()
|
||||||
test_sqlite()
|
test_sqlite()
|
||||||
test_qt()
|
test_qt()
|
||||||
test_imaging()
|
test_imaging()
|
||||||
test_unrar()
|
test_unrar()
|
||||||
test_icu()
|
test_icu()
|
||||||
|
test_woff()
|
||||||
if iswindows:
|
if iswindows:
|
||||||
test_win32()
|
test_win32()
|
||||||
test_winutil()
|
test_winutil()
|
||||||
|
@ -249,4 +249,32 @@ def samefile(src, dst):
|
|||||||
os.path.normcase(os.path.abspath(dst)))
|
os.path.normcase(os.path.abspath(dst)))
|
||||||
return samestring
|
return samestring
|
||||||
|
|
||||||
|
def windows_is_file_opened(path):
|
||||||
|
import win32file, winerror
|
||||||
|
from pywintypes import error
|
||||||
|
if isbytestring(path): path = path.decode(filesystem_encoding)
|
||||||
|
try:
|
||||||
|
h = win32file.CreateFile(path, win32file.GENERIC_READ, 0, None,
|
||||||
|
win32file.OPEN_EXISTING, 0, 0)
|
||||||
|
except error as e:
|
||||||
|
if getattr(e, 'winerror', 0) == winerror.ERROR_SHARING_VIOLATION:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
win32file.CloseHandle(h)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def windows_is_folder_in_use(path):
|
||||||
|
'''
|
||||||
|
Returns the path to a file that is used in another process in the specified
|
||||||
|
folder, or None if no such file exists. Note
|
||||||
|
that this function is not a guarantee. A file may well be opened in the
|
||||||
|
folder after this function returns. However, it is useful to handle the
|
||||||
|
common case of a sharing violation gracefully most of the time.
|
||||||
|
'''
|
||||||
|
if isbytestring(path): path = path.decode(filesystem_encoding)
|
||||||
|
for x in os.listdir(path):
|
||||||
|
f = os.path.join(path, x)
|
||||||
|
if windows_is_file_opened(f):
|
||||||
|
return f
|
||||||
|
return None
|
||||||
|
|
||||||
|
@ -55,19 +55,65 @@ class Fonts(object):
|
|||||||
files = self.backend.files_for_family(family, normalize=normalize)
|
files = self.backend.files_for_family(family, normalize=normalize)
|
||||||
ans = {}
|
ans = {}
|
||||||
for ft, val in files.iteritems():
|
for ft, val in files.iteritems():
|
||||||
name, f = val
|
f, name = val
|
||||||
ext = f.rpartition('.')[-1].lower()
|
ext = f.rpartition('.')[-1].lower()
|
||||||
ans[ft] = (ext, name, open(f, 'rb').read())
|
ans[ft] = (ext, name, open(f, 'rb').read())
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def find_font_for_text(self, text, allowed_families={'serif', 'sans-serif'},
|
||||||
|
preferred_families=('serif', 'sans-serif', 'monospace', 'cursive', 'fantasy')):
|
||||||
|
'''
|
||||||
|
Find a font on the system capable of rendering the given text.
|
||||||
|
|
||||||
|
Returns a font family (as given by fonts_for_family()) that has a
|
||||||
|
"normal" font and that can render the supplied text. If no such font
|
||||||
|
exists, returns None.
|
||||||
|
|
||||||
|
:return: (family name, faces) or None, None
|
||||||
|
'''
|
||||||
|
from calibre.utils.fonts.free_type import FreeType, get_printable_characters, FreeTypeError
|
||||||
|
from calibre.utils.fonts.utils import panose_to_css_generic_family, get_font_characteristics
|
||||||
|
ft = FreeType()
|
||||||
|
found = {}
|
||||||
|
if not isinstance(text, unicode):
|
||||||
|
raise TypeError(u'%r is not unicode'%text)
|
||||||
|
text = get_printable_characters(text)
|
||||||
|
|
||||||
|
def filter_faces(faces):
|
||||||
|
ans = {}
|
||||||
|
for k, v in faces.iteritems():
|
||||||
|
try:
|
||||||
|
font = ft.load_font(v[2])
|
||||||
|
except FreeTypeError:
|
||||||
|
continue
|
||||||
|
if font.supports_text(text, has_non_printable_chars=False):
|
||||||
|
ans[k] = v
|
||||||
|
return ans
|
||||||
|
|
||||||
|
for family in sorted(self.find_font_families()):
|
||||||
|
faces = filter_faces(self.fonts_for_family(family))
|
||||||
|
if 'normal' not in faces:
|
||||||
|
continue
|
||||||
|
panose = get_font_characteristics(faces['normal'][2])[5]
|
||||||
|
generic_family = panose_to_css_generic_family(panose)
|
||||||
|
if generic_family in allowed_families or generic_family == preferred_families[0]:
|
||||||
|
return (family, faces)
|
||||||
|
elif generic_family not in found:
|
||||||
|
found[generic_family] = (family, faces)
|
||||||
|
|
||||||
|
for f in preferred_families:
|
||||||
|
if f in found:
|
||||||
|
return found[f]
|
||||||
|
return None, None
|
||||||
|
|
||||||
fontconfig = Fonts()
|
fontconfig = Fonts()
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
import os
|
import os
|
||||||
print(fontconfig.find_font_families())
|
print(fontconfig.find_font_families())
|
||||||
m = 'times new roman' if iswindows else 'liberation serif'
|
m = 'Liberation Serif'
|
||||||
for ft, val in fontconfig.files_for_family(m).iteritems():
|
for ft, val in fontconfig.files_for_family(m).iteritems():
|
||||||
print val[0], ft, val[1], os.path.getsize(val[1])
|
print val[0], ft, val[1], os.path.getsize(val[0])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test()
|
test()
|
||||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import os, sys
|
import os, sys
|
||||||
|
|
||||||
from calibre.constants import plugins, iswindows, islinux, isbsd
|
from calibre.constants import plugins, islinux, isbsd
|
||||||
|
|
||||||
_fc, _fc_err = plugins['fontconfig']
|
_fc, _fc_err = plugins['fontconfig']
|
||||||
|
|
||||||
@ -35,18 +35,14 @@ class FontConfig(Thread):
|
|||||||
if isinstance(config_dir, unicode):
|
if isinstance(config_dir, unicode):
|
||||||
config_dir = config_dir.encode(sys.getfilesystemencoding())
|
config_dir = config_dir.encode(sys.getfilesystemencoding())
|
||||||
config = os.path.join(config_dir, 'fonts.conf')
|
config = os.path.join(config_dir, 'fonts.conf')
|
||||||
if iswindows and getattr(sys, 'frozen', False):
|
|
||||||
config_dir = os.path.join(os.path.dirname(sys.executable),
|
|
||||||
'fontconfig')
|
|
||||||
if isinstance(config_dir, unicode):
|
|
||||||
config_dir = config_dir.encode(sys.getfilesystemencoding())
|
|
||||||
config = os.path.join(config_dir, 'fonts.conf')
|
|
||||||
try:
|
try:
|
||||||
_fc.initialize(config)
|
_fc.initialize(config)
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.failed = True
|
self.failed = True
|
||||||
|
if not self.failed and hasattr(_fc, 'add_font_dir'):
|
||||||
|
_fc.add_font_dir(P('fonts/liberation'))
|
||||||
|
|
||||||
def wait(self):
|
def wait(self):
|
||||||
if not (islinux or isbsd):
|
if not (islinux or isbsd):
|
||||||
@ -162,7 +158,7 @@ def test():
|
|||||||
from pprint import pprint;
|
from pprint import pprint;
|
||||||
pprint(fontconfig.find_font_families())
|
pprint(fontconfig.find_font_families())
|
||||||
pprint(fontconfig.files_for_family('liberation serif'))
|
pprint(fontconfig.files_for_family('liberation serif'))
|
||||||
m = 'times new roman' if iswindows else 'liberation serif'
|
m = 'liberation serif'
|
||||||
pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True))
|
pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -44,6 +44,17 @@ fontconfig_initialize(PyObject *self, PyObject *args) {
|
|||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
fontconfig_add_font_dir(PyObject *self, PyObject *args) {
|
||||||
|
FcChar8 *path;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &path)) return NULL;
|
||||||
|
|
||||||
|
if (FcConfigAppFontAddDir(NULL, path))
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fontconfig_cleanup_find(FcPattern *p, FcObjectSet *oset, FcFontSet *fs) {
|
fontconfig_cleanup_find(FcPattern *p, FcObjectSet *oset, FcFontSet *fs) {
|
||||||
if (p != NULL) FcPatternDestroy(p);
|
if (p != NULL) FcPatternDestroy(p);
|
||||||
@ -314,6 +325,11 @@ PyMethodDef fontconfig_methods[] = {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{"add_font_dir", fontconfig_add_font_dir, METH_VARARGS,
|
||||||
|
"add_font_dir(path_to_dir)\n\n"
|
||||||
|
"Add the fonts in the specified directory to the list of application specific fonts."
|
||||||
|
},
|
||||||
|
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
98
src/calibre/utils/fonts/free_type.py
Normal file
98
src/calibre/utils/fonts/free_type.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#!/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 threading, unicodedata
|
||||||
|
from functools import wraps
|
||||||
|
from future_builtins import map
|
||||||
|
|
||||||
|
from calibre.constants import plugins
|
||||||
|
|
||||||
|
class ThreadingViolation(Exception):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Exception.__init__(self,
|
||||||
|
'You cannot use the MTP driver from a thread other than the '
|
||||||
|
' thread in which startup() was called')
|
||||||
|
|
||||||
|
def get_printable_characters(text):
|
||||||
|
return u''.join(x for x in unicodedata.normalize('NFC', text)
|
||||||
|
if unicodedata.category(x)[0] not in {'C', 'Z', 'M'})
|
||||||
|
|
||||||
|
def same_thread(func):
|
||||||
|
@wraps(func)
|
||||||
|
def check_thread(self, *args, **kwargs):
|
||||||
|
if self.start_thread is not threading.current_thread():
|
||||||
|
raise ThreadingViolation()
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
return check_thread
|
||||||
|
|
||||||
|
FreeTypeError = getattr(plugins['freetype'][0], 'FreeTypeError', Exception)
|
||||||
|
|
||||||
|
class Face(object):
|
||||||
|
|
||||||
|
def __init__(self, face):
|
||||||
|
self.start_thread = threading.current_thread()
|
||||||
|
self.face = face
|
||||||
|
for x in ('family_name', 'style_name'):
|
||||||
|
val = getattr(self.face, x)
|
||||||
|
try:
|
||||||
|
val = val.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
val = repr(val).decode('utf-8')
|
||||||
|
setattr(self, x, val)
|
||||||
|
|
||||||
|
@same_thread
|
||||||
|
def supports_text(self, text, has_non_printable_chars=True):
|
||||||
|
'''
|
||||||
|
Returns True if all the characters in text have glyphs in this font.
|
||||||
|
'''
|
||||||
|
if not isinstance(text, unicode):
|
||||||
|
raise TypeError('%r is not a unicode object'%text)
|
||||||
|
if has_non_printable_chars:
|
||||||
|
text = get_printable_characters(text)
|
||||||
|
chars = tuple(frozenset(map(ord, text)))
|
||||||
|
return self.face.supports_text(chars)
|
||||||
|
|
||||||
|
class FreeType(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.start_thread = threading.current_thread()
|
||||||
|
ft, ft_err = plugins['freetype']
|
||||||
|
if ft_err:
|
||||||
|
raise RuntimeError('Failed to load FreeType module with error: %s'
|
||||||
|
% ft_err)
|
||||||
|
self.ft = ft.FreeType()
|
||||||
|
|
||||||
|
@same_thread
|
||||||
|
def load_font(self, data):
|
||||||
|
return Face(self.ft.load_font(data))
|
||||||
|
|
||||||
|
def test():
|
||||||
|
data = P('fonts/calibreSymbols.otf', data=True)
|
||||||
|
ft = FreeType()
|
||||||
|
font = ft.load_font(data)
|
||||||
|
if not font.supports_text('.\u2605★'):
|
||||||
|
raise RuntimeError('Incorrectly returning that text is not supported')
|
||||||
|
if font.supports_text('abc'):
|
||||||
|
raise RuntimeError('Incorrectly claiming that text is supported')
|
||||||
|
|
||||||
|
def test_find_font():
|
||||||
|
from calibre.utils.fonts import fontconfig
|
||||||
|
abcd = '诶比西迪'
|
||||||
|
family = fontconfig.find_font_for_text(abcd)[0]
|
||||||
|
print ('Family for Chinese text:', family)
|
||||||
|
family = fontconfig.find_font_for_text(abcd)[0]
|
||||||
|
abcd = 'لوحة المفاتيح العربية'
|
||||||
|
print ('Family for Arabic text:', family)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
|
test_find_font()
|
||||||
|
|
304
src/calibre/utils/fonts/freetype.cpp
Normal file
304
src/calibre/utils/fonts/freetype.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
* freetype.cpp
|
||||||
|
* Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _UNICODE
|
||||||
|
#define UNICODE
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
static PyObject *FreeTypeError = NULL;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
FT_Face face;
|
||||||
|
// Every face must keep a reference to the FreeType library object to
|
||||||
|
// ensure it is garbage collected before the library object, to prevent
|
||||||
|
// segfaults.
|
||||||
|
PyObject *library;
|
||||||
|
} Face;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
FT_Library library;
|
||||||
|
} FreeType;
|
||||||
|
|
||||||
|
// Face.__init__() {{{
|
||||||
|
static void
|
||||||
|
Face_dealloc(Face* self)
|
||||||
|
{
|
||||||
|
if (self->face != NULL) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
FT_Done_Face(self->face);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
}
|
||||||
|
self->face = NULL;
|
||||||
|
|
||||||
|
Py_DECREF(self->library);
|
||||||
|
self->library = NULL;
|
||||||
|
|
||||||
|
self->ob_type->tp_free((PyObject*)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
Face_init(Face *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
FT_Error error = 0;
|
||||||
|
char *data;
|
||||||
|
Py_ssize_t sz;
|
||||||
|
PyObject *ft;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "Os#", &ft, &data, &sz)) return -1;
|
||||||
|
self->library = ft;
|
||||||
|
Py_XINCREF(ft);
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
error = FT_New_Memory_Face( ( (FreeType*)ft )->library,
|
||||||
|
(const FT_Byte*)data, (FT_Long)sz, 0, &self->face);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
if (error) {
|
||||||
|
self->face = NULL;
|
||||||
|
if ( error == FT_Err_Unknown_File_Format || error == FT_Err_Invalid_Stream_Operation)
|
||||||
|
PyErr_SetString(FreeTypeError, "Not a supported font format");
|
||||||
|
else
|
||||||
|
PyErr_Format(FreeTypeError, "Failed to initialize the Font with error: 0x%x", error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
family_name(Face *self, void *closure) {
|
||||||
|
return Py_BuildValue("s", self->face->family_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
style_name(Face *self, void *closure) {
|
||||||
|
return Py_BuildValue("s", self->face->style_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
supports_text(Face *self, PyObject *args) {
|
||||||
|
PyObject *chars, *fast, *ret = Py_True;
|
||||||
|
Py_ssize_t sz, i;
|
||||||
|
FT_ULong code;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &chars)) return NULL;
|
||||||
|
fast = PySequence_Fast(chars, "List of chars is not a sequence");
|
||||||
|
if (fast == NULL) return NULL;
|
||||||
|
sz = PySequence_Fast_GET_SIZE(fast);
|
||||||
|
|
||||||
|
for (i = 0; i < sz; i++) {
|
||||||
|
code = (FT_ULong)PyNumber_AsSsize_t(PySequence_Fast_GET_ITEM(fast, i), NULL);
|
||||||
|
if (FT_Get_Char_Index(self->face, code) == 0) {
|
||||||
|
ret = Py_False;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(fast);
|
||||||
|
Py_XINCREF(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyGetSetDef Face_getsetters[] = {
|
||||||
|
{(char *)"family_name",
|
||||||
|
(getter)family_name, NULL,
|
||||||
|
(char *)"The family name of this font.",
|
||||||
|
NULL},
|
||||||
|
|
||||||
|
{(char *)"style_name",
|
||||||
|
(getter)style_name, NULL,
|
||||||
|
(char *)"The style name of this font.",
|
||||||
|
NULL},
|
||||||
|
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyMethodDef Face_methods[] = {
|
||||||
|
{"supports_text", (PyCFunction)supports_text, METH_VARARGS,
|
||||||
|
"supports_text(sequence of unicode character codes) -> Return True iff this font has glyphs for all the specified characters."
|
||||||
|
},
|
||||||
|
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
// FreeType.__init__() {{{
|
||||||
|
static void
|
||||||
|
dealloc(FreeType* self)
|
||||||
|
{
|
||||||
|
if (self->library != NULL) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
FT_Done_FreeType(self->library);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
}
|
||||||
|
self->library = NULL;
|
||||||
|
|
||||||
|
self->ob_type->tp_free((PyObject*)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
init(FreeType *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
FT_Error error = 0;
|
||||||
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
|
error = FT_Init_FreeType(&self->library);
|
||||||
|
Py_END_ALLOW_THREADS;
|
||||||
|
if (error) {
|
||||||
|
self->library = NULL;
|
||||||
|
PyErr_Format(FreeTypeError, "Failed to initialize the FreeType library with error: %d", error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
static PyTypeObject FaceType = { // {{{
|
||||||
|
PyObject_HEAD_INIT(NULL)
|
||||||
|
0, /*ob_size*/
|
||||||
|
"freetype.Face", /*tp_name*/
|
||||||
|
sizeof(Face), /*tp_basicsize*/
|
||||||
|
0, /*tp_itemsize*/
|
||||||
|
(destructor)Face_dealloc, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_compare*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number*/
|
||||||
|
0, /*tp_as_sequence*/
|
||||||
|
0, /*tp_as_mapping*/
|
||||||
|
0, /*tp_hash */
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||||
|
"Face", /* tp_doc */
|
||||||
|
0, /* tp_traverse */
|
||||||
|
0, /* tp_clear */
|
||||||
|
0, /* tp_richcompare */
|
||||||
|
0, /* tp_weaklistoffset */
|
||||||
|
0, /* tp_iter */
|
||||||
|
0, /* tp_iternext */
|
||||||
|
Face_methods, /* tp_methods */
|
||||||
|
0, /* tp_members */
|
||||||
|
Face_getsetters, /* tp_getset */
|
||||||
|
0, /* tp_base */
|
||||||
|
0, /* tp_dict */
|
||||||
|
0, /* tp_descr_get */
|
||||||
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
(initproc)Face_init, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
0, /* tp_new */
|
||||||
|
}; // }}}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
load_font(FreeType *self, PyObject *args) {
|
||||||
|
PyObject *ret, *arg_list, *bytes;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &bytes)) return NULL;
|
||||||
|
|
||||||
|
arg_list = Py_BuildValue("OO", self, bytes);
|
||||||
|
if (arg_list == NULL) return NULL;
|
||||||
|
|
||||||
|
ret = PyObject_CallObject((PyObject *) &FaceType, arg_list);
|
||||||
|
Py_DECREF(arg_list);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef FreeType_methods[] = {
|
||||||
|
{"load_font", (PyCFunction)load_font, METH_VARARGS,
|
||||||
|
"load_font(bytestring) -> Load a font from font data."
|
||||||
|
},
|
||||||
|
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static PyTypeObject FreeTypeType = { // {{{
|
||||||
|
PyObject_HEAD_INIT(NULL)
|
||||||
|
0, /*ob_size*/
|
||||||
|
"freetype.FreeType", /*tp_name*/
|
||||||
|
sizeof(FreeType), /*tp_basicsize*/
|
||||||
|
0, /*tp_itemsize*/
|
||||||
|
(destructor)dealloc, /*tp_dealloc*/
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_compare*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number*/
|
||||||
|
0, /*tp_as_sequence*/
|
||||||
|
0, /*tp_as_mapping*/
|
||||||
|
0, /*tp_hash */
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
||||||
|
"FreeType", /* tp_doc */
|
||||||
|
0, /* tp_traverse */
|
||||||
|
0, /* tp_clear */
|
||||||
|
0, /* tp_richcompare */
|
||||||
|
0, /* tp_weaklistoffset */
|
||||||
|
0, /* tp_iter */
|
||||||
|
0, /* tp_iternext */
|
||||||
|
FreeType_methods, /* tp_methods */
|
||||||
|
0, /* tp_members */
|
||||||
|
0, /* tp_getset */
|
||||||
|
0, /* tp_base */
|
||||||
|
0, /* tp_dict */
|
||||||
|
0, /* tp_descr_get */
|
||||||
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
(initproc)init, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
0, /* tp_new */
|
||||||
|
}; // }}}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyMethodDef methods[] = {
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
initfreetype(void) {
|
||||||
|
PyObject *m;
|
||||||
|
|
||||||
|
FreeTypeType.tp_new = PyType_GenericNew;
|
||||||
|
if (PyType_Ready(&FreeTypeType) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FaceType.tp_new = PyType_GenericNew;
|
||||||
|
if (PyType_Ready(&FaceType) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m = Py_InitModule3(
|
||||||
|
"freetype", methods,
|
||||||
|
"FreeType API"
|
||||||
|
);
|
||||||
|
if (m == NULL) return;
|
||||||
|
|
||||||
|
FreeTypeError = PyErr_NewException((char*)"freetype.FreeTypeError", NULL, NULL);
|
||||||
|
if (FreeTypeError == NULL) return;
|
||||||
|
PyModule_AddObject(m, "FreeTypeError", FreeTypeError);
|
||||||
|
|
||||||
|
Py_INCREF(&FreeTypeType);
|
||||||
|
PyModule_AddObject(m, "FreeType", (PyObject *)&FreeTypeType);
|
||||||
|
PyModule_AddObject(m, "Face", (PyObject *)&FaceType);
|
||||||
|
}
|
||||||
|
|
@ -38,8 +38,8 @@ def get_table(raw, name):
|
|||||||
|
|
||||||
def get_font_characteristics(raw):
|
def get_font_characteristics(raw):
|
||||||
'''
|
'''
|
||||||
Return (weight, is_italic, is_bold, is_regular, fs_type). These values are taken
|
Return (weight, is_italic, is_bold, is_regular, fs_type, panose). These
|
||||||
from the OS/2 table of the font. See
|
values are taken from the OS/2 table of the font. See
|
||||||
http://www.microsoft.com/typography/otspec/os2.htm for details
|
http://www.microsoft.com/typography/otspec/os2.htm for details
|
||||||
'''
|
'''
|
||||||
os2_table = get_table(raw, 'os/2')[0]
|
os2_table = get_table(raw, 'os/2')[0]
|
||||||
@ -54,7 +54,6 @@ def get_font_characteristics(raw):
|
|||||||
family_class) = struct.unpack_from(common_fields, os2_table)
|
family_class) = struct.unpack_from(common_fields, os2_table)
|
||||||
offset = struct.calcsize(common_fields)
|
offset = struct.calcsize(common_fields)
|
||||||
panose = struct.unpack_from(b'>10B', os2_table, offset)
|
panose = struct.unpack_from(b'>10B', os2_table, offset)
|
||||||
panose
|
|
||||||
offset += 10
|
offset += 10
|
||||||
(range1,) = struct.unpack_from(b'>L', os2_table, offset)
|
(range1,) = struct.unpack_from(b'>L', os2_table, offset)
|
||||||
offset += struct.calcsize(b'>L')
|
offset += struct.calcsize(b'>L')
|
||||||
@ -69,7 +68,21 @@ def get_font_characteristics(raw):
|
|||||||
is_italic = (selection & 0b1) != 0
|
is_italic = (selection & 0b1) != 0
|
||||||
is_bold = (selection & 0b100000) != 0
|
is_bold = (selection & 0b100000) != 0
|
||||||
is_regular = (selection & 0b1000000) != 0
|
is_regular = (selection & 0b1000000) != 0
|
||||||
return weight, is_italic, is_bold, is_regular, fs_type
|
return weight, is_italic, is_bold, is_regular, fs_type, panose
|
||||||
|
|
||||||
|
def panose_to_css_generic_family(panose):
|
||||||
|
proportion = panose[3]
|
||||||
|
if proportion == 9:
|
||||||
|
return 'monospace'
|
||||||
|
family_type = panose[0]
|
||||||
|
if family_type == 3:
|
||||||
|
return 'cursive'
|
||||||
|
if family_type == 4:
|
||||||
|
return 'fantasy'
|
||||||
|
serif_style = panose[1]
|
||||||
|
if serif_style in (11, 12, 13):
|
||||||
|
return 'sans-serif'
|
||||||
|
return 'serif'
|
||||||
|
|
||||||
def decode_name_record(recs):
|
def decode_name_record(recs):
|
||||||
'''
|
'''
|
||||||
@ -225,13 +238,33 @@ def remove_embed_restriction(raw):
|
|||||||
verify_checksums(raw)
|
verify_checksums(raw)
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
|
def get_font_for_text(text, candidate_font_data=None):
|
||||||
|
ok = False
|
||||||
|
if candidate_font_data is not None:
|
||||||
|
from calibre.utils.fonts.free_type import FreeType, FreeTypeError
|
||||||
|
ft = FreeType()
|
||||||
|
try:
|
||||||
|
font = ft.load_font(candidate_font_data)
|
||||||
|
ok = font.supports_text(text)
|
||||||
|
except FreeTypeError:
|
||||||
|
ok = True
|
||||||
|
if not ok:
|
||||||
|
from calibre.utils.fonts import fontconfig
|
||||||
|
family, faces = fontconfig.find_font_for_text(text)
|
||||||
|
if family is not None:
|
||||||
|
f = faces.get('bold', faces['normal'])
|
||||||
|
candidate_font_data = f[2]
|
||||||
|
return candidate_font_data
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
import sys, os
|
import sys, os
|
||||||
for f in sys.argv[1:]:
|
for f in sys.argv[1:]:
|
||||||
print (os.path.basename(f))
|
print (os.path.basename(f))
|
||||||
raw = open(f, 'rb').read()
|
raw = open(f, 'rb').read()
|
||||||
print (get_font_names(raw))
|
print (get_font_names(raw))
|
||||||
print (get_font_characteristics(raw))
|
characs = get_font_characteristics(raw)
|
||||||
|
print (characs)
|
||||||
|
print (panose_to_css_generic_family(characs[5]))
|
||||||
verify_checksums(raw)
|
verify_checksums(raw)
|
||||||
remove_embed_restriction(raw)
|
remove_embed_restriction(raw)
|
||||||
|
|
||||||
|
@ -19,6 +19,23 @@ class WinFonts(object):
|
|||||||
|
|
||||||
def __init__(self, winfonts):
|
def __init__(self, winfonts):
|
||||||
self.w = winfonts
|
self.w = winfonts
|
||||||
|
# Windows thinks the Liberation font files are not valid, so we use
|
||||||
|
# this hack to make them available
|
||||||
|
self.app_font_families = {}
|
||||||
|
|
||||||
|
for f in ('Serif', 'Sans', 'Mono'):
|
||||||
|
base = 'fonts/liberation/Liberation%s-%s.ttf'
|
||||||
|
self.app_font_families['Liberation %s'%f] = m = {}
|
||||||
|
for weight, is_italic in product( (self.w.FW_NORMAL, self.w.FW_BOLD), (False, True) ):
|
||||||
|
name = {(self.w.FW_NORMAL, False):'Regular',
|
||||||
|
(self.w.FW_NORMAL, True):'Italic',
|
||||||
|
(self.w.FW_BOLD, False):'Bold',
|
||||||
|
(self.w.FW_BOLD, True):'BoldItalic'}[(weight,
|
||||||
|
is_italic)]
|
||||||
|
m[(weight, is_italic)] = base%(f, name)
|
||||||
|
|
||||||
|
# import pprint
|
||||||
|
# pprint.pprint(self.app_font_families)
|
||||||
|
|
||||||
def font_families(self):
|
def font_families(self):
|
||||||
names = set()
|
names = set()
|
||||||
@ -30,7 +47,7 @@ class WinFonts(object):
|
|||||||
not font['name'].startswith('@')
|
not font['name'].startswith('@')
|
||||||
):
|
):
|
||||||
names.add(font['name'])
|
names.add(font['name'])
|
||||||
return sorted(names)
|
return sorted(names.union(frozenset(self.app_font_families)))
|
||||||
|
|
||||||
def get_normalized_name(self, is_italic, weight):
|
def get_normalized_name(self, is_italic, weight):
|
||||||
if is_italic:
|
if is_italic:
|
||||||
@ -43,12 +60,18 @@ class WinFonts(object):
|
|||||||
family = type(u'')(family)
|
family = type(u'')(family)
|
||||||
ans = {}
|
ans = {}
|
||||||
for weight, is_italic in product( (self.w.FW_NORMAL, self.w.FW_BOLD), (False, True) ):
|
for weight, is_italic in product( (self.w.FW_NORMAL, self.w.FW_BOLD), (False, True) ):
|
||||||
try:
|
if family in self.app_font_families:
|
||||||
data = self.w.font_data(family, is_italic, weight)
|
m = self.app_font_families[family]
|
||||||
except Exception as e:
|
path = m.get((weight, is_italic), None)
|
||||||
prints('Failed to get font data for font: %s [%s] with error: %s'%
|
if path is None: continue
|
||||||
(family, self.get_normalized_name(is_italic, weight), e))
|
data = P(path, data=True)
|
||||||
continue
|
else:
|
||||||
|
try:
|
||||||
|
data = self.w.font_data(family, is_italic, weight)
|
||||||
|
except Exception as e:
|
||||||
|
prints('Failed to get font data for font: %s [%s] with error: %s'%
|
||||||
|
(family, self.get_normalized_name(is_italic, weight), e))
|
||||||
|
continue
|
||||||
|
|
||||||
ok, sig = is_truetype_font(data)
|
ok, sig = is_truetype_font(data)
|
||||||
if not ok:
|
if not ok:
|
||||||
@ -122,7 +145,7 @@ def test_ttf_reading():
|
|||||||
get_font_characteristics(raw)
|
get_font_characteristics(raw)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def test():
|
||||||
base = os.path.abspath(__file__)
|
base = os.path.abspath(__file__)
|
||||||
d = os.path.dirname
|
d = os.path.dirname
|
||||||
pluginsd = os.path.join(d(d(d(base))), 'plugins')
|
pluginsd = os.path.join(d(d(d(base))), 'plugins')
|
||||||
@ -143,3 +166,5 @@ if __name__ == '__main__':
|
|||||||
prints(' ', font, data[0], data[1], len(data[2]))
|
prints(' ', font, data[0], data[1], len(data[2]))
|
||||||
print ()
|
print ()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
|
36
src/calibre/utils/fonts/woff/__init__.py
Normal file
36
src/calibre/utils/fonts/woff/__init__.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/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'
|
||||||
|
|
||||||
|
from calibre.constants import plugins
|
||||||
|
|
||||||
|
def get_woff():
|
||||||
|
woff, woff_err = plugins['woff']
|
||||||
|
if woff_err:
|
||||||
|
raise RuntimeError('Failed to load the WOFF plugin: %s'%woff_err)
|
||||||
|
return woff
|
||||||
|
|
||||||
|
def to_woff(raw):
|
||||||
|
woff = get_woff()
|
||||||
|
return woff.to_woff(raw)
|
||||||
|
|
||||||
|
def from_woff(raw):
|
||||||
|
woff = get_woff()
|
||||||
|
return woff.from_woff(raw)
|
||||||
|
|
||||||
|
def test():
|
||||||
|
sfnt = P('fonts/calibreSymbols.otf', data=True)
|
||||||
|
woff = to_woff(sfnt)
|
||||||
|
recon = from_woff(woff)
|
||||||
|
if recon != sfnt:
|
||||||
|
raise ValueError('WOFF roundtrip resulted in different sfnt')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
|
|
||||||
|
|
108
src/calibre/utils/fonts/woff/main.c
Normal file
108
src/calibre/utils/fonts/woff/main.c
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* main.c
|
||||||
|
* Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define _UNICODE
|
||||||
|
#define UNICODE
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
#include "woff.h"
|
||||||
|
|
||||||
|
static PyObject *WOFFError = NULL;
|
||||||
|
|
||||||
|
static PyObject* woff_err(uint32_t status) {
|
||||||
|
const char *msg;
|
||||||
|
switch(status) {
|
||||||
|
case eWOFF_out_of_memory:
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
case eWOFF_invalid:
|
||||||
|
msg = "Invalid input data"; break;
|
||||||
|
case eWOFF_compression_failure:
|
||||||
|
msg = "Compression failed"; break;
|
||||||
|
case eWOFF_bad_signature:
|
||||||
|
msg = "Bad font signature"; break;
|
||||||
|
case eWOFF_buffer_too_small:
|
||||||
|
msg = "Buffer too small"; break;
|
||||||
|
case eWOFF_bad_parameter:
|
||||||
|
msg = "Bad parameter"; break;
|
||||||
|
case eWOFF_illegal_order:
|
||||||
|
msg = "Illegal order of WOFF chunks"; break;
|
||||||
|
default:
|
||||||
|
msg = "Unknown Error";
|
||||||
|
}
|
||||||
|
PyErr_SetString(WOFFError, msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
to_woff(PyObject *self, PyObject *args) {
|
||||||
|
const char *sfnt;
|
||||||
|
char *woff = NULL;
|
||||||
|
Py_ssize_t sz;
|
||||||
|
uint32_t wofflen = 0, status = eWOFF_ok;
|
||||||
|
PyObject *ans;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s#", &sfnt, &sz)) return NULL;
|
||||||
|
|
||||||
|
woff = (char*)woffEncode((uint8_t*)sfnt, sz, 0, 0, &wofflen, &status);
|
||||||
|
|
||||||
|
if (WOFF_FAILURE(status) || woff == NULL) return woff_err(status);
|
||||||
|
|
||||||
|
ans = Py_BuildValue("s#", woff, wofflen);
|
||||||
|
free(woff);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
from_woff(PyObject *self, PyObject *args) {
|
||||||
|
const char *woff;
|
||||||
|
char *sfnt;
|
||||||
|
Py_ssize_t sz;
|
||||||
|
uint32_t sfntlen = 0, status = eWOFF_ok;
|
||||||
|
PyObject *ans;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s#", &woff, &sz)) return NULL;
|
||||||
|
|
||||||
|
sfnt = (char*)woffDecode((uint8_t*)woff, sz, &sfntlen, &status);
|
||||||
|
|
||||||
|
if (WOFF_FAILURE(status) || sfnt == NULL) return woff_err(status);
|
||||||
|
ans = Py_BuildValue("s#", sfnt, sfntlen);
|
||||||
|
free(sfnt);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
PyMethodDef methods[] = {
|
||||||
|
{"to_woff", (PyCFunction)to_woff, METH_VARARGS,
|
||||||
|
"to_woff(bytestring) -> Convert the sfnt data in bytestring to WOFF format (returned as a bytestring)."
|
||||||
|
},
|
||||||
|
|
||||||
|
{"from_woff", (PyCFunction)from_woff, METH_VARARGS,
|
||||||
|
"from_woff(bytestring) -> Convert the woff data in bytestring to SFNT format (returned as a bytestring)."
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
initwoff(void) {
|
||||||
|
PyObject *m;
|
||||||
|
|
||||||
|
m = Py_InitModule3(
|
||||||
|
"woff", methods,
|
||||||
|
"Convert to/from the WOFF<->sfnt font formats"
|
||||||
|
);
|
||||||
|
if (m == NULL) return;
|
||||||
|
|
||||||
|
WOFFError = PyErr_NewException((char*)"woff.WOFFError", NULL, NULL);
|
||||||
|
if (WOFFError == NULL) return;
|
||||||
|
PyModule_AddObject(m, "WOFFError", WOFFError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
151
src/calibre/utils/fonts/woff/woff-private.h
Normal file
151
src/calibre/utils/fonts/woff/woff-private.h
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is WOFF font packaging code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Jonathan Kew <jfkthame@gmail.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef WOFF_PRIVATE_H_
|
||||||
|
#define WOFF_PRIVATE_H_
|
||||||
|
|
||||||
|
#include "woff.h"
|
||||||
|
|
||||||
|
/* private definitions used in the WOFF encoder/decoder functions */
|
||||||
|
|
||||||
|
/* create an OT tag from 4 characters */
|
||||||
|
#define TAG(a,b,c,d) ((a)<<24 | (b)<<16 | (c)<<8 | (d))
|
||||||
|
|
||||||
|
#define WOFF_SIGNATURE TAG('w','O','F','F')
|
||||||
|
|
||||||
|
#define SFNT_VERSION_CFF TAG('O','T','T','O')
|
||||||
|
#define SFNT_VERSION_TT 0x00010000
|
||||||
|
#define SFNT_VERSION_true TAG('t','r','u','e')
|
||||||
|
|
||||||
|
#define TABLE_TAG_DSIG TAG('D','S','I','G')
|
||||||
|
#define TABLE_TAG_head TAG('h','e','a','d')
|
||||||
|
#define TABLE_TAG_bhed TAG('b','h','e','d')
|
||||||
|
|
||||||
|
#define SFNT_CHECKSUM_CALC_CONST 0xB1B0AFBAU /* from the TT/OT spec */
|
||||||
|
|
||||||
|
#ifdef WOFF_MOZILLA_CLIENT
|
||||||
|
# include <prnetdb.h>
|
||||||
|
# define READ32BE(x) PR_ntohl(x)
|
||||||
|
# define READ16BE(x) PR_ntohs(x)
|
||||||
|
#else
|
||||||
|
/* These macros to read values as big-endian only work on "real" variables,
|
||||||
|
not general expressions, because of the use of &(x), but they are
|
||||||
|
designed to work on both BE and LE machines without the need for a
|
||||||
|
configure check. For production code, we might want to replace this
|
||||||
|
with something more efficient. */
|
||||||
|
/* read a 32-bit BigEndian value */
|
||||||
|
# define READ32BE(x) ( ( (uint32_t) ((uint8_t*)&(x))[0] << 24 ) + \
|
||||||
|
( (uint32_t) ((uint8_t*)&(x))[1] << 16 ) + \
|
||||||
|
( (uint32_t) ((uint8_t*)&(x))[2] << 8 ) + \
|
||||||
|
(uint32_t) ((uint8_t*)&(x))[3] )
|
||||||
|
/* read a 16-bit BigEndian value */
|
||||||
|
# define READ16BE(x) ( ( (uint16_t) ((uint8_t*)&(x))[0] << 8 ) + \
|
||||||
|
(uint16_t) ((uint8_t*)&(x))[1] )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t version;
|
||||||
|
uint16_t numTables;
|
||||||
|
uint16_t searchRange;
|
||||||
|
uint16_t entrySelector;
|
||||||
|
uint16_t rangeShift;
|
||||||
|
} sfntHeader;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t tag;
|
||||||
|
uint32_t checksum;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
} sfntDirEntry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t signature;
|
||||||
|
uint32_t flavor;
|
||||||
|
uint32_t length;
|
||||||
|
uint16_t numTables;
|
||||||
|
uint16_t reserved;
|
||||||
|
uint32_t totalSfntSize;
|
||||||
|
uint16_t majorVersion;
|
||||||
|
uint16_t minorVersion;
|
||||||
|
uint32_t metaOffset;
|
||||||
|
uint32_t metaCompLen;
|
||||||
|
uint32_t metaOrigLen;
|
||||||
|
uint32_t privOffset;
|
||||||
|
uint32_t privLen;
|
||||||
|
} woffHeader;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t tag;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t compLen;
|
||||||
|
uint32_t origLen;
|
||||||
|
uint32_t checksum;
|
||||||
|
} woffDirEntry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t fontRevision;
|
||||||
|
uint32_t checkSumAdjustment;
|
||||||
|
uint32_t magicNumber;
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t unitsPerEm;
|
||||||
|
uint32_t created[2];
|
||||||
|
uint32_t modified[2];
|
||||||
|
int16_t xMin;
|
||||||
|
int16_t yMin;
|
||||||
|
int16_t xMax;
|
||||||
|
int16_t yMax;
|
||||||
|
uint16_t macStyle;
|
||||||
|
uint16_t lowestRecPpem;
|
||||||
|
int16_t fontDirectionHint;
|
||||||
|
int16_t indexToLocFormat;
|
||||||
|
int16_t glyphDataFormat;
|
||||||
|
} sfntHeadTable;
|
||||||
|
|
||||||
|
#define HEAD_TABLE_SIZE 54 /* sizeof(sfntHeadTable) may report 56 because of alignment */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t offset;
|
||||||
|
uint16_t oldIndex;
|
||||||
|
uint16_t newIndex;
|
||||||
|
} tableOrderRec;
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#endif
|
1170
src/calibre/utils/fonts/woff/woff.c
Normal file
1170
src/calibre/utils/fonts/woff/woff.c
Normal file
File diff suppressed because it is too large
Load Diff
211
src/calibre/utils/fonts/woff/woff.h
Normal file
211
src/calibre/utils/fonts/woff/woff.h
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is WOFF font packaging code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Jonathan Kew <jfkthame@gmail.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef WOFF_H_
|
||||||
|
#define WOFF_H_
|
||||||
|
|
||||||
|
/* API for the WOFF encoder and decoder */
|
||||||
|
|
||||||
|
#ifdef _MSC_VER /* MS VC lacks inttypes.h
|
||||||
|
but we can make do with a few definitons here */
|
||||||
|
typedef char int8_t;
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
#else
|
||||||
|
#include <inttypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h> /* only for FILE, needed for woffPrintStatus */
|
||||||
|
|
||||||
|
/* error codes returned in the status parameter of WOFF functions */
|
||||||
|
enum {
|
||||||
|
/* Success */
|
||||||
|
eWOFF_ok = 0,
|
||||||
|
|
||||||
|
/* Errors: no valid result returned */
|
||||||
|
eWOFF_out_of_memory = 1, /* malloc or realloc failed */
|
||||||
|
eWOFF_invalid = 2, /* invalid input file (e.g., bad offset) */
|
||||||
|
eWOFF_compression_failure = 3, /* error in zlib call */
|
||||||
|
eWOFF_bad_signature = 4, /* unrecognized file signature */
|
||||||
|
eWOFF_buffer_too_small = 5, /* the provided buffer is too small */
|
||||||
|
eWOFF_bad_parameter = 6, /* bad parameter (e.g., null source ptr) */
|
||||||
|
eWOFF_illegal_order = 7, /* improperly ordered chunks in WOFF font */
|
||||||
|
|
||||||
|
/* Warnings: call succeeded but something odd was noticed.
|
||||||
|
Multiple warnings may be OR'd together. */
|
||||||
|
eWOFF_warn_unknown_version = 0x0100, /* unrecognized version of sfnt,
|
||||||
|
not standard TrueType or CFF */
|
||||||
|
eWOFF_warn_checksum_mismatch = 0x0200, /* bad checksum, use with caution;
|
||||||
|
any DSIG will be invalid */
|
||||||
|
eWOFF_warn_misaligned_table = 0x0400, /* table not long-aligned; fixing,
|
||||||
|
but DSIG will be invalid */
|
||||||
|
eWOFF_warn_trailing_data = 0x0800, /* trailing junk discarded,
|
||||||
|
any DSIG may be invalid */
|
||||||
|
eWOFF_warn_unpadded_table = 0x1000, /* sfnt not correctly padded,
|
||||||
|
any DSIG may be invalid */
|
||||||
|
eWOFF_warn_removed_DSIG = 0x2000 /* removed digital signature
|
||||||
|
while fixing checksum errors */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Note: status parameters must be initialized to eWOFF_ok before calling
|
||||||
|
WOFF functions. If the status parameter contains an error code,
|
||||||
|
functions will return immediately. */
|
||||||
|
|
||||||
|
#define WOFF_SUCCESS(status) (((uint32_t)(status) & 0xff) == eWOFF_ok)
|
||||||
|
#define WOFF_FAILURE(status) (!WOFF_SUCCESS(status))
|
||||||
|
#define WOFF_WARNING(status) ((uint32_t)(status) & ~0xff)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WOFF_DISABLE_ENCODING
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Returns a new malloc() block containing the encoded data, or NULL on error;
|
||||||
|
* caller should free() this when finished with it.
|
||||||
|
* Returns length of the encoded data in woffLen.
|
||||||
|
* The new WOFF has no metadata or private block;
|
||||||
|
* see the following functions to update these elements.
|
||||||
|
*/
|
||||||
|
const uint8_t * woffEncode(const uint8_t * sfntData, uint32_t sfntLen,
|
||||||
|
uint16_t majorVersion, uint16_t minorVersion,
|
||||||
|
uint32_t * woffLen, uint32_t * status);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Add the given metadata block to the WOFF font, replacing any existing
|
||||||
|
* metadata block. The block will be zlib-compressed.
|
||||||
|
* Metadata is required to be valid XML (use of UTF-8 is recommended),
|
||||||
|
* though this function does not currently check this.
|
||||||
|
* The woffData pointer must be a malloc() block (typically from woffEncode);
|
||||||
|
* it will be freed by this function and a new malloc() block will be returned.
|
||||||
|
* Returns NULL if an error occurs, in which case the original WOFF is NOT freed.
|
||||||
|
*/
|
||||||
|
const uint8_t * woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen,
|
||||||
|
const uint8_t * metaData, uint32_t metaLen,
|
||||||
|
uint32_t * status);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Add the given private data block to the WOFF font, replacing any existing
|
||||||
|
* private block. The block will NOT be zlib-compressed.
|
||||||
|
* Private data may be any arbitrary block of bytes; it may be externally
|
||||||
|
* compressed by the client if desired.
|
||||||
|
* The woffData pointer must be a malloc() block (typically from woffEncode);
|
||||||
|
* it will be freed by this function and a new malloc() block will be returned.
|
||||||
|
* Returns NULL if an error occurs, in which case the original WOFF is NOT freed.
|
||||||
|
*/
|
||||||
|
const uint8_t * woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen,
|
||||||
|
const uint8_t * privData, uint32_t privLen,
|
||||||
|
uint32_t * status);
|
||||||
|
|
||||||
|
#endif /* WOFF_DISABLE_ENCODING */
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Returns the size of buffer needed to decode the font (or zero on error).
|
||||||
|
*/
|
||||||
|
uint32_t woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen,
|
||||||
|
uint32_t * pStatus);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Decodes WOFF font to a caller-supplied buffer of size bufferLen.
|
||||||
|
* Returns the actual size of the decoded sfnt data in pActualSfntLen
|
||||||
|
* (must be <= bufferLen, otherwise an error will be returned).
|
||||||
|
*/
|
||||||
|
void woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen,
|
||||||
|
uint8_t * sfntData, uint32_t bufferLen,
|
||||||
|
uint32_t * pActualSfntLen, uint32_t * pStatus);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Returns a new malloc() block containing the decoded data, or NULL on error;
|
||||||
|
* caller should free() this when finished with it.
|
||||||
|
* Returns length of the decoded data in sfntLen.
|
||||||
|
*/
|
||||||
|
const uint8_t * woffDecode(const uint8_t * woffData, uint32_t woffLen,
|
||||||
|
uint32_t * sfntLen, uint32_t * status);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Returns a new malloc() block containing the metadata from the WOFF font,
|
||||||
|
* or NULL if an error occurs or no metadata is present.
|
||||||
|
* Length of the metadata is returned in metaLen.
|
||||||
|
* The metadata is decompressed before returning.
|
||||||
|
*/
|
||||||
|
const uint8_t * woffGetMetadata(const uint8_t * woffData, uint32_t woffLen,
|
||||||
|
uint32_t * metaLen, uint32_t * status);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Returns a new malloc() block containing the private data from the WOFF font,
|
||||||
|
* or NULL if an error occurs or no private data is present.
|
||||||
|
* Length of the private data is returned in privLen.
|
||||||
|
*/
|
||||||
|
const uint8_t * woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen,
|
||||||
|
uint32_t * privLen, uint32_t * status);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Returns the font version numbers from the WOFF font in the major and minor
|
||||||
|
* parameters.
|
||||||
|
* Check the status result to know if the function succeeded.
|
||||||
|
*/
|
||||||
|
void woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen,
|
||||||
|
uint16_t * major, uint16_t * minor,
|
||||||
|
uint32_t * status);
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Utility to print warning and/or error status to the specified FILE*.
|
||||||
|
* The prefix string will be prepended to each line (ok to pass NULL if no
|
||||||
|
* prefix is wanted).
|
||||||
|
* (Provides terse English messages only, not intended for end-user display;
|
||||||
|
* user-friendly tools should map the status codes to their own messages.)
|
||||||
|
*/
|
||||||
|
void woffPrintStatus(FILE * f, uint32_t status, const char * prefix);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -10,7 +10,7 @@ import os
|
|||||||
from calibre.utils.magick import Image, DrawingWand, create_canvas
|
from calibre.utils.magick import Image, DrawingWand, create_canvas
|
||||||
from calibre.constants import __appname__, __version__
|
from calibre.constants import __appname__, __version__
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks
|
||||||
from calibre import fit_image
|
from calibre import fit_image, force_unicode
|
||||||
|
|
||||||
def _data_to_image(data):
|
def _data_to_image(data):
|
||||||
if isinstance(data, Image):
|
if isinstance(data, Image):
|
||||||
@ -166,12 +166,9 @@ def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0,
|
|||||||
return canvas.export(fmt)
|
return canvas.export(fmt)
|
||||||
|
|
||||||
def create_text_wand(font_size, font_path=None):
|
def create_text_wand(font_size, font_path=None):
|
||||||
if font_path is None:
|
|
||||||
font_path = tweaks['generate_cover_title_font']
|
|
||||||
if font_path is None:
|
|
||||||
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
|
||||||
ans = DrawingWand()
|
ans = DrawingWand()
|
||||||
ans.font = font_path
|
if font_path is not None:
|
||||||
|
ans.font = font_path
|
||||||
ans.font_size = font_size
|
ans.font_size = font_size
|
||||||
ans.gravity = 'CenterGravity'
|
ans.gravity = 'CenterGravity'
|
||||||
ans.text_alias = True
|
ans.text_alias = True
|
||||||
@ -238,6 +235,17 @@ class TextLine(object):
|
|||||||
def __init__(self, text, font_size, bottom_margin=30, font_path=None):
|
def __init__(self, text, font_size, bottom_margin=30, font_path=None):
|
||||||
self.text, self.font_size, = text, font_size
|
self.text, self.font_size, = text, font_size
|
||||||
self.bottom_margin = bottom_margin
|
self.bottom_margin = bottom_margin
|
||||||
|
if font_path is None:
|
||||||
|
if not isinstance(text, unicode):
|
||||||
|
text = force_unicode(text)
|
||||||
|
from calibre.utils.fonts.utils import get_font_for_text
|
||||||
|
fd = get_font_for_text(text)
|
||||||
|
if fd is not None:
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
pt = PersistentTemporaryFile('.ttf')
|
||||||
|
pt.write(fd)
|
||||||
|
pt.close()
|
||||||
|
font_path = pt.name
|
||||||
self.font_path = font_path
|
self.font_path = font_path
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -549,7 +549,7 @@ def option_parser(usage=_('%prog URL\n\nWhere URL is for example http://google.c
|
|||||||
|
|
||||||
def create_fetcher(options, image_map={}, log=None):
|
def create_fetcher(options, image_map={}, log=None):
|
||||||
if log is None:
|
if log is None:
|
||||||
log = Log()
|
log = Log(level=Log.DEBUG) if options.verbose else Log()
|
||||||
return RecursiveFetcher(options, log, image_map={})
|
return RecursiveFetcher(options, log, image_map={})
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user