sync with Kovid's branch

This commit is contained in:
Tomasz Długosz 2012-11-01 21:42:46 +01:00
commit 4f1d847a75
169 changed files with 22984 additions and 1007 deletions

View File

@ -28,6 +28,12 @@ License: LGPL-2.1+
The full text of the LGPL is distributed as in
/usr/share/common-licenses/LGPL-2.1 on Debian systems.
Files: src/calibre/utils/fonts/woff/*
Copyright: Jonathan Kew?
License: LGPL-2.1
The full text of the LGPL is distributed as in
/usr/share/common-licenses/LGPL-2.1 on Debian systems.
Files: src/calibre/ebooks/hyphenate.py
Copyright: Copyright (C) 1990, 2004, 2005 Gerard D.C. Kuiken.
License: other
@ -41,6 +47,12 @@ License: Apache 2.0
The full text of the Apache 2.0 license is available at:
http://www.apache.org/licenses/LICENSE-2.0
Files: src/sfntly/*
Copyright: Google Inc.
License: Apache 2.0
The full text of the Apache 2.0 license is available at:
http://www.apache.org/licenses/LICENSE-2.0
Files: resources/viewer/mathjax/*
Copyright: Unknown
License: Apache 2.0

View File

@ -557,6 +557,27 @@ There can be two reasons why |app| is showing a empty list of books:
* Your metadata.db file was deleted/corrupted. In this case, you can ask |app| to rebuild the metadata.db from its backups. Right click the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Library maintenance->Restore database. |app| will automatically rebuild metadata.db.
I am getting errors with my calibre library on a networked drive/NAS?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Do not put your calibre library on a networked drive**.
A filesystem is a complex beast. Most network filesystems lack various
filesystem features that |app| uses. Some dont support file locking, some dont
support hardlinking, some are just flaky. Additionally, |app| is a single user
application, if you accidentally run two copies of |app| on the same networked
library, bad things will happen. Finally, different OSes impose different
limitations on filesystems, so if you share your networked drive across OSes,
once again, bad things *will happen*.
Consider using the |app| Content Server to make your books available on other
computers. Run |app| on a single computer and access it via the Content Server
or a Remote Desktop solution.
If you must share the actual library, use a file syncing tool like
DropBox or rsync or Microsoft SkyDrive instead of a networked drive. Even with
these tools there is danger of data corruption/loss, so only do this if you are
willing to live with that risk.
Content From The Web
---------------------
@ -674,7 +695,20 @@ If you still cannot get the installer to work and you are on windows, you can us
My antivirus program claims |app| is a virus/trojan?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Your antivirus program is wrong. Antivirus programs use heuristics, patterns of code that "looks suspicuous" to detect viruses. It's rather like racial profiling. |app| is a completely open source product. You can actually browse the source code yourself (or hire someone to do it for you) to verify that it is not a virus. Please report the false identification to whatever company you buy your antivirus software from. If the antivirus program is preventing you from downloading/installing |app|, disable it temporarily, install |app| and then re-enable it.
The first thing to check is that you are downloading |app| from the official
website: `<http://calibre-ebook.com/download>`_. |app| is a very popular program
and unscrupulous people try to setup websites offering it for download to fool
the unwary.
If you have the official download and your antivirus program is still claiming
|app| is a virus, then, your antivirus program is wrong. Antivirus programs use
heuristics, patterns of code that "look suspicious" to detect viruses. It's
rather like racial profiling. |app| is a completely open source product. You
can actually browse the source code yourself (or hire someone to do it for you)
to verify that it is not a virus. Please report the false identification to
whatever company you buy your antivirus software from. If the antivirus program
is preventing you from downloading/installing |app|, disable it temporarily,
install |app| and then re-enable it.
How do I backup |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,26 @@
from calibre.web.feeds.news import BasicNewsRecipe
class HindustanTimes(BasicNewsRecipe):
title = u'Delcoe Times'
language = 'en'
__author__ = 'Krittika Goyal'
oldest_article = 1 #days
max_articles_per_feed = 25
#encoding = 'cp1252'
use_embedded_content = False
no_stylesheets = True
auto_cleanup = True
feeds = [
('News',
'http://www.delcotimes.com/?rss=news'),
('Sports',
'http://www.delcotimes.com/?rss=sports'),
('Business',
'http://business-news.thestreet.com/the-delaware-county-daily-times/rss/109393'),
('Entertainment',
'http://www.delcotimes.com/?rss=entertainment'),
]

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__copyright__ = '2010-2011, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2010-2012, Darko Miletic <darko.miletic at gmail.com>'
'''
www.ft.com/uk-edition
'''
@ -42,18 +42,23 @@ class FinancialTimes(BasicNewsRecipe):
def get_browser(self):
br = BasicNewsRecipe.get_browser()
br.open(self.INDEX)
br.open(self.LOGIN)
br.select_form(name='loginForm')
br['username'] = self.username
br['password'] = self.password
br.submit()
if self.username is not None and self.password is not None:
br.open(self.LOGIN2)
br.select_form(name='loginForm')
br['username'] = self.username
br['password'] = self.password
br.submit()
return br
keep_only_tags = [
dict(name='div', attrs={'class':['fullstory fullstoryHeader', 'ft-story-header']})
,dict(name='div', attrs={'class':'standfirst'})
,dict(name='div', attrs={'id' :'storyContent'})
,dict(name='div', attrs={'class':['ft-story-body','index-detail']})
dict(name='div' , attrs={'class':['fullstory fullstoryHeader', 'ft-story-header']})
,dict(name='div' , attrs={'class':'standfirst'})
,dict(name='div' , attrs={'id' :'storyContent'})
,dict(name='div' , attrs={'class':['ft-story-body','index-detail']})
,dict(name='h2' , attrs={'class':'entry-title'} )
,dict(name='span', attrs={'class':lambda x: x and 'posted-on' in x.split()} )
,dict(name='span', attrs={'class':'author_byline'} )
,dict(name='div' , attrs={'class':'entry-content'} )
]
remove_tags = [
dict(name='div', attrs={'id':'floating-con'})
@ -82,21 +87,20 @@ class FinancialTimes(BasicNewsRecipe):
if self.test and count > 2:
return articles
rawlink = item['href']
if rawlink.startswith('http://'):
url = rawlink
else:
url = self.PREFIX + rawlink
url = rawlink
if not rawlink.startswith('http://'):
url = self.PREFIX + rawlink
try:
urlverified = self.browser.open_novisit(url).geturl() # resolve redirect.
except:
continue
continue
title = self.tag_to_string(item)
date = strftime(self.timefmt)
articles.append({
'title' :title
,'date' :date
,'url' :urlverified
,'description':''
'title' :title
,'date' :date
,'url' :urlverified
,'description':''
})
return articles
@ -108,21 +112,20 @@ class FinancialTimes(BasicNewsRecipe):
wide = soup.find('div',attrs={'class':'wide'})
if not wide:
return feeds
strest = wide.findAll('h3', attrs={'class':'section'})
if not strest:
allsections = wide.findAll(attrs={'class':lambda x: x and 'footwell' in x.split()})
if not allsections:
return feeds
st = wide.findAll('h4',attrs={'class':'section-no-arrow'})
if st:
st.extend(strest)
count = 0
for item in st:
for item in allsections:
count = count + 1
if self.test and count > 2:
return feeds
ftitle = self.tag_to_string(item)
fitem = item.h3
if not fitem:
fitem = item.h4
ftitle = self.tag_to_string(fitem)
self.report_progress(0, _('Fetching feed')+' %s...'%(ftitle))
if item.parent.ul is not None:
feedarts = self.get_artlinks(item.parent.ul)
feedarts = self.get_artlinks(item.ul)
feeds.append((ftitle,feedarts))
return feeds
@ -156,7 +159,7 @@ class FinancialTimes(BasicNewsRecipe):
def get_cover_url(self):
cdate = datetime.date.today()
if cdate.isoweekday() == 7:
cdate -= datetime.timedelta(days=1)
cdate -= datetime.timedelta(days=1)
return cdate.strftime('http://specials.ft.com/vtf_pdf/%d%m%y_FRONT1_LON.pdf')
def get_obfuscated_article(self, url):
@ -169,10 +172,11 @@ class FinancialTimes(BasicNewsRecipe):
except:
print "Retrying download..."
count += 1
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write(html)
self.temp_files[-1].close()
return self.temp_files[-1].name
tfile = PersistentTemporaryFile('_fa.html')
tfile.write(html)
tfile.close()
self.temp_files.append(tfile)
return tfile.name
def cleanup(self):
self.browser.open('https://registration.ft.com/registration/login/logout?location=')
self.browser.open('https://registration.ft.com/registration/login/logout?location=')

View File

@ -27,7 +27,7 @@ class ScienceAAS(BasicNewsRecipe):
br = BasicNewsRecipe.get_browser()
if self.username is not None and self.password is not None:
br.open(self.LOGIN)
br.select_form(nr=1)
br.select_form(nr=0)
br['username'] = self.username
br['code' ] = self.password
br.submit()

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -11,6 +11,7 @@ let g:syntastic_cpp_include_dirs = [
\'/usr/include/freetype2',
\'/usr/include/fontconfig',
\'src/qtcurve/common', 'src/qtcurve',
\'src/sfntly/src', 'src/sfntly/src/sample',
\'/usr/include/ImageMagick',
\]
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs

View File

@ -87,8 +87,6 @@ ft_libs = []
ft_inc_dirs = []
jpg_libs = []
jpg_lib_dirs = []
fc_inc = '/usr/include/fontconfig'
fc_lib = '/usr/lib'
podofo_inc = '/usr/include/podofo'
podofo_lib = '/usr/lib'
chmlib_inc_dirs = chmlib_lib_dirs = []
@ -107,8 +105,6 @@ if iswindows:
'source', 'i18n')]
icu_lib_dirs = [os.path.join(ICU, 'source', 'lib')]
sqlite_inc_dirs = [sw_inc_dir]
fc_inc = os.path.join(sw_inc_dir, 'fontconfig')
fc_lib = sw_lib_dir
chmlib_inc_dirs = consolidate('CHMLIB_INC_DIR', os.path.join(prefix,
'build', 'chmlib-0.40', 'src'))
chmlib_lib_dirs = consolidate('CHMLIB_LIB_DIR', os.path.join(prefix,
@ -131,8 +127,6 @@ if iswindows:
podofo_inc = os.path.join(sw_inc_dir, 'podofo')
podofo_lib = sw_lib_dir
elif isosx:
fc_inc = '/sw/include/fontconfig'
fc_lib = '/sw/lib'
podofo_inc = '/sw/podofo'
podofo_lib = '/sw/lib'
magick_inc_dirs = consolidate('MAGICK_INC',
@ -166,13 +160,6 @@ else:
ft_libs = pkgconfig_libs('freetype2', '', '')
fc_inc = os.environ.get('FC_INC_DIR', fc_inc)
fc_lib = os.environ.get('FC_LIB_DIR', fc_lib)
fc_error = None if os.path.exists(os.path.join(fc_inc, 'fontconfig.h')) else \
('fontconfig header files not found on your system. '
'Try setting the FC_INC_DIR and FC_LIB_DIR environment '
'variables.')
magick_error = None
if not magick_inc_dirs or not os.path.exists(os.path.join(magick_inc_dirs[0],
'wand')):

View File

@ -13,12 +13,13 @@ from multiprocessing import cpu_count
from PyQt4.pyqtconfig import QtGuiModuleMakefile
from setup import Command, islinux, isbsd, isosx, SRC, iswindows
from setup.build_environment import (fc_inc, fc_lib, chmlib_inc_dirs, fc_error,
from setup.build_environment import (chmlib_inc_dirs,
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,
magick_libs, chmlib_lib_dirs, sqlite_inc_dirs, icu_inc_dirs,
icu_lib_dirs, win_ddk_lib_dirs, ft_libs, ft_lib_dirs, ft_inc_dirs,
zlib_libs, zlib_lib_dirs, zlib_inc_dirs)
from setup.sfntly import SfntlyBuilderMixin
MT
isunix = islinux or isosx or isbsd
@ -48,6 +49,9 @@ class Extension(object):
self.optional = kwargs.get('optional', False)
self.needs_ddk = kwargs.get('needs_ddk', False)
def preflight(self, obj_dir, compiler, linker, builder, cflags, ldflags):
pass
reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp'))
reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h'))
@ -59,9 +63,26 @@ if isosx:
icu_libs = ['icucore']
icu_cflags = ['-DU_DISABLE_RENAMING'] # Needed to use system libicucore.dylib
class SfntlyExtension(Extension, SfntlyBuilderMixin):
def __init__(self, *args, **kwargs):
Extension.__init__(self, *args, **kwargs)
SfntlyBuilderMixin.__init__(self)
def preflight(self, *args, **kwargs):
self(*args, **kwargs)
extensions = [
SfntlyExtension('sfntly',
['calibre/utils/fonts/sfntly.cpp'],
headers= ['calibre/utils/fonts/sfntly.h'],
libraries=icu_libs,
lib_dirs=icu_lib_dirs,
inc_dirs=icu_inc_dirs,
cflags=icu_cflags
),
Extension('speedup',
['calibre/utils/speedup.c'],
),
@ -122,13 +143,6 @@ extensions = [
libraries=ft_libs,
lib_dirs=ft_lib_dirs),
Extension('fontconfig',
['calibre/utils/fonts/fontconfig.c'],
inc_dirs = [fc_inc],
libraries=['fontconfig'],
lib_dirs=[fc_lib],
error=fc_error),
Extension('woff',
['calibre/utils/fonts/woff/main.c',
'calibre/utils/fonts/woff/woff.c'],
@ -243,6 +257,7 @@ if isunix:
cc = os.environ.get('CC', 'gcc')
cxx = os.environ.get('CXX', 'g++')
cflags = os.environ.get('OVERRIDE_CFLAGS',
# '-Wall -DNDEBUG -ggdb -fno-strict-aliasing -pipe')
'-O3 -Wall -DNDEBUG -fno-strict-aliasing -pipe')
cflags = shlex.split(cflags) + ['-fPIC']
ldflags = os.environ.get('OVERRIDE_LDFLAGS', '-Wall')
@ -305,9 +320,6 @@ class Build(Command):
CFLAGS - Extra compiler flags
LDFLAGS - Extra linker flags
FC_INC_DIR - fontconfig header files
FC_LIB_DIR - fontconfig library
POPPLER_INC_DIR - poppler header files
POPPLER_LIB_DIR - poppler-qt4 library
@ -373,8 +385,9 @@ class Build(Command):
compiler = cxx if ext.needs_cxx else cc
linker = msvc.linker if iswindows else compiler
objects = []
einc = self.inc_dirs_to_cflags(ext.inc_dirs)
obj_dir = self.j(self.obj_dir, ext.name)
ext.preflight(obj_dir, compiler, linker, self, cflags, ldflags)
einc = self.inc_dirs_to_cflags(ext.inc_dirs)
if ext.needs_ddk:
ddk_flags = ['-I'+x for x in win_ddk]
cflags.extend(ddk_flags)
@ -395,7 +408,7 @@ class Build(Command):
dest = self.dest(ext)
elib = self.lib_dirs_to_ldflags(ext.lib_dirs)
xlib = self.libraries_to_ldflags(ext.libraries)
if self.newer(dest, objects):
if self.newer(dest, objects+ext.extra_objs):
print 'Linking', ext.name
cmd = [linker]
if iswindows:

View File

@ -15,8 +15,8 @@ from setup import __version__ as VERSION, __appname__ as APPNAME, basenames, \
LICENSE = open('LICENSE', 'rb').read()
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick'
ENV = dict(
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
FC_CONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf',
FONTCONFIG_PATH='@executable_path/../Resources/fonts',
FONTCONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf',
MAGICK_CONFIGURE_PATH=MAGICK_HOME+'/config',
MAGICK_CODER_MODULE_PATH=MAGICK_HOME+'/modules-Q16/coders',
MAGICK_CODER_FILTER_PATH=MAGICK_HOME+'/modules-Q16/filter',
@ -379,7 +379,7 @@ class Py2App(object):
@flush
def add_poppler(self):
info('\nAdding poppler')
for x in ('libpoppler.27.dylib',):
for x in ('libpoppler.28.dylib',):
self.install_dylib(os.path.join(SW, 'lib', x))
for x in ('pdftohtml', 'pdftoppm', 'pdfinfo'):
self.install_dylib(os.path.join(SW, 'bin', x), False)
@ -411,7 +411,6 @@ class Py2App(object):
raw = open(fc, 'rb').read()
raw = raw.replace('<dir>/usr/share/fonts</dir>', '''\
<dir>/Library/Fonts</dir>
<dir>/Network/Library/Fonts</dir>
<dir>/System/Library/Fonts</dir>
<dir>/usr/X11R6/lib/X11/fonts</dir>
<dir>/usr/share/fonts</dir>

View File

@ -281,8 +281,6 @@ class Win32Freeze(Command, WixMixIn):
for x in ('zlib1.dll', 'libxml2.dll'):
shutil.copy2(self.j(bindir, x+'.manifest'), self.dll_dir)
shutil.copytree(os.path.join(SW, 'etc', 'fonts'),
os.path.join(self.base, 'fontconfig'))
# Copy ImageMagick
for pat in ('*.dll', '*.xml'):
for f in glob.glob(self.j(IMAGEMAGICK, pat)):

View File

@ -276,27 +276,6 @@ cp build/kdewin32-msvc-0.3.9/build/bin/Release/*.exp lib/
cp -r build/kdewin32-msvc-0.3.9/include/msvc/ include/
cp build/kdewin32-msvc-0.3.9/include/*.h include/
fontconfig
---------------
Get it from http://www.winkde.org/pub/kde/ports/win32/repository/win32libs/
mkdir build
Remove subdirectory test from the bottom of CMakeLists.txt
run cmake
Set build type to release and project config to dll
Right click on the fontconfig project and select properties. Add sw/include/msvc to the include paths
Build only fontconfig
cp build/fontconfig-msvc-2.4.2-3/build/src/Release/*.dll bin
cp build/fontconfig-msvc-2.4.2-3/build/src/Release/*.lib lib
cp build/fontconfig-msvc-2.4.2-3/build/src/Release/*.exp lib
cp -r build/fontconfig-msvc-2.4.2-3/fontconfig/ include/
Also install the etc files from the font-config-bin archive from kde win32libs
It contains correct fonts.conf etc.
poppler
-------------

93
setup/sfntly.py Normal file
View File

@ -0,0 +1,93 @@
#!/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 shlex, os
from glob import glob
from setup import iswindows
class Group(object):
def __init__(self, name, base, build_base, cflags):
self.name = name
self.cflags = cflags
self.headers = frozenset(glob(os.path.join(base, '*.h')))
self.src_files = glob(os.path.join(base, '*.cc'))
self.bdir = os.path.abspath(os.path.join(build_base, name))
if not os.path.exists(self.bdir):
os.makedirs(self.bdir)
self.objects = [os.path.join(self.bdir,
os.path.basename(x).rpartition('.')[0] + ('.obj' if iswindows else
'.o')) for x in self.src_files]
def __call__(self, compiler, linker, builder, all_headers):
for src, obj in zip(self.src_files, self.objects):
if builder.newer(obj, [src] + list(all_headers)):
sinc = ['/Tp'+src] if iswindows else ['-c', src]
oinc = ['/Fo'+obj] if iswindows else ['-o', obj]
cmd = [compiler] + self.cflags + sinc + oinc
builder.info(' '.join(cmd))
builder.check_call(cmd)
class SfntlyBuilderMixin(object):
def __init__(self):
self.sfntly_cflags = [
'-DSFNTLY_NO_EXCEPTION',
'-DSFNTLY_EXPERIMENTAL',
]
if iswindows:
self.sfntly_cflags += [
'-D_UNICODE', '-DUNICODE',
] + shlex.split('/W4 /WX /Gm- /Gy /GR-')
self.cflags += ['-DWIN32']
else:
# Possibly add -fno-inline (slower, but more robust)
self.sfntly_cflags += [
'-Werror',
'-fno-exceptions',
]
if len(self.libraries) > 1:
self.libraries = ['icuuc']
if not iswindows:
self.libraries += ['pthread']
def __call__(self, obj_dir, compiler, linker, builder, cflags, ldflags):
self.sfntly_build_dir = os.path.join(obj_dir, 'sfntly')
if '/Ox' in cflags:
cflags.remove('/Ox')
if '-O3' in cflags:
cflags.remove('-O3')
if '/W3' in cflags:
cflags.remove('/W3')
if '-ggdb' not in cflags:
cflags.insert(0, '/O2' if iswindows else '-O2')
groups = []
all_headers = set()
all_objects = []
src_dir = self.absolutize([os.path.join('sfntly', 'src')])[0]
inc_dirs = [src_dir]
self.inc_dirs += inc_dirs
inc_flags = builder.inc_dirs_to_cflags(self.inc_dirs)
for loc in ('', 'port', 'data', 'math', 'table', 'table/bitmap',
'table/core', 'table/truetype'):
path = os.path.join(src_dir, 'sfntly', *loc.split('/'))
gr = Group(loc, path, self.sfntly_build_dir, cflags+
inc_flags+self.sfntly_cflags+self.cflags)
groups.append(gr)
all_headers |= gr.headers
all_objects.extend(gr.objects)
for group in groups:
group(compiler, linker, builder, all_headers)
self.extra_objs = all_objects

View File

@ -690,29 +690,6 @@ def remove_bracketed_text(src,
buf.append(char)
return u''.join(buf)
def load_builtin_fonts():
# 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
families = {u'Liberation Serif', u'Liberation Sans', u'Liberation Mono'}
if iswindows or isosx:
import glob
from PyQt4.Qt import QFontDatabase
families = set()
for f in glob.glob(P('fonts/liberation/*.ttf')):
with open(f, 'rb') as s:
# Windows requires font files to be executable for them to be
# loaded successfully, so we use the in memory loader
fid = QFontDatabase.addApplicationFontFromData(s.read())
if fid > -1:
families |= set(map(unicode,
QFontDatabase.applicationFontFamilies(fid)))
return families
def ipython(user_ns=None):
from calibre.utils.ipython import ipython
ipython(user_ns=user_ns)

View File

@ -36,6 +36,7 @@ isunix = isosx or islinux
isportable = os.environ.get('CALIBRE_PORTABLE_BUILD', None) is not None
ispy3 = sys.version_info.major > 2
isxp = iswindows and sys.getwindowsversion().major < 6
isworker = os.environ.has_key('CALIBRE_WORKER') or os.environ.has_key('CALIBRE_SIMPLE_WORKER')
try:
preferred_encoding = locale.getpreferredencoding()
@ -83,7 +84,6 @@ class Plugins(collections.Mapping):
'magick',
'podofo',
'cPalmdoc',
'fontconfig',
'progress_indicator',
'chmlib',
'chm_extra',
@ -91,6 +91,7 @@ class Plugins(collections.Mapping):
'speedup',
'freetype',
'woff',
'sfntly',
]
if iswindows:
plugins.extend(['winutil', 'wpd', 'winfonts'])

View File

@ -19,6 +19,8 @@ Run an embedded python interpreter.
''')
parser.add_option('-c', '--command', help='Run python code.', default=None)
parser.add_option('-e', '--exec-file', default=None, help='Run the python code in file.')
parser.add_option('-f', '--subset-font', default=False,
action='store_true', help='Subset the specified font')
parser.add_option('-d', '--debug-device-driver', default=False, action='store_true',
help='Debug the specified device driver.')
parser.add_option('-g', '--gui', default=False, action='store_true',
@ -209,6 +211,11 @@ def main(args=sys.argv):
execfile(ef, g)
return
if len(args) > 1 and args[1] in ('-f', '--subset-font'):
from calibre.utils.fonts.subset import main
main(['subset-font']+args[2:])
return
opts, args = option_parser().parse_args(args)
if opts.gui:
from calibre.gui2.main import main

View File

@ -212,7 +212,7 @@ class ANDROID(USBMS):
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
'PMP5097C', 'MASS', 'NOVO7', 'ZEKI', 'COBY', 'SXZ', 'USB_2.0',
'COBY_MID', 'VS', 'AINOL', 'TOPWISE']
'COBY_MID', 'VS', 'AINOL', 'TOPWISE', 'PAD703']
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',

View File

@ -58,7 +58,8 @@ class Book(Book_):
self.datetime = time.gmtime()
self.contentID = None
self.current_shelves = []
self.current_shelves = []
self.kobo_collections = []
if thumbnail_name is not None:
self.thumbnail = ImageWrapper(thumbnail_name)
@ -99,6 +100,10 @@ class KTCollectionsBookList(CollectionsBookList):
lpath = getattr(book, 'lpath', None)
if lpath is None:
continue
# If the book is not in the current library, we don't want to use the metadtaa for the collections
if book.application_id is None:
# debug_print("KTCollectionsBookList:get_collections - Book not in current library")
continue
# Decide how we will build the collections. The default: leave the
# book in all existing collections. Do not add any new ones.
attrs = ['device_collections']
@ -115,7 +120,8 @@ class KTCollectionsBookList(CollectionsBookList):
elif prefs['manage_device_metadata'] == 'on_connect':
# For existing books, modify the collections only if the user
# specified 'on_connect'
attrs += collection_attributes
attrs = collection_attributes
book.device_collections = []
if show_debug:
debug_print("KTCollectionsBookList:get_collections - attrs=", attrs)

View File

@ -33,7 +33,7 @@ class KOBO(USBMS):
gui_name = 'Kobo Reader'
description = _('Communicate with the Kobo Reader')
author = 'Timothy Legge and David Forrester'
version = (2, 0, 2)
version = (2, 0, 3)
dbversion = 0
fwversion = 0
@ -653,6 +653,7 @@ class KOBO(USBMS):
@classmethod
def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID):
# debug_print("KOBO:book_from_path - title=%s"%title)
from calibre.ebooks.metadata import MetaInformation
if cls.settings().read_metadata or cls.MUST_READ_METADATA:
@ -1199,7 +1200,7 @@ class KOBOTOUCH(KOBO):
author = 'David Forrester'
description = 'Communicate with the Kobo Touch, Glo and Mini firmware. Based on the existing Kobo driver by %s.' % (KOBO.author)
supported_dbversion = 65
supported_dbversion = 70
min_supported_dbversion = 53
booklist_class = KTCollectionsBookList
@ -1408,9 +1409,14 @@ class KOBOTOUCH(KOBO):
debug_print("KoboTouch:update_booklist - have a deleted book")
# Label Previews
if accessibility == 6:
playlist_map[lpath].append('Preview')
if isdownloaded == 'false':
playlist_map[lpath].append('Recommendation')
else:
playlist_map[lpath].append('Preview')
elif accessibility == 4:
playlist_map[lpath].append('Recommendation')
kobo_collections = playlist_map[lpath][:]
if len(bookshelves) > 0:
playlist_map[lpath].extend(bookshelves)
@ -1428,6 +1434,7 @@ class KOBOTOUCH(KOBO):
debug_print('KoboTouch:update_booklist - bl[idx].device_collections=', bl[idx].device_collections)
debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map)
debug_print('KoboTouch:update_booklist - bookshelves=', bookshelves)
debug_print('KoboTouch:update_booklist - kobo_collections=', kobo_collections)
debug_print('KoboTouch:update_booklist - series="%s"' % bl[idx].series)
debug_print('KoboTouch:update_booklist - the book=', bl[idx])
debug_print('KoboTouch:update_booklist - the authors=', bl[idx].authors)
@ -1454,8 +1461,9 @@ class KOBOTOUCH(KOBO):
if lpath in playlist_map:
bl[idx].device_collections = playlist_map.get(lpath,[])
bl[idx].current_shelves = bookshelves
bl[idx].kobo_collections = kobo_collections
changed = True
bl[idx].current_shelves = bookshelves
if show_debug:
debug_print('KoboTouch:update_booklist - updated bl[idx].device_collections=', bl[idx].device_collections)
@ -1490,10 +1498,12 @@ class KOBOTOUCH(KOBO):
debug_print(" the book:", book)
debug_print(" author_sort:'%s'"%book.author_sort)
debug_print(" bookshelves:", bookshelves)
debug_print(" kobo_collections:", kobo_collections)
# print 'Update booklist'
book.device_collections = playlist_map.get(lpath,[])# if lpath in playlist_map else []
book.current_shelves = bookshelves
book.kobo_collections = kobo_collections
book.contentID = ContentID
# debug_print('KoboTouch:update_booklist - title=', title, 'book.device_collections', book.device_collections)
@ -1630,7 +1640,8 @@ class KOBOTOUCH(KOBO):
#print "count found in cache: %d, count of files in metadata: %d, need_sync: %s" % \
# (len(bl_cache), len(bl), need_sync)
# Bypassing the KOBO sync_booklists as that does things we don't need to do
if need_sync: #self.count_found_in_bl != len(bl) or need_sync:
# Also forcing sync to see if this solves issues with updating shelves and matching books.
if need_sync or True: #self.count_found_in_bl != len(bl) or need_sync:
debug_print("KoboTouch:books - about to sync_booklists")
if oncard == 'cardb':
USBMS.sync_booklists(self, (None, None, bl))
@ -1948,7 +1959,6 @@ class KOBOTOUCH(KOBO):
debug_print("Booklists=", booklists)
if self.dbversion < 53:
self.reset_readstatus(connection, oncard)
self.remove_from_bookshelves(connection, oncard)
if self.dbversion >= 14:
debug_print("No Collections - reseting FavouritesIndex")
self.reset_favouritesindex(connection, oncard)
@ -1961,6 +1971,7 @@ class KOBOTOUCH(KOBO):
if book.application_id is not None:
# debug_print("KoboTouch:update_device_database_collections - about to remove a book from shelves book.title=%s" % book.title)
self.remove_book_from_device_bookshelves(connection, book)
book.device_collections.extend(book.kobo_collections)
if not prefs['manage_device_metadata'] == 'manual' and delete_empty_shelves:
debug_print("KoboTouch:update_device_database_collections - about to clear empty bookshelves")
self.delete_empty_bookshelves(connection)
@ -2096,7 +2107,7 @@ class KOBOTOUCH(KOBO):
def remove_book_from_device_bookshelves(self, connection, book):
show_debug = self.is_debugging_title(book.title)# or True
remove_shelf_list = set(book.current_shelves) - set(book.device_collections)# - set(["Im_Reading", "Read", "Closed"])
remove_shelf_list = set(book.current_shelves) - set(book.device_collections)
if show_debug:
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.application_id="%s"'%book.application_id)
@ -2212,11 +2223,8 @@ class KOBOTOUCH(KOBO):
debug_print('KoboTouch:check_for_bookshelf bookshelf_name="%s"'%bookshelf_name)
test_query = 'SELECT InternalName, Name, _IsDeleted FROM Shelf WHERE Name = ?'
test_values = (bookshelf_name, )
addquery = 'INSERT INTO "main"."Shelf"'\
' ("CreationDate","InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced")'\
' VALUES (?, ?, ?, ?, ?, ?, ?)'
add_values = (
time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
addquery = 'INSERT INTO "main"."Shelf"'
add_values = (time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
bookshelf_name,
time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
bookshelf_name,
@ -2224,6 +2232,17 @@ class KOBOTOUCH(KOBO):
"true",
"false",
)
if self.dbversion < 64:
addquery += ' ("CreationDate","InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced")'\
' VALUES (?, ?, ?, ?, ?, ?, ?)'
else:
addquery += ' ("CreationDate", "InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced", "Id")'\
' VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
add_values = add_values +(bookshelf_name,)
if show_debug:
debug_print('KoboTouch:check_for_bookshelf addquery=', addquery)
debug_print('KoboTouch:check_for_bookshelf add_values=', add_values)
updatequery = 'UPDATE Shelf SET _IsDeleted = "false" WHERE Name = ?'
cursor = connection.cursor()

View File

@ -114,7 +114,7 @@ class MTP_DEVICE(BASE):
except:
prints('Failed to load existing driveinfo.calibre file, with error:')
traceback.print_exc()
dinfo = None
dinfo = {}
if dinfo.get('device_store_uuid', None) is None:
dinfo['device_store_uuid'] = unicode(uuid.uuid4())
if dinfo.get('device_name', None) is None:

View File

@ -254,7 +254,6 @@ def unit_convert(value, base, font, dpi):
def generate_masthead(title, output_path=None, width=600, height=60):
from calibre.ebooks.conversion.config import load_defaults
from calibre.utils.fonts import fontconfig
from calibre.utils.config import tweaks
fp = tweaks['generate_cover_title_font']
if not fp:
@ -264,11 +263,10 @@ def generate_masthead(title, output_path=None, width=600, height=60):
masthead_font_family = recs.get('masthead_font', 'Default')
if masthead_font_family != 'Default':
masthead_font = fontconfig.files_for_family(masthead_font_family)
# Assume 'normal' always in dict, else use default
# {'normal': (path_to_font, friendly name)}
if 'normal' in masthead_font:
font_path = masthead_font['normal'][0]
from calibre.utils.fonts.scanner import font_scanner
faces = font_scanner.fonts_for_family(masthead_font_family)
if faces:
font_path = faces[0]['path']
if not font_path or not os.access(font_path, os.R_OK):
font_path = default_font

View File

@ -34,24 +34,24 @@ class PRS500_PROFILE(object):
name = 'prs500'
def find_custom_fonts(options, logger):
from calibre.utils.fonts import fontconfig
files_for_family = fontconfig.files_for_family
from calibre.utils.fonts.scanner import font_scanner
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
def family(cmd):
return cmd.split(',')[-1].strip()
if options.serif_family:
f = family(options.serif_family)
fonts['serif'] = files_for_family(f)
fonts['serif'] = font_scanner.legacy_fonts_for_family(f)
print (111111, fonts['serif'])
if not fonts['serif']:
logger.warn('Unable to find serif family %s'%f)
if options.sans_family:
f = family(options.sans_family)
fonts['sans'] = files_for_family(f)
fonts['sans'] = font_scanner.legacy_fonts_for_family(f)
if not fonts['sans']:
logger.warn('Unable to find sans family %s'%f)
if options.mono_family:
f = family(options.mono_family)
fonts['mono'] = files_for_family(f)
fonts['mono'] = font_scanner.legacy_fonts_for_family(f)
if not fonts['mono']:
logger.warn('Unable to find mono family %s'%f)
return fonts

View File

@ -178,49 +178,40 @@ class CSSFlattener(object):
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:
from calibre.utils.fonts.scanner import font_scanner
from calibre.utils.fonts.utils import panose_to_css_generic_family
faces = font_scanner.fonts_for_family(family)
if not faces:
msg = (u'No embeddable fonts found for family: %r'%self.opts.embed_font_family)
if faces:
msg = (u'The selected font %s has no Regular typeface, only'
' %s faces, it cannot be used.')%(
self.opts.embed_font_family,
', '.join(faces.iterkeys()))
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'
for i, font in enumerate(faces):
ext = 'otf' if font['is_otf'] else 'ttf'
fid, href = self.oeb.manifest.generate(id=u'font',
href=u'%s.%s'%(ascii_filename(full_name).replace(u' ', u'-'), ext))
href=u'%s.%s'%(ascii_filename(font['full_name']).replace(u' ', u'-'), ext))
item = self.oeb.manifest.add(fid, href,
guess_type(full_name+'.'+ext)[0],
data=data)
guess_type('dummy.'+ext)[0],
data=font_scanner.get_font_data(font))
item.unload_data_from_memory()
font[u'src'] = u'url(%s)'%item.href
cfont = {
u'font-family':u'"%s"'%font['font-family'],
u'panose-1': u' '.join(map(unicode, font['panose'])),
u'src': u'url(%s)'%item.href,
}
if i == 0:
generic_family = panose_to_css_generic_family(font['panose'])
body_font_family = u"'%s',%s"%(font['font-family'], generic_family)
self.oeb.log(u'Embedding font: %s'%font['font-family'])
for k in (u'font-weight', u'font-style', u'font-stretch'):
if font[k] != u'normal':
cfont[k] = font[k]
rule = '@font-face { %s }'%('; '.join(u'%s:%s'%(k, v) for k, v in
font.iteritems()))
cfont.iteritems()))
rule = cssutils.parseString(rule)
efi.append(rule)

View File

@ -1,18 +1,19 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
""" The GUI """
import os, sys, Queue, threading
import os, sys, Queue, threading, glob
from threading import RLock
from urllib import unquote
from PyQt4.Qt import (QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt,
QByteArray, QTranslator, QCoreApplication, QThread,
QEvent, QTimer, pyqtSignal, QDateTime, QDesktopServices,
QFileDialog, QFileIconProvider, QSettings, QColor,
QIcon, QApplication, QDialog, QUrl, QFont, QPalette)
QIcon, QApplication, QDialog, QUrl, QFont, QPalette,
QFontDatabase)
ORG_NAME = 'KovidsBrain'
APP_UID = 'libprs500'
from calibre import prints, load_builtin_fonts
from calibre import prints
from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx,
plugins, config_dir, filesystem_encoding, DEBUG)
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
@ -779,7 +780,6 @@ class Application(QApplication):
qt_app = self
self._file_open_paths = []
self._file_open_lock = RLock()
load_builtin_fonts()
self.setup_styles(force_calibre_style)
if DEBUG:
@ -792,6 +792,29 @@ class Application(QApplication):
self.redirect_notify = True
return ret
def load_builtin_fonts(self, scan_for_fonts=False):
global _rating_font
if scan_for_fonts:
from calibre.utils.fonts.scanner import font_scanner
# Start scanning the users computer for fonts
font_scanner
# Load the builtin fonts and any fonts added to calibre by the user to
# Qt
for ff in glob.glob(P('fonts/liberation/*.?tf')) + \
[P('fonts/calibreSymbols.otf')] + \
glob.glob(os.path.join(config_dir, 'fonts', '*.?tf')):
if ff.rpartition('.')[-1].lower() in {'ttf', 'otf'}:
with open(ff, 'rb') as s:
# Windows requires font files to be executable for them to be
# loaded successfully, so we use the in memory loader
fid = QFontDatabase.addApplicationFontFromData(s.read())
if fid > -1:
fam = QFontDatabase.applicationFontFamilies(fid)
fam = set(map(unicode, fam))
if u'calibre Symbols' in fam:
_rating_font = u'calibre Symbols'
def load_calibre_style(self):
# On OS X QtCurve resets the palette, so we preserve it explicitly
orig_pal = QPalette(self.palette())
@ -964,22 +987,9 @@ def is_gui_thread():
global gui_thread
return gui_thread is QThread.currentThread()
_rating_font = None
_rating_font = 'Arial Unicode MS' if iswindows else 'sans-serif'
def rating_font():
global _rating_font
if _rating_font is None:
from PyQt4.Qt import QFontDatabase
_rating_font = 'Arial Unicode MS' if iswindows else 'sans-serif'
fontid = QFontDatabase.addApplicationFont(
#P('fonts/liberation/LiberationSerif-Regular.ttf')
P('fonts/calibreSymbols.otf')
)
if fontid > -1:
try:
_rating_font = unicode(list(
QFontDatabase.applicationFontFamilies(fontid))[0])
except:
pass
return _rating_font
def find_forms(srcdir):

View File

@ -29,38 +29,8 @@ class PluginWidget(Widget, Ui_Form):
)
self.db, self.book_id = db, book_id
'''
from calibre.utils.fonts import fontconfig
global font_family_model
if font_family_model is None:
font_family_model = FontFamilyModel()
try:
font_family_model.families = fontconfig.find_font_families(allowed_extensions=['ttf'])
except:
import traceback
font_family_model.families = []
print 'WARNING: Could not load fonts'
traceback.print_exc()
font_family_model.families.sort()
font_family_model.families[:0] = [_('Default')]
self.font_family_model = font_family_model
self.opt_masthead_font.setModel(self.font_family_model)
'''
self.opt_mobi_file_type.addItems(['old', 'both', 'new'])
self.initialize_options(get_option, get_help, db, book_id)
'''
def set_value_handler(self, g, val):
if unicode(g.objectName()) in 'opt_masthead_font':
idx = -1
if val:
idx = g.findText(val, Qt.MatchFixedString)
if idx < 0:
idx = 0
g.setCurrentIndex(idx)
return True
return False
'''

View File

@ -219,7 +219,13 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
if mi:
self.mi = mi
else:
self.mi = Metadata(None, None)
self.mi = Metadata(_('Title'), [_('Author')])
self.mi.author_sort = _('Author Sort')
self.mi.series = _('Series')
self.mi.series_index = 3
self.mi.rating = 4.0
self.mi.tags = [_('Tag 1'), _('Tag 2')]
self.mi.language = ['eng']
# Remove help icon on title bar
icon = self.windowIcon()

View File

@ -13,9 +13,6 @@ from PyQt4.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
QToolButton, QGridLayout, QListView, QWidget, QDialogButtonBox, QIcon,
QHBoxLayout, QLabel, QModelIndex)
from calibre.gui2 import error_dialog
from calibre.utils.icu import sort_key
def writing_system_for_font(font):
has_latin = True
systems = QFontDatabase().writingSystems(font.family())
@ -113,8 +110,54 @@ class FontFamilyDelegate(QStyledItemDelegate):
r.setLeft(r.left() + w)
painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, sample)
class Typefaces(QWidget):
pass
class Typefaces(QLabel):
def __init__(self, parent=None):
QLabel.__init__(self, parent)
self.setMinimumWidth(400)
self.base_msg = '<h3>'+_('Choose a font family')+'</h3>'
self.setText(self.base_msg)
self.setWordWrap(True)
def show_family(self, family, faces):
if not family:
self.setText(self.base_msg)
return
msg = '''
<h3>%s</h3>
<dl style="font-size: smaller">
{0}
</dl>
'''%(_('Available faces for %s')%family)
entries = []
for font in faces:
sf = (font['wws_subfamily_name'] or font['preferred_subfamily_name']
or font['subfamily_name'])
entries.append('''
<dt><b>{sf}</b></dt>
<dd>font-stretch: <i>{width}</i> font-weight: <i>{weight}</i> font-style:
<i>{style}</i></dd>
'''.format(sf=sf, width=font['font-stretch'],
weight=font['font-weight'], style=font['font-style']))
msg = msg.format('\n\n'.join(entries))
self.setText(msg)
class FontsView(QListView):
changed = pyqtSignal()
def __init__(self, parent):
QListView.__init__(self, parent)
self.setSelectionMode(self.SingleSelection)
self.setAlternatingRowColors(True)
self.d = FontFamilyDelegate(self)
self.setItemDelegate(self.d)
def currentChanged(self, current, previous):
self.changed.emit()
QListView.currentChanged(self, current, previous)
class FontFamilyDialog(QDialog):
@ -122,28 +165,22 @@ class FontFamilyDialog(QDialog):
QDialog.__init__(self, parent)
self.setWindowTitle(_('Choose font family'))
self.setWindowIcon(QIcon(I('font.png')))
from calibre.utils.fonts import fontconfig
from calibre.utils.fonts.scanner import font_scanner
self.font_scanner = font_scanner
try:
self.families = fontconfig.find_font_families()
self.families = list(font_scanner.find_font_families())
except:
self.families = []
print ('WARNING: Could not load fonts')
import traceback
traceback.print_exc()
# Restrict to Qt families as we need the font to be available in
# QFontDatabase
qt_families = set([unicode(x) for x in QFontDatabase().families()])
self.families = list(qt_families.intersection(set(self.families)))
self.families.sort(key=sort_key)
self.families.insert(0, _('None'))
self.l = l = QGridLayout()
self.setLayout(l)
self.view = QListView(self)
self.view = FontsView(self)
self.m = QStringListModel(self.families)
self.view.setModel(self.m)
self.d = FontFamilyDelegate(self)
self.view.setItemDelegate(self.d)
self.view.setCurrentIndex(self.m.index(0))
if current_family:
for i, val in enumerate(self.families):
@ -151,22 +188,22 @@ class FontFamilyDialog(QDialog):
self.view.setCurrentIndex(self.m.index(i))
break
self.view.doubleClicked.connect(self.accept, type=Qt.QueuedConnection)
self.view.setSelectionMode(self.view.SingleSelection)
self.view.setAlternatingRowColors(True)
self.view.changed.connect(self.current_changed,
type=Qt.QueuedConnection)
self.faces = Typefaces(self)
self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
self.bb.accepted.connect(self.accept)
self.bb.rejected.connect(self.reject)
self.ml = QLabel(_('Choose a font family from the list below:'))
self.faces = Typefaces(self)
l.addWidget(self.ml, 0, 0, 1, 2)
l.addWidget(self.view, 1, 0, 1, 1)
l.addWidget(self.faces, 1, 1, 1, 1)
l.addWidget(self.bb, 2, 0, 1, 2)
l.setAlignment(self.faces, Qt.AlignTop)
self.resize(600, 500)
self.resize(800, 600)
@property
def font_family(self):
@ -174,19 +211,10 @@ class FontFamilyDialog(QDialog):
if idx == 0: return None
return self.families[idx]
def accept(self):
ff = self.font_family
if ff:
from calibre.utils.fonts import fontconfig
faces = fontconfig.fonts_for_family(ff) or {}
faces = frozenset(faces.iterkeys())
if 'normal' not in faces:
error_dialog(self, _('Not a useable font'),
_('The %s font family does not have a Regular typeface, so it'
' cannot be used. It has only the "%s" face(s).')%(
ff, ', '.join(faces)), show=True)
return
QDialog.accept(self)
def current_changed(self):
fam = self.font_family
self.faces.show_family(fam, self.font_scanner.fonts_for_family(fam)
if fam else None)
class FontFamilyChooser(QWidget):

View File

@ -871,12 +871,18 @@ class BooksModel(QAbstractTableModel): # {{{
try:
return self._set_data(index, value)
except (IOError, OSError) as err:
import traceback
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback
fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
error_dialog(get_gui(), _('Permission denied'),
_('Could not change the on disk location of this'
' book. Is it open in another program?'),
det_msg=traceback.format_exc(), show=True)
det_msg=p+traceback.format_exc(), show=True)
return False
error_dialog(get_gui(), _('Failed to set data'),
_('Could not set data, click Show Details to see why.'),
det_msg=traceback.format_exc(), show=True)
except:
import traceback
traceback.print_exc()

View File

@ -291,6 +291,7 @@ def run_in_debug_mode(logpath=None):
def run_gui(opts, args, actions, listener, app, gui_debug=None):
initialize_file_icon_provider()
app.load_builtin_fonts(scan_for_fonts=True)
if not dynamic.get('welcome_wizard_was_run', False):
from calibre.gui2.wizard import wizard
wizard().exec_()

View File

@ -91,22 +91,26 @@ class TitleEdit(EnLineEdit):
def commit(self, db, id_):
title = self.current_val
try:
if self.COMMIT:
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False)
else:
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False,
commit=False)
except (IOError, OSError) as err:
if getattr(err, 'errno', -1) == errno.EACCES: # Permission denied
import traceback
fname = err.filename if err.filename else 'file'
error_dialog(self, _('Permission denied'),
_('Could not open %s. Is it being used by another'
' program?')%fname, det_msg=traceback.format_exc(),
show=True)
return False
raise
if title != self.original_val:
# Only try to commit if changed. This allow setting of other fields
# to work even if some of the book files are opened in windows.
try:
if self.COMMIT:
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False)
else:
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False,
commit=False)
except (IOError, OSError) as err:
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback
fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
error_dialog(self, _('Permission denied'),
_('Could not change the on disk location of this'
' book. Is it open in another program?'),
det_msg=p+traceback.format_exc(), show=True)
return False
raise
return True
@dynamic_property
@ -262,19 +266,23 @@ class AuthorsEdit(EditWithComplete):
def commit(self, db, id_):
authors = self.current_val
try:
self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
allow_case_change=True)
except (IOError, OSError) as err:
if getattr(err, 'errno', -1) == errno.EACCES: # Permission denied
import traceback
fname = err.filename if err.filename else 'file'
error_dialog(self, _('Permission denied'),
_('Could not open "%s". Is it being used by another'
' program?')%fname, det_msg=traceback.format_exc(),
show=True)
return False
raise
if authors != self.original_val:
# Only try to commit if changed. This allow setting of other fields
# to work even if some of the book files are opened in windows.
try:
self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
allow_case_change=True)
except (IOError, OSError) as err:
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback
fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
error_dialog(self, _('Permission denied'),
_('Could not change the on disk location of this'
' book. Is it open in another program?'),
det_msg=p+traceback.format_exc(), show=True)
return False
raise
return True
@dynamic_property

View File

@ -322,6 +322,7 @@ class MetadataSingleDialogBase(ResizableDialog):
' program?')%fname, det_msg=traceback.format_exc(),
show=True)
return
raise
if mi is None:
return
cdata = None
@ -444,11 +445,12 @@ class MetadataSingleDialogBase(ResizableDialog):
except (IOError, OSError) as err:
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback
fname = err.filename if err.filename else 'file'
fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
error_dialog(self, _('Permission denied'),
_('Could not open %s. Is it being used by another'
' program?')%fname, det_msg=traceback.format_exc(),
show=True)
_('Could not change the on disk location of this'
' book. Is it open in another program?'),
det_msg=p+traceback.format_exc(), show=True)
return False
raise
for widget in getattr(self, 'custom_metadata_widgets', []):

View File

@ -485,7 +485,7 @@ class DocumentView(QWebView): # {{{
self.dictionary_action.triggered.connect(self.lookup)
self.addAction(self.dictionary_action)
self.image_popup = ImagePopup(self)
self.view_image_action = QAction(_('View &image...'), self)
self.view_image_action = QAction(QIcon(I('view-image.png')), _('View &image...'), self)
self.view_image_action.triggered.connect(self.image_popup)
self.search_action = QAction(QIcon(I('dictionary.png')),
_('&Search for next occurrence'), self)

View File

@ -8,7 +8,8 @@ __copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QDialog, QPixmap, QUrl, QScrollArea, QLabel, QSizePolicy,
QDialogButtonBox, QVBoxLayout, QPalette, QApplication, QSize, QIcon, Qt)
QDialogButtonBox, QVBoxLayout, QPalette, QApplication, QSize, QIcon,
Qt, QTransform)
from calibre.gui2 import choose_save_file, gprefs
@ -37,12 +38,15 @@ class ImageView(QDialog):
self.zi_button = zi = bb.addButton(_('Zoom &in'), bb.ActionRole)
self.zo_button = zo = bb.addButton(_('Zoom &out'), bb.ActionRole)
self.save_button = so = bb.addButton(_('&Save as'), bb.ActionRole)
self.rotate_button = ro = bb.addButton(_('&Rotate'), bb.ActionRole)
zi.setIcon(QIcon(I('plus.png')))
zo.setIcon(QIcon(I('minus.png')))
so.setIcon(QIcon(I('save.png')))
ro.setIcon(QIcon(I('rotate-right.png')))
zi.clicked.connect(self.zoom_in)
zo.clicked.connect(self.zoom_out)
so.clicked.connect(self.save_image)
ro.clicked.connect(self.rotate_image)
self.l = l = QVBoxLayout()
self.setLayout(l)
@ -76,6 +80,14 @@ class ImageView(QDialog):
self.scrollarea.verticalScrollBar()):
sb.setValue(int(factor*sb.value()) + ((factor - 1) * sb.pageStep()/2))
def rotate_image(self):
pm = self.label.pixmap()
t = QTransform()
t.rotate(90)
pm = pm.transformed(t)
self.label.setPixmap(pm)
self.label.adjustSize()
def __call__(self):
geom = self.avail_geom
self.label.setPixmap(self.current_img)
@ -93,6 +105,14 @@ class ImageView(QDialog):
gprefs['viewer_image_popup_geometry'] = bytearray(self.saveGeometry())
return QDialog.done(self, e)
def wheelEvent(self, event):
if event.delta() < -14:
self.zoom_out()
event.accept()
elif event.delta() > 14:
event.accept()
self.zoom_in()
class ImagePopup(object):
def __init__(self, parent):
@ -114,3 +134,12 @@ class ImagePopup(object):
if not d.isVisible():
self.dialogs.remove(d)
if __name__ == '__main__':
import sys
app = QApplication([])
p = QPixmap()
p.load(sys.argv[-1])
u = QUrl.fromLocalFile(sys.argv[-1])
d = ImageView(None, p, u)
d()
app.exec_()

View File

@ -1137,6 +1137,7 @@ def main(args=sys.argv):
if pid <= 0:
override = 'calibre-ebook-viewer' if islinux else None
app = Application(args, override_program_name=override)
app.load_builtin_fonts()
app.setWindowIcon(QIcon(I('viewer.png')))
QApplication.setOrganizationName(ORG_NAME)
QApplication.setApplicationName(APP_UID)

View File

@ -11,7 +11,7 @@ from PyQt4.Qt import (QIcon, QFont, QLabel, QListWidget, QAction,
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, QRegExp, QSize,
QSplitter, QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene, QMenu,
QStringListModel, QCompleter, QStringList, QTimer, QRect,
QFontDatabase, QGraphicsView, QByteArray)
QGraphicsView, QByteArray)
from calibre.constants import iswindows
from calibre.gui2 import (NONE, error_dialog, pixmap_to_data, gprefs,
@ -352,17 +352,14 @@ class FontFamilyModel(QAbstractListModel): # {{{
def __init__(self, *args):
QAbstractListModel.__init__(self, *args)
from calibre.utils.fonts import fontconfig
from calibre.utils.fonts.scanner import font_scanner
try:
self.families = fontconfig.find_font_families()
self.families = font_scanner.find_font_families()
except:
self.families = []
print 'WARNING: Could not load fonts'
traceback.print_exc()
# Restrict to Qt families as Qt tends to crash
qt_families = set([unicode(x) for x in QFontDatabase().families()])
self.families = list(qt_families.intersection(set(self.families)))
self.families.sort()
self.families[:0] = [_('None')]
self.font = QFont('Arial' if iswindows else 'sansserif')

View File

@ -2757,7 +2757,6 @@ class CatalogBuilder(object):
"""
from calibre.ebooks.conversion.config import load_defaults
from calibre.utils.fonts import fontconfig
MI_WIDTH = 600
MI_HEIGHT = 60
@ -2767,11 +2766,10 @@ class CatalogBuilder(object):
masthead_font_family = recs.get('masthead_font', 'Default')
if masthead_font_family != 'Default':
masthead_font = fontconfig.files_for_family(masthead_font_family)
# Assume 'normal' always in dict, else use default
# {'normal': (path_to_font, friendly name)}
if 'normal' in masthead_font:
font_path = masthead_font['normal'][0]
from calibre.utils.fonts.scanner import font_scanner
faces = font_scanner.fonts_for_family(masthead_font_family)
if faces:
font_path = faces[0]['path']
if not font_path or not os.access(font_path, os.R_OK):
font_path = default_font

View File

@ -2205,13 +2205,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def set(self, row, column, val, allow_case_change=False):
'''
Convenience method for setting the title, authors, publisher or rating
Convenience method for setting the title, authors, publisher, tags or
rating
'''
id = self.data[row][0]
col = {'title':1, 'authors':2, 'publisher':3, 'rating':4, 'tags':7}[column]
col = self.FIELD_MAP[column]
books_to_refresh = set()
self.data.set(row, col, val)
set_args = (row, col, val)
if column == 'authors':
val = string_to_authors(val)
books_to_refresh |= self.set_authors(id, val, notify=False,
@ -2227,6 +2228,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
books_to_refresh |= \
self.set_tags(id, [x.strip() for x in val.split(',') if x.strip()],
append=False, notify=False, allow_case_change=allow_case_change)
self.data.set(*set_args)
self.data.refresh_ids(self, [id])
self.set_path(id, True)
self.notify('metadata', [id])
@ -2474,6 +2476,23 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.clean_standard_field('authors', commit=True)
return books_to_refresh
def windows_check_if_files_in_use(self, book_id):
'''
Raises an EACCES IOError if any of the files in the folder of book_id
are opened in another program on windows.
'''
if iswindows:
path = self.path(book_id, index_is_id=True)
if path:
spath = os.path.join(self.library_path, *path.split('/'))
wam = None
if os.path.exists(spath):
try:
wam = WindowsAtomicFolderMove(spath)
finally:
if wam is not None:
wam.close_handles()
def set_authors(self, id, authors, notify=True, commit=True,
allow_case_change=False):
'''
@ -2482,6 +2501,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
:param authors: A list of authors.
'''
self.windows_check_if_files_in_use(id)
books_to_refresh = self._set_authors(id, authors,
allow_case_change=allow_case_change)
self.dirtied(set([id])|books_to_refresh, commit=False)
@ -2532,6 +2552,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
Note that even if commit is False, the db will still be committed to
because this causes the location of files to change
'''
self.windows_check_if_files_in_use(id)
if not self._set_title(id, title):
return
self.set_path(id, index_is_id=True)

View File

@ -37,13 +37,10 @@ def test_freetype():
test()
print ('FreeType OK!')
def test_fontconfig():
from calibre.utils.fonts import fontconfig
families = fontconfig.find_font_families()
num = len(families)
if num < 10:
raise RuntimeError('Fontconfig found only %d font families'%num)
print ('Fontconfig OK! (%d families)'%num)
def test_sfntly():
from calibre.utils.fonts.subset import test
test()
print ('sfntly OK!')
def test_winutil():
from calibre.devices.scanner import win_pnp_drives
@ -123,13 +120,13 @@ def test():
test_plugins()
test_lxml()
test_freetype()
test_fontconfig()
test_sfntly()
test_sqlite()
test_qt()
test_imaging()
test_unrar()
test_icu()
test_woff()
test_qt()
if iswindows:
test_win32()
test_winutil()

View File

@ -208,17 +208,22 @@ def samefile_windows(src, dst):
if samestring:
return True
handles = []
def get_fileid(x):
if isbytestring(x): x = x.decode(filesystem_encoding)
try:
h = win32file.CreateFile(x, 0, 0, None, win32file.OPEN_EXISTING,
win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
handles.append(h)
data = win32file.GetFileInformationByHandle(h)
except (error, EnvironmentError):
return None
return (data[4], data[8], data[9])
a, b = get_fileid(src), get_fileid(dst)
for h in handles:
win32file.CloseHandle(h)
if a is None and b is None:
return False
return a == b
@ -302,6 +307,8 @@ class WindowsAtomicFolderMove(object):
' operation was started'%path)
try:
win32file.CreateHardLink(dest, path)
if os.path.getsize(dest) != os.path.getsize(path):
raise Exception('This apparently can happen on network shares. Sigh.')
return
except:
pass
@ -341,6 +348,8 @@ def hardlink_file(src, dest):
if iswindows:
import win32file
win32file.CreateHardLink(dest, src)
if os.path.getsize(dest) != os.path.getsize(src):
raise Exception('This apparently can happen on network shares. Sigh.')
return
os.link(src, dest)

View File

@ -6,120 +6,3 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.constants import iswindows, isosx
class Fonts(object):
def __init__(self):
if iswindows:
from calibre.utils.fonts.win_fonts import load_winfonts
self.backend = load_winfonts()
else:
from calibre.utils.fonts.fc import fontconfig
self.backend = fontconfig
def find_font_families(self, allowed_extensions={'ttf', 'otf'}):
if iswindows:
return self.backend.font_families()
return self.backend.find_font_families(allowed_extensions=allowed_extensions)
def find_font_families_no_delay(self, allowed_extensions={'ttf', 'otf'}):
if isosx:
if self.backend.is_scanning():
return False, []
return True, self.find_font_families(allowed_extensions=allowed_extensions)
def files_for_family(self, family, normalize=True):
'''
Find all the variants in the font family `family`.
Returns a dictionary of tuples. Each tuple is of the form (path to font
file, Full font name).
The keys of the dictionary depend on `normalize`. If `normalize` is `False`,
they are a tuple (slant, weight) otherwise they are strings from the set
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
'''
if iswindows:
from calibre.ptempfile import PersistentTemporaryFile
fonts = self.backend.fonts_for_family(family, normalize=normalize)
ans = {}
for ft, val in fonts.iteritems():
ext, name, data = val
pt = PersistentTemporaryFile('.'+ext)
pt.write(data)
pt.close()
ans[ft] = (pt.name, name)
return ans
return self.backend.files_for_family(family, normalize=normalize)
def fonts_for_family(self, family, normalize=True):
'''
Just like files for family, except that it returns 3-tuples of the form
(extension, full name, font data).
'''
if iswindows:
return self.backend.fonts_for_family(family, normalize=normalize)
files = self.backend.files_for_family(family, normalize=normalize)
ans = {}
for ft, val in files.iteritems():
f, name = val
ext = f.rpartition('.')[-1].lower()
ans[ft] = (ext, name, open(f, 'rb').read())
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()
def test():
import os
print(fontconfig.find_font_families())
m = 'Liberation Serif'
for ft, val in fontconfig.files_for_family(m).iteritems():
print val[0], ft, val[1], os.path.getsize(val[0])
if __name__ == '__main__':
test()

View File

@ -1,170 +0,0 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys
from calibre.constants import plugins, islinux, isbsd, isosx
_fc, _fc_err = plugins['fontconfig']
if _fc is None:
raise RuntimeError('Failed to load fontconfig with error:'+_fc_err)
if islinux or isbsd:
Thread = object
else:
from threading import Thread
class FontConfig(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.failed = False
def run(self):
config = None
if getattr(sys, 'frameworks_dir', False):
config_dir = os.path.join(os.path.dirname(
getattr(sys, 'frameworks_dir')), 'Resources', 'fonts')
if isinstance(config_dir, unicode):
config_dir = config_dir.encode(sys.getfilesystemencoding())
config = os.path.join(config_dir, 'fonts.conf')
try:
_fc.initialize(config)
except:
import traceback
traceback.print_exc()
self.failed = True
if not self.failed and hasattr(_fc, 'add_font_dir'):
_fc.add_font_dir(P('fonts/liberation'))
def is_scanning(self):
if isosx:
return self.is_alive()
return False
def wait(self):
if not (islinux or isbsd):
self.join()
if self.failed:
raise RuntimeError('Failed to initialize fontconfig')
def find_font_families(self, allowed_extensions={'ttf', 'otf'}):
'''
Return an alphabetically sorted list of font families available on the system.
`allowed_extensions`: A list of allowed extensions for font file types. Defaults to
`['ttf', 'otf']`. If it is empty, it is ignored.
'''
self.wait()
ans = _fc.find_font_families([bytes('.'+x) for x in allowed_extensions])
ans = sorted(set(ans), cmp=lambda x,y:cmp(x.lower(), y.lower()))
ans2 = []
for x in ans:
try:
ans2.append(x.decode('utf-8'))
except UnicodeDecodeError:
continue
return ans2
def files_for_family(self, family, normalize=True):
'''
Find all the variants in the font family `family`.
Returns a dictionary of tuples. Each tuple is of the form (path to font
file, Full font name).
The keys of the dictionary depend on `normalize`. If `normalize` is `False`,
they are a tuple (slant, weight) otherwise they are strings from the set
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
'''
self.wait()
if isinstance(family, unicode):
family = family.encode('utf-8')
fonts = {}
ofamily = str(family).decode('utf-8')
for fullname, path, style, nfamily, weight, slant in \
_fc.files_for_family(str(family)):
style = (slant, weight)
if normalize:
italic = slant > 0
normal = weight == 80
bold = weight > 80
if italic:
style = 'italic' if normal else 'bi' if bold else 'li'
else:
style = 'normal' if normal else 'bold' if bold else 'light'
try:
fullname, path = fullname.decode('utf-8'), path.decode('utf-8')
nfamily = nfamily.decode('utf-8')
except UnicodeDecodeError:
continue
if style in fonts:
if nfamily.lower().strip() == ofamily.lower().strip() \
and 'Condensed' not in fullname and 'ExtraLight' not in fullname:
fonts[style] = (path, fullname)
else:
fonts[style] = (path, fullname)
return fonts
def match(self, name, all=False, verbose=False):
'''
Find the system font that most closely matches `name`, where `name` is a specification
of the form::
familyname-<pointsize>:<property1=value1>:<property2=value2>...
For example, `verdana:weight=bold:slant=italic`
Returns a list of dictionaries, or a single dictionary.
Each dictionary has the keys:
'weight', 'slant', 'family', 'file', 'fullname', 'style'
`all`: If `True` return a sorted list of matching fonts, where the sort
is in order of decreasing closeness of matching. If `False` only the
best match is returned. '''
self.wait()
if isinstance(name, unicode):
name = name.encode('utf-8')
fonts = []
for fullname, path, style, family, weight, slant in \
_fc.match(str(name), bool(all), bool(verbose)):
try:
fullname = fullname.decode('utf-8')
path = path.decode('utf-8')
style = style.decode('utf-8')
family = family.decode('utf-8')
fonts.append({
'fullname' : fullname,
'path' : path,
'style' : style,
'family' : family,
'weight' : weight,
'slant' : slant
})
except UnicodeDecodeError:
continue
return fonts if all else (fonts[0] if fonts else None)
fontconfig = FontConfig()
if islinux or isbsd:
# On X11 Qt also uses fontconfig, so initialization must happen in the
# main thread. In any case on X11 initializing fontconfig should be very
# fast
fontconfig.run()
else:
fontconfig.start()
def test():
from pprint import pprint;
pprint(fontconfig.find_font_families())
pprint(fontconfig.files_for_family('liberation serif'))
m = 'liberation serif'
pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True))
if __name__ == '__main__':
test()

View File

@ -1,347 +0,0 @@
/*
:mod:`fontconfig` -- Pythonic interface to fontconfig
=====================================================
.. module:: fontconfig
:platform: All
:synopsis: Pythonic interface to the fontconfig library
.. moduleauthor:: Kovid Goyal <kovid@kovidgoyal.net> Copyright 2009
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <fontconfig.h>
static PyObject *
fontconfig_initialize(PyObject *self, PyObject *args) {
FcChar8 *path;
FcBool ok;
FcConfig *config;
PyThreadState *_save;
if (!PyArg_ParseTuple(args, "z", &path))
return NULL;
if (path == NULL) {
_save = PyEval_SaveThread();
ok = FcInit();
PyEval_RestoreThread(_save);
} else {
config = FcConfigCreate();
if (config == NULL) return PyErr_NoMemory();
_save = PyEval_SaveThread();
ok = FcConfigParseAndLoad(config, path, FcTrue);
if (ok) ok = FcConfigBuildFonts(config);
if (ok) ok = FcConfigSetCurrent(config);
PyEval_RestoreThread(_save);
if (!ok) return PyErr_NoMemory();
ok = 1;
}
if (ok) Py_RETURN_TRUE;
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
fontconfig_cleanup_find(FcPattern *p, FcObjectSet *oset, FcFontSet *fs) {
if (p != NULL) FcPatternDestroy(p);
if (oset != NULL) FcObjectSetDestroy(oset);
if (fs != NULL) FcFontSetDestroy(fs);
}
static PyObject *
fontconfig_find_font_families(PyObject *self, PyObject *args) {
int i;
size_t flen;
char *ext;
Py_ssize_t l, j, extlen;
FcBool ok;
FcPattern *pat, *temp;
FcObjectSet *oset;
FcFontSet *fs;
FcValue v, w;
PyObject *ans, *exts, *t;
ans = PyList_New(0);
fs = NULL; oset = NULL; pat = NULL;
if (ans == NULL) return PyErr_NoMemory();
if (!PyArg_ParseTuple(args, "O", &exts))
return NULL;
if (!PySequence_Check(exts)) {
PyErr_SetString(PyExc_ValueError, "Must pass sequence of extensions");
return NULL;
}
l = PySequence_Size(exts);
pat = FcPatternCreate();
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
oset = FcObjectSetCreate();
if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FILE)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FAMILY)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
fs = FcFontList(FcConfigGetCurrent(), pat, oset);
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
for (i = 0; i < fs->nfont; i++) {
temp = fs->fonts[i];
if (temp == NULL) continue;
if (FcPatternGet(temp, FC_FILE, 0, &v) != FcResultMatch) continue;
if (v.type == FcTypeString) {
flen = strlen((char *)v.u.s);
ok = FcFalse;
if (l == 0) ok = FcTrue;
for ( j = 0; j < l && !ok; j++) {
ext = PyBytes_AS_STRING(PySequence_ITEM(exts, j));
extlen = PyBytes_GET_SIZE(PySequence_ITEM(exts, j));
ok = flen > extlen && extlen > 0 &&
PyOS_strnicmp(ext, ((char *)v.u.s) + (flen - extlen), extlen) == 0;
}
if (ok) {
if (FcPatternGet(temp, FC_FAMILY, 0, &w) != FcResultMatch) continue;
if (w.type != FcTypeString) continue;
t = PyString_FromString((char *)w.u.s);
if (t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (PyList_Append(ans, t) != 0)
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
}
}
fontconfig_cleanup_find(pat, oset, fs);
Py_INCREF(ans);
return ans;
}
static PyObject *
fontconfig_files_for_family(PyObject *self, PyObject *args) {
char *family; int i;
FcPattern *pat, *tp;
FcObjectSet *oset;
FcFontSet *fs;
FcValue file, weight, fullname, style, slant, family2;
PyObject *ans, *temp, *t;
if (!PyArg_ParseTuple(args, "s", &family))
return NULL;
ans = PyList_New(0);
if (ans == NULL) return PyErr_NoMemory();
fs = NULL; oset = NULL; pat = NULL;
pat = FcPatternBuild(0, FC_FAMILY, FcTypeString, family, (char *) 0);
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
oset = FcObjectSetCreate();
if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FILE)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_STYLE)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_SLANT)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_WEIGHT)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, FC_FAMILY)) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcObjectSetAdd(oset, "fullname")) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
fs = FcFontList(FcConfigGetCurrent(), pat, oset);
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
for (i = 0; i < fs->nfont; i++) {
tp = fs->fonts[i];
if (tp == NULL) continue;
if (FcPatternGet(tp, FC_FILE, 0, &file) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_STYLE, 0, &style) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_WEIGHT, 0, &weight) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_SLANT, 0, &slant) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_FAMILY, 0, &family2) != FcResultMatch) continue;
if (FcPatternGet(tp, "fullname", 0, &fullname) != FcResultMatch) continue;
temp = PyTuple_New(6);
if(temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
t = PyBytes_FromString((char *)fullname.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 0, t);
t = PyBytes_FromString((char *)file.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 1, t);
t = PyBytes_FromString((char *)style.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 2, t);
t = PyBytes_FromString((char *)family2.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 3, t);
t = PyInt_FromLong((long)weight.u.i);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 4, t);
t = PyInt_FromLong((long)slant.u.i);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 5, t);
if (PyList_Append(ans, temp) != 0)
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
fontconfig_cleanup_find(pat, oset, fs);
Py_INCREF(ans);
return ans;
}
static PyObject *
fontconfig_match(PyObject *self, PyObject *args) {
FcChar8 *namespec; int i;
FcPattern *pat, *tp;
FcObjectSet *oset;
FcFontSet *fs, *fs2;
FcValue file, weight, fullname, style, slant, family;
FcResult res;
PyObject *ans, *temp, *t, *all, *verbose;
if (!PyArg_ParseTuple(args, "sOO", &namespec, &all, &verbose))
return NULL;
ans = PyList_New(0);
if (ans == NULL) return PyErr_NoMemory();
fs = NULL; oset = NULL; pat = NULL; fs2 = NULL;
pat = FcNameParse(namespec);
if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (PyObject_IsTrue(verbose)) FcPatternPrint(pat);
if (!FcConfigSubstitute(FcConfigGetCurrent(), pat, FcMatchPattern))
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
FcDefaultSubstitute(pat);
fs = FcFontSetCreate();
if (fs == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (PyObject_IsTrue(all)) {
fs2 = FcFontSort(FcConfigGetCurrent(), pat, FcTrue, NULL, &res);
if (fs2 == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
for (i = 0; i < fs2->nfont; i++) {
tp = fs2->fonts[i];
if (tp == NULL) continue;
tp = FcFontRenderPrepare(FcConfigGetCurrent(), pat, tp);
if (tp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcFontSetAdd(fs, tp))
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
if (fs2 != NULL) FcFontSetDestroy(fs2);
} else {
tp = FcFontMatch(FcConfigGetCurrent(), pat, &res);
if (tp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
if (!FcFontSetAdd(fs, tp))
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
for (i = 0; i < fs->nfont; i++) {
tp = fs->fonts[i];
if (tp == NULL) continue;
if (FcPatternGet(tp, FC_FILE, 0, &file) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_STYLE, 0, &style) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_WEIGHT, 0, &weight) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_SLANT, 0, &slant) != FcResultMatch) continue;
if (FcPatternGet(tp, FC_FAMILY, 0, &family) != FcResultMatch) continue;
if (FcPatternGet(tp, "fullname", 0, &fullname) != FcResultMatch) continue;
temp = PyTuple_New(6);
if(temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
t = PyBytes_FromString((char *)fullname.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 0, t);
t = PyBytes_FromString((char *)file.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 1, t);
t = PyBytes_FromString((char *)style.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 2, t);
t = PyBytes_FromString((char *)family.u.s);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 3, t);
t = PyInt_FromLong((long)weight.u.i);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 4, t);
t = PyInt_FromLong((long)slant.u.i);
if(t == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(temp, 5, t);
if (PyList_Append(ans, temp) != 0)
{ fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); }
}
fontconfig_cleanup_find(pat, oset, fs);
Py_INCREF(ans);
return ans;
}
static
PyMethodDef fontconfig_methods[] = {
{"initialize", fontconfig_initialize, METH_VARARGS,
"initialize(path_to_config_file)\n\n"
"Initialize the library. If path to config file is specified it is used instead of the "
"default configuration. Returns True iff the initialization succeeded."
},
{"find_font_families", fontconfig_find_font_families, METH_VARARGS,
"find_font_families(allowed_extensions)\n\n"
"Find all font families on the system for fonts of the specified types. If no "
"types are specified all font families are returned."
},
{"files_for_family", fontconfig_files_for_family, METH_VARARGS,
"files_for_family(family, normalize)\n\n"
"Find all the variants in the font family `family`. "
"Returns a list of tuples. Each tuple is of the form "
"(fullname, path, style, family, weight, slant). "
},
{"match", fontconfig_match, METH_VARARGS,
"match(namespec,all,verbose)\n\n"
"Find all system fonts that match namespec, in decreasing order "
"of closeness. "
"Returns a list of tuples. Each tuple is of the form "
"(fullname, path, style, family, weight, slant). "
},
{"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}
};
PyMODINIT_FUNC
initfontconfig(void) {
PyObject *m;
m = Py_InitModule3(
"fontconfig", fontconfig_methods,
"Find fonts."
);
if (m == NULL) return;
}

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import threading, unicodedata
import threading
from functools import wraps
from future_builtins import map
@ -20,10 +20,6 @@ class ThreadingViolation(Exception):
'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):
@ -55,10 +51,18 @@ class Face(object):
if not isinstance(text, unicode):
raise TypeError('%r is not a unicode object'%text)
if has_non_printable_chars:
from calibre.utils.fonts.utils import get_printable_characters
text = get_printable_characters(text)
chars = tuple(frozenset(map(ord, text)))
return self.face.supports_text(chars)
@same_thread
def glyph_ids(self, text):
if not isinstance(text, unicode):
raise TypeError('%r is not a unicode object'%text)
for char in text:
yield self.face.glyph_id(ord(char))
class FreeType(object):
def __init__(self):
@ -73,26 +77,4 @@ class FreeType(object):
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()

View File

@ -22,6 +22,7 @@ typedef struct {
// ensure it is garbage collected before the library object, to prevent
// segfaults.
PyObject *library;
PyObject *data;
} Face;
typedef struct {
@ -40,9 +41,12 @@ Face_dealloc(Face* self)
}
self->face = NULL;
Py_DECREF(self->library);
Py_XDECREF(self->library);
self->library = NULL;
Py_XDECREF(self->data);
self->data = NULL;
self->ob_type->tp_free((PyObject*)self);
}
@ -55,8 +59,6 @@ Face_init(Face *self, PyObject *args, PyObject *kwds)
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,
@ -70,6 +72,10 @@ Face_init(Face *self, PyObject *args, PyObject *kwds)
PyErr_Format(FreeTypeError, "Failed to initialize the Font with error: 0x%x", error);
return -1;
}
self->library = ft;
Py_XINCREF(ft);
self->data = PySequence_GetItem(args, 1);
return 0;
}
@ -109,6 +115,14 @@ supports_text(Face *self, PyObject *args) {
return ret;
}
static PyObject*
glyph_id(Face *self, PyObject *args) {
unsigned long code;
if (!PyArg_ParseTuple(args, "k", &code)) return NULL;
return Py_BuildValue("k", (unsigned long)FT_Get_Char_Index(self->face, (FT_ULong)code));
}
static PyGetSetDef Face_getsetters[] = {
{(char *)"family_name",
(getter)family_name, NULL,
@ -128,6 +142,10 @@ static PyMethodDef Face_methods[] = {
"supports_text(sequence of unicode character codes) -> Return True iff this font has glyphs for all the specified characters."
},
{"glyph_id", (PyCFunction)glyph_id, METH_VARARGS,
"glyph_id(character code) -> Returns the glyph id for the specified character code."
},
{NULL} /* Sentinel */
};

View File

@ -0,0 +1,119 @@
#!/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 io import BytesIO
from struct import calcsize, unpack, unpack_from
from collections import namedtuple
from calibre.utils.fonts.utils import get_font_names2, get_font_characteristics
class UnsupportedFont(ValueError):
pass
FontCharacteristics = namedtuple('FontCharacteristics',
'weight, is_italic, is_bold, is_regular, fs_type, panose, width, is_oblique, is_wws, os2_version')
FontNames = namedtuple('FontNames',
'family_name, subfamily_name, full_name, preferred_family_name, preferred_subfamily_name, wws_family_name, wws_subfamily_name')
class FontMetadata(object):
def __init__(self, bytes_or_stream):
if not hasattr(bytes_or_stream, 'read'):
bytes_or_stream = BytesIO(bytes_or_stream)
f = bytes_or_stream
f.seek(0)
header = f.read(4)
if header not in {b'\x00\x01\x00\x00', b'OTTO'}:
raise UnsupportedFont('Not a supported sfnt variant')
self.is_otf = header == b'OTTO'
self.read_table_metadata(f)
self.read_names(f)
self.read_characteristics(f)
f.seek(0)
self.font_family = (self.names.wws_family_name or
self.names.preferred_family_name or self.names.family_name)
wt = self.characteristics.weight
if wt == 400:
wt = 'normal'
elif wt == 700:
wt = 'bold'
else:
wt = type(u'')(wt)
self.font_weight = wt
self.font_stretch = ('ultra-condensed', 'extra-condensed',
'condensed', 'semi-condensed', 'normal', 'semi-expanded',
'expanded', 'extra-expanded', 'ultra-expanded')[
self.characteristics.width-1]
if self.characteristics.is_oblique:
self.font_style = 'oblique'
elif self.characteristics.is_italic:
self.font_style = 'italic'
else:
self.font_style = 'normal'
def read_table_metadata(self, f):
f.seek(4)
num_tables = unpack(b'>H', f.read(2))[0]
# Start of table record entries
f.seek(4 + 4*2)
table_record = b'>4s3L'
sz = calcsize(table_record)
self.tables = {}
block = f.read(sz * num_tables)
for i in xrange(num_tables):
table_tag, table_checksum, table_offset, table_length = \
unpack_from(table_record, block, i*sz)
self.tables[table_tag.lower()] = (table_offset, table_length,
table_checksum)
def read_names(self, f):
if b'name' not in self.tables:
raise UnsupportedFont('This font has no name table')
toff, tlen = self.tables[b'name'][:2]
f.seek(toff)
table = f.read(tlen)
if len(table) != tlen:
raise UnsupportedFont('This font has a name table of incorrect length')
vals = get_font_names2(table, raw_is_table=True)
self.names = FontNames(*vals)
def read_characteristics(self, f):
if b'os/2' not in self.tables:
raise UnsupportedFont('This font has no OS/2 table')
toff, tlen = self.tables[b'os/2'][:2]
f.seek(toff)
table = f.read(tlen)
if len(table) != tlen:
raise UnsupportedFont('This font has an OS/2 table of incorrect length')
vals = get_font_characteristics(table, raw_is_table=True)
self.characteristics = FontCharacteristics(*vals)
def to_dict(self):
ans = {
'is_otf':self.is_otf,
'font-family':self.font_family,
'font-weight':self.font_weight,
'font-style':self.font_style,
'font-stretch':self.font_stretch
}
for f in self.names._fields:
ans[f] = getattr(self.names, f)
for f in self.characteristics._fields:
ans[f] = getattr(self.characteristics, f)
return ans
if __name__ == '__main__':
import sys
with open(sys.argv[-1], 'rb') as f:
fm = FontMetadata(f)
import pprint
pprint.pprint(fm.to_dict())

View File

@ -0,0 +1,315 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
from collections import defaultdict
from threading import Thread
from calibre import walk, prints, as_unicode
from calibre.constants import (config_dir, iswindows, isosx, plugins, DEBUG,
isworker)
from calibre.utils.fonts.metadata import FontMetadata, UnsupportedFont
from calibre.utils.icu import sort_key
class NoFonts(ValueError):
pass
def font_dirs():
if iswindows:
winutil, err = plugins['winutil']
if err:
raise RuntimeError('Failed to load winutil: %s'%err)
return [winutil.special_folder_path(winutil.CSIDL_FONTS)]
if isosx:
return [
'/Library/Fonts',
'/System/Library/Fonts',
'/usr/share/fonts',
'/var/root/Library/Fonts',
os.path.expanduser('~/.fonts'),
os.path.expanduser('~/Library/Fonts'),
]
return [
'/opt/share/fonts',
'/usr/share/fonts',
'/usr/local/share/fonts',
os.path.expanduser('~/.fonts')
]
class Scanner(Thread):
CACHE_VERSION = 1
def __init__(self, folders=[], allowed_extensions={'ttf', 'otf'}):
Thread.__init__(self)
self.folders = folders + font_dirs() + [os.path.join(config_dir, 'fonts'),
P('fonts/liberation')]
self.folders = [os.path.normcase(os.path.abspath(f)) for f in
self.folders]
self.font_families = ()
self.allowed_extensions = allowed_extensions
# API {{{
def find_font_families(self):
self.join()
return self.font_families
def fonts_for_family(self, family):
'''
Return a list of the faces belonging to the specified family. The first
face is the "Regular" face of family. Each face is a dictionary with
many keys, the most important of which are: path, font-family,
font-weight, font-style, font-stretch. The font-* properties follow the
CSS 3 Fonts specification.
'''
self.join()
try:
return self.font_family_map[icu_lower(family)]
except KeyError:
raise NoFonts('No fonts found for the family: %r'%family)
def legacy_fonts_for_family(self, family):
'''
Return a simple set of regular, bold, italic and bold-italic faces for
the specified family. Returns a dictionary with each element being a
2-tuple of (path to font, full font name) and the keys being: normal,
bold, italic, bi.
'''
ans = {}
try:
faces = self.fonts_for_family(family)
except NoFonts:
return ans
for i, face in enumerate(faces):
if i == 0:
key = 'normal'
elif face['font-style'] in {'italic', 'oblique'}:
key = 'bi' if face['font-weight'] == 'bold' else 'italic'
elif face['font-weight'] == 'bold':
key = 'bold'
else:
continue
ans[key] = (face['path'], face['full_name'])
return ans
def get_font_data(self, font_or_path):
path = font_or_path
if isinstance(font_or_path, dict):
path = font_or_path['path']
with lopen(path, 'rb') as f:
return f.read()
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.utils import (supports_text,
panose_to_css_generic_family, get_printable_characters)
if not isinstance(text, unicode):
raise TypeError(u'%r is not unicode'%text)
text = get_printable_characters(text)
found = {}
def filter_faces(font):
try:
raw = self.get_font_data(font)
return supports_text(raw, text)
except:
pass
return False
for family in self.find_font_families():
faces = filter(filter_faces, self.fonts_for_family(family))
if not faces: continue
generic_family = panose_to_css_generic_family(faces[0]['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
# }}}
def reload_cache(self):
if not hasattr(self, 'cache'):
from calibre.utils.config import JSONConfig
self.cache = JSONConfig('fonts/scanner_cache')
else:
self.cache.refresh()
if self.cache.get('version', None) != self.CACHE_VERSION:
self.cache.clear()
self.cached_fonts = self.cache.get('fonts', {})
def run(self):
self.do_scan()
def do_scan(self):
self.reload_cache()
num = 0
for folder in self.folders:
if isworker:
# Dont scan font files in worker processes, use whatever is
# cached.
continue
if not os.path.isdir(folder):
continue
try:
files = tuple(walk(folder))
except EnvironmentError as e:
if DEBUG:
prints('Failed to walk font folder:', folder,
as_unicode(e))
continue
for candidate in files:
if (candidate.rpartition('.')[-1].lower() not in self.allowed_extensions
or not os.path.isfile(candidate)):
continue
candidate = os.path.normcase(os.path.abspath(candidate))
try:
s = os.stat(candidate)
except EnvironmentError:
continue
fileid = '{0}||{1}:{2}'.format(candidate, s.st_size, s.st_mtime)
if fileid in self.cached_fonts:
continue
try:
self.read_font_metadata(candidate, fileid)
except Exception as e:
if DEBUG:
prints('Failed to read metadata from font file:',
candidate, as_unicode(e))
continue
num += 1
if num >= 10:
num = 0
self.write_cache()
if num > 0:
self.write_cache()
self.build_families()
def font_priority(self, font):
'''
Try to ensure that the "Regular" face is the first font for a given
family.
'''
style_normal = font['font-style'] == 'normal'
width_normal = font['font-stretch'] == 'normal'
weight_normal = font['font-weight'] == 'normal'
num_normal = sum(filter(None, (style_normal, width_normal,
weight_normal)))
subfamily_name = (font['wws_subfamily_name'] or
font['preferred_subfamily_name'] or font['subfamily_name'])
if num_normal == 3 and subfamily_name == 'Regular':
return 0
if num_normal == 3:
return 1
if subfamily_name == 'Regular':
return 2
return 3 + (3 - num_normal)
def build_families(self):
families = defaultdict(list)
for f in self.cached_fonts.itervalues():
if not f: continue
lf = icu_lower(f['font-family'] or '')
if lf:
families[lf].append(f)
for fonts in families.itervalues():
# Look for duplicate font files and choose the copy that is from a
# more significant font directory (prefer user directories over
# system directories).
fmap = {}
remove = []
for f in fonts:
fingerprint = (icu_lower(f['font-family']), f['font-weight'],
f['font-stretch'], f['font-style'])
if fingerprint in fmap:
opath = fmap[fingerprint]['path']
npath = f['path']
if self.path_significance(npath) >= self.path_significance(opath):
remove.append(fmap[fingerprint])
fmap[fingerprint] = f
else:
remove.append(f)
else:
fmap[fingerprint] = f
for font in remove:
fonts.remove(font)
fonts.sort(key=self.font_priority)
self.font_family_map = dict.copy(families)
self.font_families = tuple(sorted((f[0]['font-family'] for f in
self.font_family_map.itervalues()), key=sort_key))
def path_significance(self, path):
path = os.path.normcase(os.path.abspath(path))
for i, q in enumerate(self.folders):
if path.startswith(q):
return i
return -1
def write_cache(self):
with self.cache:
self.cache['version'] = self.CACHE_VERSION
self.cache['fonts'] = self.cached_fonts
def force_rescan(self):
self.cached_fonts = {}
self.write_cache()
def read_font_metadata(self, path, fileid):
with lopen(path, 'rb') as f:
try:
fm = FontMetadata(f)
except UnsupportedFont:
self.cached_fonts[fileid] = {}
else:
data = fm.to_dict()
data['path'] = path
self.cached_fonts[fileid] = data
def dump_fonts(self):
self.join()
for family in self.font_families:
prints(family)
for font in self.fonts_for_family(family):
prints('\t%s: %s'%(font['full_name'], font['path']))
prints(end='\t')
for key in ('font-stretch', 'font-weight', 'font-style'):
prints('%s: %s'%(key, font[key]), end=' ')
prints()
prints('\tSub-family:', font['wws_subfamily_name'] or
font['preferred_subfamily_name'] or
font['subfamily_name'])
prints()
prints()
font_scanner = Scanner()
font_scanner.start()
def force_rescan():
font_scanner.join()
font_scanner.force_rescan()
font_scanner.run()
if __name__ == '__main__':
font_scanner.dump_fonts()

View File

@ -0,0 +1,628 @@
/*
* sfntly.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 "sfntly.h"
#include <new>
#include <sfntly/port/memory_input_stream.h>
#include <sfntly/port/memory_output_stream.h>
static PyObject *Error = NULL;
static PyObject *NoGlyphs = NULL;
static PyObject *UnsupportedFont = NULL;
// Predicates {{{
CompositePredicate::CompositePredicate(IntegerSet &chars, IntegerList &ranges) :
chars(chars), ranges(ranges) {}
CompositePredicate::~CompositePredicate() {}
bool CompositePredicate::operator()(int32_t character) const {
for (size_t i = 0; i < ranges.size()/2; i++) {
if (ranges[2*i] <= character && character <= ranges[2*i+1]) return true;
}
return chars.count(character) > 0;
}
// }}}
// Font Info {{{
GlyphId::GlyphId(int32_t glyph_id, FontId font_id) : glyph_id_(glyph_id), font_id_(font_id) {}
GlyphId::~GlyphId() {}
bool GlyphId::operator==(const GlyphId& other) const { return glyph_id_ == other.glyph_id(); }
bool GlyphId::operator<(const GlyphId& other) const { return glyph_id_ < other.glyph_id(); }
int32_t GlyphId::glyph_id() const { return glyph_id_; }
void GlyphId::set_glyph_id(const int32_t glyph_id) { glyph_id_ = glyph_id; }
FontId GlyphId::font_id() const { return font_id_; }
void GlyphId::set_font_id(const FontId font_id) { font_id_ = font_id; }
FontInfo::FontInfo() : chars_to_glyph_ids_(new CharacterMap),
resolved_glyph_ids_(new GlyphIdSet), fonts_(new FontIdMap) { }
FontInfo::FontInfo(CharacterMap* chars_to_glyph_ids,
GlyphIdSet* resolved_glyph_ids,
FontIdMap* fonts) {
chars_to_glyph_ids_ = new CharacterMap(chars_to_glyph_ids->begin(),
chars_to_glyph_ids->end());
resolved_glyph_ids_ = new GlyphIdSet(resolved_glyph_ids->begin(),
resolved_glyph_ids->end());
fonts_ = new FontIdMap(fonts->begin(), fonts->end());
}
FontInfo::~FontInfo() {
delete chars_to_glyph_ids_;
delete resolved_glyph_ids_;
delete fonts_;
}
FontDataTable* FontInfo::GetTable(FontId font_id, int32_t tag) {
if (!fonts_)
return NULL;
FontIdMap::iterator it = fonts_->find(font_id);
if (it == fonts_->end())
return NULL;
return it->second->GetTable(tag);
}
const TableMap* FontInfo::GetTableMap(FontId font_id) {
if (!fonts_)
return NULL;
FontIdMap::iterator it = fonts_->find(font_id);
if (it == fonts_->end())
return NULL;
return it->second->GetTableMap();
}
CharacterMap* FontInfo::chars_to_glyph_ids() const { return chars_to_glyph_ids_; }
void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) { *chars_to_glyph_ids_ = *chars_to_glyph_ids; }
GlyphIdSet* FontInfo::resolved_glyph_ids() const { return resolved_glyph_ids_; }
void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) { *resolved_glyph_ids_ = *resolved_glyph_ids; }
FontIdMap* FontInfo::fonts() const { return fonts_; }
void FontInfo::set_fonts(FontIdMap* fonts) { *fonts_ = *fonts; }
FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id) : font_(font), font_id_(font_id),
predicate_(NULL) { Initialize(); }
FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font,
FontId font_id,
CharacterPredicate* predicate) :
font_(font), font_id_(font_id), predicate_(predicate) { Initialize(); }
FontSourcedInfoBuilder::~FontSourcedInfoBuilder() { }
CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() {
if (!cmap_) {
PyErr_SetString(UnsupportedFont, "This font has no format 4 cmap table (usually symbol or asian fonts), subsetting is not supported");
return NULL;
}
CharacterMap* chars_to_glyph_ids = new CharacterMap;
bool success = GetCharacterMap(chars_to_glyph_ids);
if (!success) {
delete chars_to_glyph_ids;
if (!PyErr_Occurred()) PyErr_SetString(Error, "Error creating character map.\n");
return NULL;
}
GlyphIdSet* resolved_glyph_ids = new GlyphIdSet;
success = ResolveCompositeGlyphs(chars_to_glyph_ids, resolved_glyph_ids);
if (!success) {
delete chars_to_glyph_ids;
delete resolved_glyph_ids;
if (!PyErr_Occurred()) PyErr_SetString(Error, "Error resolving composite glyphs.\n");
return NULL;
}
Ptr<FontInfo> font_info = new FontInfo;
font_info->set_chars_to_glyph_ids(chars_to_glyph_ids);
font_info->set_resolved_glyph_ids(resolved_glyph_ids);
FontIdMap* font_id_map = new FontIdMap;
font_id_map->insert(std::make_pair(font_id_, font_));
font_info->set_fonts(font_id_map);
delete chars_to_glyph_ids;
delete resolved_glyph_ids;
delete font_id_map;
return font_info.Detach();
}
bool FontSourcedInfoBuilder::GetCharacterMap(CharacterMap* chars_to_glyph_ids) {
if (!cmap_ || !chars_to_glyph_ids)
return false;
chars_to_glyph_ids->clear();
CMapTable::CMap::CharacterIterator* character_iterator = cmap_->Iterator();
if (!character_iterator)
return false;
while (character_iterator->HasNext()) {
int32_t character = character_iterator->Next();
if (!predicate_ || (*predicate_)(character)) {
chars_to_glyph_ids->insert
(std::make_pair(character,
GlyphId(cmap_->GlyphId(character), font_id_)));
}
}
delete character_iterator;
return true;
}
bool FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids,
GlyphIdSet* resolved_glyph_ids) {
if (!chars_to_glyph_ids || !resolved_glyph_ids)
return false;
resolved_glyph_ids->clear();
resolved_glyph_ids->insert(GlyphId(0, font_id_));
IntegerSet* unresolved_glyph_ids = new IntegerSet;
// Since composite glyph elements might themselves be composite, we would need
// to recursively resolve the elements too. To avoid the recursion we
// create two sets, |unresolved_glyph_ids| for the unresolved glyphs,
// initially containing all the ids and |resolved_glyph_ids|, initially empty.
// We'll remove glyph ids from |unresolved_glyph_ids| until it is empty and,
// if the glyph is composite, add its elements to the unresolved set.
for (CharacterMap::iterator it = chars_to_glyph_ids->begin(),
e = chars_to_glyph_ids->end(); it != e; ++it) {
unresolved_glyph_ids->insert(it->second.glyph_id());
}
// As long as there are unresolved glyph ids.
while (!unresolved_glyph_ids->empty()) {
// Get the corresponding glyph.
if (!loca_table_) {
PyErr_SetString(UnsupportedFont, "This font does not have a loca table. Subsetting is not supported for fonts without loca tables (usually OTF fonts with PostScript (CFF) outlines).");
return false;
}
int32_t glyph_id = *(unresolved_glyph_ids->begin());
unresolved_glyph_ids->erase(unresolved_glyph_ids->begin());
if (glyph_id < 0 || glyph_id > loca_table_->num_glyphs()) {
continue;
}
int32_t length = loca_table_->GlyphLength(glyph_id);
if (length == 0) {
continue;
}
int32_t offset = loca_table_->GlyphOffset(glyph_id);
GlyphPtr glyph;
if (!glyph_table_) {
PyErr_SetString(UnsupportedFont, "This font does not have a glyf table. Subsetting is not supported for fonts without glyf tables (usually OTF fonts with PostScript (CFF) outlines).");
return false;
}
glyph.Attach(glyph_table_->GetGlyph(offset, length));
if (glyph == NULL) {
continue;
}
// Mark the glyph as resolved.
resolved_glyph_ids->insert(GlyphId(glyph_id, font_id_));
// If it is composite, add all its components to the unresolved glyph set.
if (glyph->GlyphType() == GlyphType::kComposite) {
Ptr<GlyphTable::CompositeGlyph> composite_glyph =
down_cast<GlyphTable::CompositeGlyph*>(glyph.p_);
int32_t num_glyphs = composite_glyph->NumGlyphs();
for (int32_t i = 0; i < num_glyphs; ++i) {
int32_t glyph_id = composite_glyph->GlyphIndex(i);
if (resolved_glyph_ids->find(GlyphId(glyph_id, -1))
== resolved_glyph_ids->end()) {
unresolved_glyph_ids->insert(glyph_id);
}
}
}
}
delete unresolved_glyph_ids;
return true;
}
void FontSourcedInfoBuilder::Initialize() {
Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font_->GetTable(Tag::cmap));
// We prefer Windows BMP format 4 cmaps.
cmap_.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP));
// But if none is found,
if (!cmap_) {
return;
}
loca_table_ = down_cast<LocaTable*>(font_->GetTable(Tag::loca));
glyph_table_ = down_cast<GlyphTable*>(font_->GetTable(Tag::glyf));
}
// }}}
// Font Assembler {{{
FontAssembler::FontAssembler(FontInfo* font_info, IntegerSet* table_blacklist) :
table_blacklist_(table_blacklist) {
font_info_ = font_info;
Initialize();
}
FontAssembler::FontAssembler(FontInfo* font_info) : table_blacklist_(NULL) {
font_info_ = font_info;
Initialize();
}
FontAssembler::~FontAssembler() { }
// Assemble a new font from the font info object.
CALLER_ATTACH Font* FontAssembler::Assemble() {
// Assemble tables we can subset.
if (!AssembleCMapTable() || !AssembleGlyphAndLocaTables()) {
return NULL;
}
// For all other tables, either include them unmodified or don't at all.
const TableMap* common_table_map =
font_info_->GetTableMap(font_info_->fonts()->begin()->first);
for (TableMap::const_iterator it = common_table_map->begin(),
e = common_table_map->end(); it != e; ++it) {
if (table_blacklist_
&& table_blacklist_->find(it->first) != table_blacklist_->end()) {
continue;
}
font_builder_->NewTableBuilder(it->first, it->second->ReadFontData());
}
return font_builder_->Build();
}
IntegerSet* FontAssembler::table_blacklist() const { return table_blacklist_; }
void FontAssembler::set_table_blacklist(IntegerSet* table_blacklist) {
table_blacklist_ = table_blacklist;
}
bool FontAssembler::AssembleCMapTable() {
// Creating the new CMapTable and the new format 4 CMap
Ptr<CMapTable::Builder> cmap_table_builder =
down_cast<CMapTable::Builder*>
(font_builder_->NewTableBuilder(Tag::cmap));
if (!cmap_table_builder)
return false;
Ptr<CMapTable::CMapFormat4::Builder> cmap_builder =
down_cast<CMapTable::CMapFormat4::Builder*>
(cmap_table_builder->NewCMapBuilder(CMapFormat::kFormat4,
CMapTable::WINDOWS_BMP));
if (!cmap_builder)
return false;
// Creating the segments and the glyph id array
CharacterMap* chars_to_glyph_ids = font_info_->chars_to_glyph_ids();
SegmentList* segment_list = new SegmentList;
IntegerList* glyph_id_array = new IntegerList;
int32_t last_chararacter = -2;
int32_t last_offset = 0;
Ptr<CMapTable::CMapFormat4::Builder::Segment> current_segment;
// For simplicity, we will have one segment per contiguous range.
// To test the algorithm, we've replaced the original CMap with the CMap
// generated by this code without removing any character.
// Tuffy.ttf: CMap went from 3146 to 3972 bytes (1.7% to 2.17% of file)
// AnonymousPro.ttf: CMap went from 1524 to 1900 bytes (0.96% to 1.2%)
for (CharacterMap::iterator it = chars_to_glyph_ids->begin(),
e = chars_to_glyph_ids->end(); it != e; ++it) {
int32_t character = it->first;
int32_t glyph_id = it->second.glyph_id();
if (character != last_chararacter + 1) { // new segment
if (current_segment != NULL) {
current_segment->set_end_count(last_chararacter);
segment_list->push_back(current_segment);
}
// start_code = character
// end_code = -1 (unknown for now)
// id_delta = 0 (we don't use id_delta for this representation)
// id_range_offset = last_offset (offset into the glyph_id_array)
current_segment =
new CMapTable::CMapFormat4::Builder::
Segment(character, -1, 0, last_offset);
}
glyph_id_array->push_back(glyph_id);
last_offset += DataSize::kSHORT;
last_chararacter = character;
}
// The last segment is still open.
if (glyph_id_array->size() < 1) {
PyErr_SetString(NoGlyphs, "No glyphs for the specified characters found");
return false;
}
current_segment->set_end_count(last_chararacter);
segment_list->push_back(current_segment);
// Updating the id_range_offset for every segment.
for (int32_t i = 0, num_segs = segment_list->size(); i < num_segs; ++i) {
Ptr<CMapTable::CMapFormat4::Builder::Segment> segment = segment_list->at(i);
segment->set_id_range_offset(segment->id_range_offset()
+ (num_segs - i + 1) * DataSize::kSHORT);
}
// Adding the final, required segment.
current_segment =
new CMapTable::CMapFormat4::Builder::Segment(0xffff, 0xffff, 1, 0);
segment_list->push_back(current_segment);
// Writing the segments and glyph id array to the CMap
cmap_builder->set_segments(segment_list);
cmap_builder->set_glyph_id_array(glyph_id_array);
delete segment_list;
delete glyph_id_array;
return true;
}
bool FontAssembler::AssembleGlyphAndLocaTables() {
Ptr<LocaTable::Builder> loca_table_builder =
down_cast<LocaTable::Builder*>
(font_builder_->NewTableBuilder(Tag::loca));
Ptr<GlyphTable::Builder> glyph_table_builder =
down_cast<GlyphTable::Builder*>
(font_builder_->NewTableBuilder(Tag::glyf));
GlyphIdSet* resolved_glyph_ids = font_info_->resolved_glyph_ids();
IntegerList loca_list;
// Basic sanity check: all LOCA tables are of the same size
// This is necessary but not sufficient!
int32_t previous_size = -1;
for (FontIdMap::iterator it = font_info_->fonts()->begin();
it != font_info_->fonts()->end(); ++it) {
Ptr<LocaTable> loca_table =
down_cast<LocaTable*>(font_info_->GetTable(it->first, Tag::loca));
int32_t current_size = loca_table->header_length();
if (previous_size != -1 && current_size != previous_size) {
return false;
}
previous_size = current_size;
}
// Assuming all fonts referenced by the FontInfo are the subsets of the same
// font, their loca tables should all have the same sizes.
// We'll just get the size of the first font's LOCA table for simplicty.
Ptr<LocaTable> first_loca_table =
down_cast<LocaTable*>
(font_info_->GetTable(font_info_->fonts()->begin()->first, Tag::loca));
int32_t num_loca_glyphs = first_loca_table->num_glyphs();
loca_list.resize(num_loca_glyphs);
loca_list.push_back(0);
int32_t last_glyph_id = 0;
int32_t last_offset = 0;
GlyphTable::GlyphBuilderList* glyph_builders =
glyph_table_builder->GlyphBuilders();
for (GlyphIdSet::iterator it = resolved_glyph_ids->begin(),
e = resolved_glyph_ids->end(); it != e; ++it) {
// Get the glyph for this resolved_glyph_id.
int32_t resolved_glyph_id = it->glyph_id();
int32_t font_id = it->font_id();
// Get the LOCA table for the current glyph id.
Ptr<LocaTable> loca_table =
down_cast<LocaTable*>
(font_info_->GetTable(font_id, Tag::loca));
int32_t length = loca_table->GlyphLength(resolved_glyph_id);
int32_t offset = loca_table->GlyphOffset(resolved_glyph_id);
// Get the GLYF table for the current glyph id.
Ptr<GlyphTable> glyph_table =
down_cast<GlyphTable*>
(font_info_->GetTable(font_id, Tag::glyf));
GlyphPtr glyph;
glyph.Attach(glyph_table->GetGlyph(offset, length));
// The data reference by the glyph is copied into a new glyph and
// added to the glyph_builders belonging to the glyph_table_builder.
// When Build gets called, all the glyphs will be built.
Ptr<ReadableFontData> data = glyph->ReadFontData();
Ptr<WritableFontData> copy_data;
copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length()));
data->CopyTo(copy_data);
GlyphBuilderPtr glyph_builder;
glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data));
glyph_builders->push_back(glyph_builder);
// If there are missing glyphs between the last glyph_id and the
// current resolved_glyph_id, since the LOCA table needs to have the same
// size, the offset is kept the same.
for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i)
loca_list[i] = last_offset;
last_offset += length;
loca_list[resolved_glyph_id + 1] = last_offset;
last_glyph_id = resolved_glyph_id + 1;
}
// If there are missing glyph ids, their loca entries must all point
// to the same offset as the last valid glyph id making them all zero length.
for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i)
loca_list[i] = last_offset;
loca_table_builder->SetLocaList(&loca_list);
return true;
}
void FontAssembler::Initialize() {
font_factory_.Attach(FontFactory::GetInstance());
font_builder_.Attach(font_factory_->NewFontBuilder());
}
// }}}
// Subsetters {{{
// Subsets a given font using a character predicate.
PredicateSubsetter::PredicateSubsetter(Font* font, CharacterPredicate* predicate) : font_(font), predicate_(predicate) {}
PredicateSubsetter::~PredicateSubsetter() { }
// Performs subsetting returning the subsetted font.
CALLER_ATTACH Font* PredicateSubsetter::Subset() {
Ptr<FontSourcedInfoBuilder> info_builder =
new FontSourcedInfoBuilder(font_, 0, predicate_);
Ptr<FontInfo> font_info;
font_info.Attach(info_builder->GetFontInfo());
if (!font_info) {
if (!PyErr_Occurred()) PyErr_SetString(Error, "Could not create font info");
return NULL;
}
IntegerSet* table_blacklist = new IntegerSet;
table_blacklist->insert(Tag::DSIG);
Ptr<FontAssembler> font_assembler = new FontAssembler(font_info,
table_blacklist);
Ptr<Font> font_subset;
font_subset.Attach(font_assembler->Assemble());
delete table_blacklist;
if (!font_subset) { if (!PyErr_Occurred()) PyErr_SetString(Error, "Could not subset font"); }
return font_subset.Detach();
}
// }}}
static void get_stats(Font *font, PyObject *dict) {
PyObject *t;
const TableMap* tables = font->GetTableMap();
for (TableMap::const_iterator it = tables->begin(),
e = tables->end(); it != e; ++it) {
t = PyInt_FromLong(it->second->DataLength());
if (t != NULL) {
PyDict_SetItemString(dict, TagToString(it->first), t);
Py_DECREF(t);
}
}
}
static PyObject*
do_subset(const char *data, Py_ssize_t sz, Ptr<CharacterPredicate> &predicate) {
FontPtr font;
Ptr<FontFactory> font_factory;
FontArray fonts;
MemoryInputStream stream;
PyObject *stats, *stats2;
if (!stream.Attach(reinterpret_cast<const byte_t*>(data), sz))
return PyErr_NoMemory();
font_factory.Attach(FontFactory::GetInstance());
font_factory->LoadFonts(&stream, &fonts);
if (fonts.empty() || fonts[0] == NULL) {
PyErr_SetString(Error, "Failed to load font from provided data.");
return NULL;
}
font = fonts[0];
if (font->num_tables() == 0) {
PyErr_SetString(Error, "Loaded font has 0 tables.");
return NULL;
}
Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap));
if (!cmap_table) {
PyErr_SetString(Error, "Loaded font has no cmap table.");
return NULL;
}
Ptr<PredicateSubsetter> subsetter = new PredicateSubsetter(font, predicate);
Ptr<Font> new_font;
new_font.Attach(subsetter->Subset());
if (!new_font) return NULL;
Ptr<FontFactory> ff;
ff.Attach(FontFactory::GetInstance());
MemoryOutputStream output_stream;
ff->SerializeFont(new_font, &output_stream);
stats = PyDict_New(); stats2 = PyDict_New();
if (stats == NULL || stats2 == NULL) return PyErr_NoMemory();
get_stats(font, stats);
get_stats(new_font, stats2);
return Py_BuildValue("s#NN", (char*)output_stream.Get(), output_stream.Size(), stats, stats2);
}
static PyObject*
subset(PyObject *self, PyObject *args) {
const char *data;
Py_ssize_t sz;
PyObject *individual_chars, *ranges, *t;
int32_t temp;
if (!PyArg_ParseTuple(args, "s#OO", &data, &sz, &individual_chars, &ranges)) return NULL;
if (!PyTuple_Check(individual_chars) || !PyTuple_Check(ranges)) {
PyErr_SetString(PyExc_TypeError, "individual_chars and ranges must be tuples");
return NULL;
}
if (PyTuple_Size(ranges) < 1 && PyTuple_Size(individual_chars) < 1) {
PyErr_SetString(NoGlyphs, "No characters specified");
return NULL;
}
IntegerSet chars;
for (Py_ssize_t i = 0; i < PyTuple_Size(individual_chars); i++) {
temp = (int32_t)PyInt_AsLong(PyTuple_GET_ITEM(individual_chars, i));
if (temp == -1 && PyErr_Occurred()) return NULL;
chars.insert(temp);
}
IntegerList cranges;
cranges.resize(2*PyTuple_Size(ranges));
for (Py_ssize_t i = 0; i < PyTuple_Size(ranges); i++) {
t = PyTuple_GET_ITEM(ranges, i);
if (!PyTuple_Check(t) || PyTuple_Size(t) != 2) {
PyErr_SetString(PyExc_TypeError, "ranges must contain only 2-tuples");
return NULL;
}
for (Py_ssize_t j = 0; j < 2; j++) {
cranges[2*i+j] = (int32_t)PyInt_AsLong(PyTuple_GET_ITEM(t, j));
if (cranges[2*i+j] == -1 && PyErr_Occurred()) return NULL;
}
}
Ptr<CharacterPredicate> predicate = new (std::nothrow) CompositePredicate(chars, cranges);
if (predicate == NULL) return PyErr_NoMemory();
try {
return do_subset(data, sz, predicate);
} catch (std::exception &e) {
PyErr_SetString(Error, e.what());
return NULL;
} catch (...) {
PyErr_SetString(Error, "An unknown exception occurred while subsetting");
return NULL;
}
}
static
PyMethodDef methods[] = {
{"subset", (PyCFunction)subset, METH_VARARGS,
"subset(bytestring, individual_chars, ranges) -> Subset the sfnt in bytestring, keeping only characters specified by individual_chars and ranges. Returns the subset font as a bytestring and the sizes of all font tables in the old and new fonts."
},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initsfntly(void) {
PyObject *m;
m = Py_InitModule3(
"sfntly", methods,
"Wrapper for the Google sfntly library"
);
if (m == NULL) return;
Error = PyErr_NewException((char*)"sfntly.Error", NULL, NULL);
if (Error == NULL) return;
PyModule_AddObject(m, "Error", Error);
NoGlyphs = PyErr_NewException((char*)"sfntly.NoGlyphs", NULL, NULL);
if (NoGlyphs == NULL) return;
PyModule_AddObject(m, "NoGlyphs", NoGlyphs);
UnsupportedFont = PyErr_NewException((char*)"sfntly.UnsupportedFont", NULL, NULL);
if (UnsupportedFont == NULL) return;
PyModule_AddObject(m, "UnsupportedFont", UnsupportedFont);
}

View File

@ -0,0 +1,196 @@
/*
* sfntly.h
* Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include <map>
#include <set>
#include <sfntly/tag.h>
#include <sfntly/font.h>
#include <sfntly/font_factory.h>
#include <sfntly/port/exception_type.h>
#include <sfntly/table/truetype/loca_table.h>
#include <sfntly/table/truetype/glyph_table.h>
#include <sfntly/tools/subsetter/subsetter.h>
using namespace sfntly;
typedef int32_t FontId;
typedef std::map<FontId, Ptr<Font> > FontIdMap;
class CharacterPredicate : virtual public RefCount {
public:
CharacterPredicate() {}
virtual ~CharacterPredicate() {}
virtual bool operator()(int32_t character) const = 0;
};
class CompositePredicate : public CharacterPredicate,
public RefCounted<CompositePredicate> {
public:
CompositePredicate(IntegerSet &chars, IntegerList &ranges);
~CompositePredicate();
virtual bool operator()(int32_t character) const;
private:
IntegerSet chars;
IntegerList ranges;
};
// Glyph id pair that contains the loca table glyph id as well as the
// font id that has the glyph table this glyph belongs to.
class GlyphId {
public:
GlyphId(int32_t glyph_id, FontId font_id);
~GlyphId();
bool operator==(const GlyphId& other) const;
bool operator<(const GlyphId& other) const;
int32_t glyph_id() const;
void set_glyph_id(const int32_t glyph_id);
FontId font_id() const;
void set_font_id(const FontId font_id);
private:
int32_t glyph_id_;
FontId font_id_;
};
typedef std::map<int32_t, GlyphId> CharacterMap;
typedef std::set<GlyphId> GlyphIdSet;
// Font information used for FontAssembler in the construction of a new font.
// Will make copies of character map, glyph id set and font id map.
class FontInfo : public RefCounted<FontInfo> {
public:
// Empty FontInfo object.
FontInfo();
// chars_to_glyph_ids maps characters to GlyphIds for CMap construction
// resolved_glyph_ids defines GlyphIds which should be in the final font
// fonts is a map of font ids to fonts to reference any needed table
FontInfo(CharacterMap* chars_to_glyph_ids,
GlyphIdSet* resolved_glyph_ids,
FontIdMap* fonts);
virtual ~FontInfo();
// Gets the table with the specified tag from the font corresponding to
// font_id or NULL if there is no such font/table.
// font_id is the id of the font that contains the table
// tag identifies the table to be obtained
virtual FontDataTable* GetTable(FontId font_id, int32_t tag);
// Gets the table map of the font whose id is font_id
virtual const TableMap* GetTableMap(FontId font_id);
CharacterMap* chars_to_glyph_ids() const;
// Takes ownership of the chars_to_glyph_ids CharacterMap.
void set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids);
GlyphIdSet* resolved_glyph_ids() const;
// Takes ownership of the glyph_ids GlyphIdSet.
void set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids);
FontIdMap* fonts() const;
// Takes ownership of the fonts FontIdMap.
void set_fonts(FontIdMap* fonts);
private:
CharacterMap* chars_to_glyph_ids_;
GlyphIdSet* resolved_glyph_ids_;
FontIdMap* fonts_;
};
// FontSourcedInfoBuilder is used to create a FontInfo object from a Font
// optionally specifying a CharacterPredicate to filter out some of
// the font's characters.
// It does not take ownership or copy the values its constructor receives.
class FontSourcedInfoBuilder :
public RefCounted<FontSourcedInfoBuilder> {
public:
FontSourcedInfoBuilder(Font* font, FontId font_id);
FontSourcedInfoBuilder(Font* font,
FontId font_id,
CharacterPredicate* predicate);
virtual ~FontSourcedInfoBuilder();
virtual CALLER_ATTACH FontInfo* GetFontInfo();
protected:
bool GetCharacterMap(CharacterMap* chars_to_glyph_ids);
bool ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids,
GlyphIdSet* resolved_glyph_ids);
void Initialize();
private:
Ptr<Font> font_;
FontId font_id_;
CharacterPredicate* predicate_;
Ptr<CMapTable::CMap> cmap_;
Ptr<LocaTable> loca_table_;
Ptr<GlyphTable> glyph_table_;
};
// Assembles FontInfo into font builders.
// Does not take ownership of data passed to it.
class FontAssembler : public RefCounted<FontAssembler> {
public:
// font_info is the FontInfo which will be used for the new font
// table_blacklist is used to decide which tables to exclude from the
// final font.
FontAssembler(FontInfo* font_info, IntegerSet* table_blacklist);
explicit FontAssembler(FontInfo* font_info);
~FontAssembler();
// Assemble a new font from the font info object.
virtual CALLER_ATTACH Font* Assemble();
IntegerSet* table_blacklist() const;
void set_table_blacklist(IntegerSet* table_blacklist);
protected:
virtual bool AssembleCMapTable();
virtual bool AssembleGlyphAndLocaTables();
virtual void Initialize();
private:
Ptr<FontInfo> font_info_;
Ptr<FontFactory> font_factory_;
Ptr<Font::Builder> font_builder_;
IntegerSet* table_blacklist_;
};
class PredicateSubsetter : public RefCounted<Subsetter> {
public:
PredicateSubsetter(Font* font, CharacterPredicate* predicate);
virtual ~PredicateSubsetter();
// Performs subsetting returning the subsetted font.
virtual CALLER_ATTACH Font* Subset();
private:
Ptr<Font> font_;
Ptr<CharacterPredicate> predicate_;
};

View File

@ -0,0 +1,207 @@
#!/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 future_builtins import map
class NoGlyphs(ValueError):
'Raised when the font has no glyphs for the specified characters'
pass
class UnsupportedFont(ValueError):
'Raised when the font is not supported for subsetting '
'(usually an OTF font with PostScript outlines).'
pass
def load_sfntly():
from calibre.constants import plugins
sfntly, err = plugins['sfntly']
if err:
raise RuntimeError('Failed to load sfntly: %s'%err)
return sfntly
def subset(font_data, individual_chars, ranges=()):
if font_data[:4] not in {b'\x00\x01\x00\x00', b'OTTO', b'true', b'typ1'}:
raise ValueError('Not a supported font file. sfnt_version not recognized: %r'%
font_data[:4])
individual = tuple(sorted(map(ord, individual_chars)))
cranges = []
for s, e in ranges:
sc, ec = map(ord, (s, e))
if ec <= sc:
raise ValueError('The start character %s is after the end'
' character %s'%(s, e))
cranges.append((sc, ec))
sfntly = load_sfntly()
try:
return sfntly.subset(font_data, individual, tuple(cranges))
except sfntly.NoGlyphs:
raise NoGlyphs('No glyphs were found in this font for the'
' specified characters. Subsetting is pointless')
except sfntly.UnsupportedFont as e:
raise UnsupportedFont(type('')(e))
def option_parser():
import textwrap
from calibre.utils.config import OptionParser
parser = OptionParser(usage=textwrap.dedent('''\
%prog [options] input_font_file output_font_file characters_to_keep
Subset the specified font, keeping only the glyphs for the characters in
characters_to_keep. characters_to_keep is a comma separated list of characters of
the form: a,b,c,A-Z,0-9,xyz
You can specify ranges in the list of characters, as shown above.
'''))
parser.add_option('-c', '--codes', default=False, action='store_true',
help='If specified, the list of characters is interpreted as '
'numeric unicode codes instead of characters. So to specify the '
'characters a,b you would use 97,98')
parser.prog = 'subset-font'
return parser
def print_stats(old_stats, new_stats):
from calibre import prints
prints('========= Table comparison (original vs. subset) =========')
prints('Table', ' ', '%10s'%'Size', ' ', 'Percent', ' ', '%10s'%'New Size',
' New Percent')
prints('='*80)
old_total = sum(old_stats.itervalues())
new_total = sum(new_stats.itervalues())
tables = sorted(old_stats.iterkeys(), key=lambda x:old_stats[x],
reverse=True)
for table in tables:
osz = old_stats[table]
op = osz/old_total * 100
nsz = new_stats.get(table, 0)
np = nsz/new_total * 100
suffix = ' | same size'
if nsz != osz:
suffix = ' | reduced to %.1f %%'%(nsz/osz * 100)
prints('%4s'%table, ' ', '%10s'%osz, ' ', '%5.1f %%'%op, ' ',
'%10s'%nsz, ' ', '%5.1f %%'%np, suffix)
prints('='*80)
def test_mem():
load_sfntly()
from calibre.utils.mem import memory
import gc
gc.collect()
start_mem = memory()
raw = P('fonts/liberation/LiberationSerif-Regular.ttf', data=True)
calls = 1000
for i in xrange(calls):
subset(raw, (), (('a', 'z'),))
del raw
for i in xrange(3): gc.collect()
print ('Leaked memory per call:', (memory() - start_mem)/calls*1024, 'KB')
def test():
raw = P('fonts/liberation/LiberationSerif-Regular.ttf', data=True)
sf, old_stats, new_stats = subset(raw, set(('a', 'b', 'c')), ())
if len(sf) > 0.3 * len(raw):
raise Exception('Subsetting failed')
def all():
from calibre.utils.fonts.scanner import font_scanner
failed = []
unsupported = []
total = 0
for family in font_scanner.find_font_families():
for font in font_scanner.fonts_for_family(family):
raw = font_scanner.get_font_data(font)
print ('Subsetting', font['full_name'], end='\t')
total += 1
try:
sf, old_stats, new_stats = subset(raw, set(('a', 'b', 'c')), ())
except NoGlyphs:
continue
except UnsupportedFont as e:
unsupported.append((font['full_name'], font['path'], unicode(e)))
print ('Unsupported!')
continue
except Exception as e:
print ('Failed!')
failed.append((font['full_name'], font['path'], unicode(e)))
else:
print ('Reduced to:', '%.1f'%(
sum(new_stats.itervalues())/sum(old_stats.itervalues())
* 100), '%')
if unsupported:
print ('\n\nUnsupported:')
for name, path, err in unsupported:
print (name, path, err)
print()
if failed:
print ('\n\nFailures:')
for name, path, err in failed:
print (name, path, err)
print()
print('Total:', total, 'Unsupported:', len(unsupported), 'Failed:',
len(failed))
def main(args):
import sys, time
from calibre import prints
parser = option_parser()
opts, args = parser.parse_args(args)
if len(args) < 4 or len(args) > 4:
parser.print_help()
raise SystemExit(1)
iff, off, chars = args[1:]
with open(iff, 'rb') as f:
orig = f.read()
chars = [x.strip() for x in chars.split(',')]
individual, ranges = set(), set()
def not_single(c):
if len(c) > 1:
prints(c, 'is not a single character', file=sys.stderr)
raise SystemExit(1)
for c in chars:
if '-' in c:
parts = [x.strip() for x in c.split('-')]
if len(parts) != 2:
prints('Invalid range:', c, file=sys.stderr)
raise SystemExit(1)
if opts.codes:
parts = tuple(map(unichr, map(int, parts)))
map(not_single, parts)
ranges.add(tuple(parts))
else:
if opts.codes:
c = unichr(int(c))
not_single(c)
individual.add(c)
st = time.time()
sf, old_stats, new_stats = subset(orig, individual, ranges)
taken = time.time() - st
reduced = (len(sf)/len(orig)) * 100
def sz(x):
return '%gKB'%(len(x)/1024.)
print_stats(old_stats, new_stats)
prints('Original size:', sz(orig), 'Subset size:', sz(sf), 'Reduced to: %g%%'%(reduced))
prints('Subsetting took %g seconds'%taken)
with open(off, 'wb') as f:
f.write(sf)
prints('Subset font written to:', off)
if __name__ == '__main__':
try:
import init_calibre
init_calibre
except ImportError:
pass
import sys
main(sys.argv)

View File

@ -14,6 +14,11 @@ from collections import defaultdict
class UnsupportedFont(ValueError):
pass
def get_printable_characters(text):
import unicodedata
return u''.join(x for x in unicodedata.normalize('NFC', text)
if unicodedata.category(x)[0] not in {'C', 'Z', 'M'})
def is_truetype_font(raw):
sfnt_version = raw[:4]
return (sfnt_version in {b'\x00\x01\x00\x00', b'OTTO'}, sfnt_version)
@ -36,15 +41,19 @@ def get_table(raw, name):
return table, table_index, table_offset, table_checksum
return None, None, None, None
def get_font_characteristics(raw):
def get_font_characteristics(raw, raw_is_table=False):
'''
Return (weight, is_italic, is_bold, is_regular, fs_type, panose). These
Return (weight, is_italic, is_bold, is_regular, fs_type, panose, width,
is_oblique, is_wws). These
values are taken from the OS/2 table of the font. See
http://www.microsoft.com/typography/otspec/os2.htm for details
'''
os2_table = get_table(raw, 'os/2')[0]
if os2_table is None:
raise UnsupportedFont('Not a supported font, has no OS/2 table')
if raw_is_table:
os2_table = raw
else:
os2_table = get_table(raw, 'os/2')[0]
if os2_table is None:
raise UnsupportedFont('Not a supported font, has no OS/2 table')
common_fields = b'>Hh3H11h'
(version, char_width, weight, width, fs_type, subscript_x_size,
@ -65,10 +74,12 @@ def get_font_characteristics(raw):
offset += 4
selection, = struct.unpack_from(b'>H', os2_table, offset)
is_italic = (selection & 0b1) != 0
is_bold = (selection & 0b100000) != 0
is_regular = (selection & 0b1000000) != 0
return weight, is_italic, is_bold, is_regular, fs_type, panose
is_italic = (selection & (1 << 0)) != 0
is_bold = (selection & (1 << 5)) != 0
is_regular = (selection & (1 << 6)) != 0
is_wws = (selection & (1 << 8)) != 0
is_oblique = (selection & (1 << 9)) != 0
return weight, is_italic, is_bold, is_regular, fs_type, panose, width, is_oblique, is_wws, version
def panose_to_css_generic_family(panose):
proportion = panose[3]
@ -142,10 +153,13 @@ def decode_name_record(recs):
return None
def get_font_names(raw):
table = get_table(raw, 'name')[0]
if table is None:
raise UnsupportedFont('Not a supported font, has no name table')
def _get_font_names(raw, raw_is_table=False):
if raw_is_table:
table = raw
else:
table = get_table(raw, 'name')[0]
if table is None:
raise UnsupportedFont('Not a supported font, has no name table')
table_type, count, string_offset = struct.unpack_from(b'>3H', table)
records = defaultdict(list)
@ -161,12 +175,32 @@ def get_font_names(raw):
records[name_id].append((platform_id, encoding_id, language_id,
src))
return records
def get_font_names(raw, raw_is_table=False):
records = _get_font_names(raw, raw_is_table)
family_name = decode_name_record(records[1])
subfamily_name = decode_name_record(records[2])
full_name = decode_name_record(records[4])
return family_name, subfamily_name, full_name
def get_font_names2(raw, raw_is_table=False):
records = _get_font_names(raw, raw_is_table)
family_name = decode_name_record(records[1])
subfamily_name = decode_name_record(records[2])
full_name = decode_name_record(records[4])
preferred_family_name = decode_name_record(records[16])
preferred_subfamily_name = decode_name_record(records[17])
wws_family_name = decode_name_record(records[21])
wws_subfamily_name = decode_name_record(records[22])
return (family_name, subfamily_name, full_name, preferred_family_name,
preferred_subfamily_name, wws_family_name, wws_subfamily_name)
def checksum_of_block(raw):
extra = 4 - len(raw)%4
raw += b'\0'*extra
@ -238,25 +272,129 @@ def remove_embed_restriction(raw):
verify_checksums(raw)
return raw
def get_bmp_glyph_ids(table, bmp, codes):
length, language, segcount = struct.unpack_from(b'>3H', table, bmp+2)
array_len = segcount //2
offset = bmp + 7*2
array_sz = 2*array_len
array = b'>%dH'%array_len
end_count = struct.unpack_from(array, table, offset)
offset += array_sz + 2
start_count = struct.unpack_from(array, table, offset)
offset += array_sz
id_delta = struct.unpack_from(array.replace(b'H', b'h'), table, offset)
offset += array_sz
range_offset = struct.unpack_from(array, table, offset)
if length + bmp < offset + array_sz:
raise ValueError('cmap subtable length is too small')
glyph_id_len = (length + bmp - (offset + array_sz))//2
glyph_id_map = struct.unpack_from(b'>%dH'%glyph_id_len, table, offset +
array_sz)
for code in codes:
found = False
for i, ec in enumerate(end_count):
if ec >= code:
sc = start_count[i]
if sc <= code:
found = True
ro = range_offset[i]
if ro == 0:
glyph_id = id_delta[i] + code
else:
idx = ro//2 + (code - sc) + i - array_len
glyph_id = glyph_id_map[idx]
if glyph_id != 0:
glyph_id += id_delta[i]
yield glyph_id % 0x1000
break
if not found:
yield 0
def get_glyph_ids(raw, text, raw_is_table=False):
if not isinstance(text, unicode):
raise TypeError('%r is not a unicode object'%text)
if raw_is_table:
table = raw
else:
table = get_table(raw, 'cmap')[0]
if table is None:
raise UnsupportedFont('Not a supported font, has no cmap table')
version, num_tables = struct.unpack_from(b'>HH', table)
bmp_table = None
for i in xrange(num_tables):
platform_id, encoding_id, offset = struct.unpack_from(b'>HHL', table,
4 + (i*8))
if platform_id == 3 and encoding_id == 1:
table_format = struct.unpack_from(b'>H', table, offset)[0]
if table_format == 4:
bmp_table = offset
break
if bmp_table is None:
raise UnsupportedFont('Not a supported font, has no format 4 cmap table')
for glyph_id in get_bmp_glyph_ids(table, bmp_table, map(ord, text)):
yield glyph_id
def supports_text(raw, text, has_only_printable_chars=False):
if not isinstance(text, unicode):
raise TypeError('%r is not a unicode object'%text)
if not has_only_printable_chars:
text = get_printable_characters(text)
try:
for glyph_id in get_glyph_ids(raw, text):
if glyph_id == 0:
return False
except:
return False
return True
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
ok = supports_text(candidate_font_data, text)
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]
from calibre.utils.fonts.scanner import font_scanner
family, faces = font_scanner.find_font_for_text(text)
if faces:
with lopen(faces[0]['path'], 'rb') as f:
candidate_font_data = f.read()
return candidate_font_data
def test_glyph_ids():
from calibre.utils.fonts.free_type import FreeType
data = P('fonts/liberation/LiberationSerif-Regular.ttf', data=True)
ft = FreeType()
font = ft.load_font(data)
text = u'诶йab'
ft_glyphs = tuple(font.glyph_ids(text))
glyphs = tuple(get_glyph_ids(data, text))
if ft_glyphs != glyphs:
raise Exception('My code and FreeType differ on the glyph ids')
def test_supports_text():
data = P('fonts/calibreSymbols.otf', data=True)
if not supports_text(data, '.\u2605'):
raise RuntimeError('Incorrectly returning that text is not supported')
if supports_text(data, 'abc'):
raise RuntimeError('Incorrectly claiming that text is supported')
def test_find_font():
from calibre.utils.fonts.scanner import font_scanner
abcd = '诶比西迪'
family = font_scanner.find_font_for_text(abcd)[0]
print ('Family for Chinese text:', family)
family = font_scanner.find_font_for_text(abcd)[0]
abcd = 'لوحة المفاتيح العربية'
print ('Family for Arabic text:', family)
def test():
test_glyph_ids()
test_supports_text()
test_find_font()
def main():
import sys, os
for f in sys.argv[1:]:
print (os.path.basename(f))
@ -270,5 +408,5 @@ def test():
if __name__ == '__main__':
test()
main()

203
src/sfntly/COPYING.txt Normal file
View File

@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2011 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,199 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/byte_array.h"
#include <algorithm>
#include "sfntly/port/exception_type.h"
namespace sfntly {
const int32_t ByteArray::COPY_BUFFER_SIZE = 8192;
ByteArray::~ByteArray() {}
int32_t ByteArray::Length() { return filled_length_; }
int32_t ByteArray::Size() { return storage_length_; }
int32_t ByteArray::SetFilledLength(int32_t filled_length) {
filled_length_ = std::min<int32_t>(filled_length, storage_length_);
return filled_length_;
}
int32_t ByteArray::Get(int32_t index) {
return InternalGet(index) & 0xff;
}
int32_t ByteArray::Get(int32_t index, ByteVector* b) {
assert(b);
return Get(index, &((*b)[0]), 0, b->size());
}
int32_t ByteArray::Get(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
assert(b);
if (index < 0 || index >= filled_length_) {
return 0;
}
int32_t actual_length = std::min<int32_t>(length, filled_length_ - index);
return InternalGet(index, b, offset, actual_length);
}
void ByteArray::Put(int32_t index, byte_t b) {
if (index < 0 || index >= Size()) {
#if defined (SFNTLY_NO_EXCEPTION)
return;
#else
throw IndexOutOfBoundException(
"Attempt to write outside the bounds of the data");
#endif
}
InternalPut(index, b);
filled_length_ = std::max<int32_t>(filled_length_, index + 1);
}
int32_t ByteArray::Put(int index, ByteVector* b) {
assert(b);
return Put(index, &((*b)[0]), 0, b->size());
}
int32_t ByteArray::Put(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
assert(b);
if (index < 0 || index >= Size()) {
#if defined (SFNTLY_NO_EXCEPTION)
return 0;
#else
throw IndexOutOfBoundException(
"Attempt to write outside the bounds of the data");
#endif
}
int32_t actual_length = std::min<int32_t>(length, Size() - index);
int32_t bytes_written = InternalPut(index, b, offset, actual_length);
filled_length_ = std::max<int32_t>(filled_length_, index + bytes_written);
return bytes_written;
}
int32_t ByteArray::CopyTo(ByteArray* array) {
return CopyTo(array, 0, Length());
}
int32_t ByteArray::CopyTo(ByteArray* array, int32_t offset, int32_t length) {
return CopyTo(0, array, offset, length);
}
int32_t ByteArray::CopyTo(int32_t dst_offset, ByteArray* array,
int32_t src_offset, int32_t length) {
assert(array);
if (array->Size() < dst_offset + length) { // insufficient space
return -1;
}
ByteVector b(COPY_BUFFER_SIZE);
int32_t bytes_read = 0;
int32_t index = 0;
int32_t remaining_length = length;
int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
while ((bytes_read =
Get(index + src_offset, &(b[0]), 0, buffer_length)) > 0) {
int bytes_written = array->Put(index + dst_offset, &(b[0]), 0, bytes_read);
UNREFERENCED_PARAMETER(bytes_written);
index += bytes_read;
remaining_length -= bytes_read;
buffer_length = std::min<int32_t>(b.size(), remaining_length);
}
return index;
}
int32_t ByteArray::CopyTo(OutputStream* os) {
return CopyTo(os, 0, Length());
}
int32_t ByteArray::CopyTo(OutputStream* os, int32_t offset, int32_t length) {
ByteVector b(COPY_BUFFER_SIZE);
int32_t bytes_read = 0;
int32_t index = 0;
int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
while ((bytes_read = Get(index + offset, &(b[0]), 0, buffer_length)) > 0) {
os->Write(&b, 0, bytes_read);
index += bytes_read;
buffer_length = std::min<int32_t>(b.size(), length - index);
}
return index;
}
bool ByteArray::CopyFrom(InputStream* is, int32_t length) {
ByteVector b(COPY_BUFFER_SIZE);
int32_t bytes_read = 0;
int32_t index = 0;
int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
if (Put(index, &(b[0]), 0, bytes_read) != bytes_read) {
#if defined (SFNTLY_NO_EXCEPTION)
return 0;
#else
throw IOException("Error writing bytes.");
#endif
}
index += bytes_read;
length -= bytes_read;
buffer_length = std::min<int32_t>(b.size(), length);
}
return true;
}
bool ByteArray::CopyFrom(InputStream* is) {
ByteVector b(COPY_BUFFER_SIZE);
int32_t bytes_read = 0;
int32_t index = 0;
int32_t buffer_length = COPY_BUFFER_SIZE;
while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
if (Put(index, &b[0], 0, bytes_read) != bytes_read) {
#if defined (SFNTLY_NO_EXCEPTION)
return 0;
#else
throw IOException("Error writing bytes.");
#endif
}
index += bytes_read;
}
return true;
}
ByteArray::ByteArray(int32_t filled_length,
int32_t storage_length,
bool growable) {
Init(filled_length, storage_length, growable);
}
ByteArray::ByteArray(int32_t filled_length, int32_t storage_length) {
Init(filled_length, storage_length, false);
}
void ByteArray::Init(int32_t filled_length,
int32_t storage_length,
bool growable) {
storage_length_ = storage_length;
growable_ = growable;
SetFilledLength(filled_length);
}
} // namespace sfntly

View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2011 The sfntly Open Source Project
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
#include "sfntly/port/refcount.h"
#include "sfntly/port/type.h"
#include "sfntly/port/input_stream.h"
#include "sfntly/port/output_stream.h"
namespace sfntly {
// An abstraction to a contiguous array of bytes.
// C++ port of this class assumes that the data are stored in a linear region
// like std::vector.
class ByteArray : virtual public RefCount {
public:
virtual ~ByteArray();
// Gets the current filled and readable length of the array.
int32_t Length();
// Gets the maximum size of the array. This is the maximum number of bytes that
// the array can hold and all of it may not be filled with data or even fully
// allocated yet.
int32_t Size();
// Determines whether or not this array is growable or of fixed size.
bool growable() { return growable_; }
int32_t SetFilledLength(int32_t filled_length);
// Gets the byte from the given index.
// @param index the index into the byte array
// @return the byte or -1 if reading beyond the bounds of the data
virtual int32_t Get(int32_t index);
// Gets the bytes from the given index and fill the buffer with them. As many
// bytes as will fit into the buffer are read unless that would go past the
// end of the array.
// @param index the index into the byte array
// @param b the buffer to put the bytes read into
// @return the number of bytes read from the buffer
virtual int32_t Get(int32_t index, ByteVector* b);
// Gets the bytes from the given index and fill the buffer with them starting
// at the offset given. As many bytes as the specified length are read unless
// that would go past the end of the array.
// @param index the index into the byte array
// @param b the buffer to put the bytes read into
// @param offset the location in the buffer to start putting the bytes
// @param length the number of bytes to put into the buffer
// @return the number of bytes read from the buffer
virtual int32_t Get(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
// Puts the specified byte into the array at the given index unless that would
// be beyond the length of the array and it isn't growable.
virtual void Put(int32_t index, byte_t b);
// Puts the specified bytes into the array at the given index. The entire
// buffer is put into the array unless that would extend beyond the length and
// the array isn't growable.
virtual int32_t Put(int32_t index, ByteVector* b);
// Puts the specified bytes into the array at the given index. All of the bytes
// specified are put into the array unless that would extend beyond the length
// and the array isn't growable. The bytes to be put into the array are those
// in the buffer from the given offset and for the given length.
// @param index the index into the ByteArray
// @param b the bytes to put into the array
// @param offset the offset in the bytes to start copying from
// @param length the number of bytes to copy into the array
// @return the number of bytes actually written
virtual int32_t Put(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
// Fully copies this ByteArray to another ByteArray to the extent that the
// destination array has storage for the data copied.
virtual int32_t CopyTo(ByteArray* array);
// Copies a segment of this ByteArray to another ByteArray.
// @param array the destination
// @param offset the offset in this ByteArray to start copying from
// @param length the maximum length in bytes to copy
// @return the number of bytes copied
virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length);
// Copies this ByteArray to another ByteArray.
// @param dstOffset the offset in the destination array to start copying to
// @param array the destination
// @param srcOffset the offset in this ByteArray to start copying from
// @param length the maximum length in bytes to copy
// @return the number of bytes copied
virtual int32_t CopyTo(int32_t dst_offset,
ByteArray* array,
int32_t src_offset,
int32_t length);
// Copies this ByteArray to an OutputStream.
// @param os the destination
// @return the number of bytes copied
virtual int32_t CopyTo(OutputStream* os);
// Copies this ByteArray to an OutputStream.
// @param os the destination
// @param offset
// @param length
// @return the number of bytes copied
virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
// Copies from the InputStream into this ByteArray.
// @param is the source
// @param length the number of bytes to copy
virtual bool CopyFrom(InputStream* is, int32_t length);
// Copies everything from the InputStream into this ByteArray.
// @param is the source
virtual bool CopyFrom(InputStream* is);
protected:
// filledLength the length that is "filled" and readable counting from offset.
// storageLength the maximum storage size of the underlying data.
// growable is the storage growable - storageLength is the max growable size.
ByteArray(int32_t filled_length, int32_t storage_length, bool growable);
ByteArray(int32_t filled_length, int32_t storage_length);
void Init(int32_t filled_length, int32_t storage_length, bool growable);
// Internal subclass API
// Stores the byte at the index given.
// @param index the location to store at
// @param b the byte to store
virtual void InternalPut(int32_t index, byte_t b) = 0;
// Stores the array of bytes at the given index.
// @param index the location to store at
// @param b the bytes to store
// @param offset the offset to start from in the byte array
// @param length the length of the byte array to store from the offset
// @return the number of bytes actually stored
virtual int32_t InternalPut(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) = 0;
// Gets the byte at the index given.
// @param index the location to get from
// @return the byte stored at the index
virtual byte_t InternalGet(int32_t index) = 0;
// Gets the bytes at the index given of the given length.
// @param index the location to start getting from
// @param b the array to put the bytes into
// @param offset the offset in the array to put the bytes into
// @param length the length of bytes to read
// @return the number of bytes actually ready
virtual int32_t InternalGet(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) = 0;
// Close this instance of the ByteArray.
virtual void Close() = 0;
// C++ port only, raw pointer to the first element of storage.
virtual byte_t* Begin() = 0;
// Java toString() not ported.
static const int32_t COPY_BUFFER_SIZE;
private:
//bool bound_; // unused, comment out
int32_t filled_length_;
int32_t storage_length_;
bool growable_;
};
typedef Ptr<ByteArray> ByteArrayPtr;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_

View File

@ -0,0 +1,82 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <limits.h>
#include <algorithm>
#include <functional>
#include "sfntly/data/font_data.h"
namespace sfntly {
int32_t FontData::Size() const {
return std::min<int32_t>(array_->Size() - bound_offset_, bound_length_);
}
bool FontData::Bound(int32_t offset, int32_t length) {
if (offset + length > Size() || offset < 0 || length < 0)
return false;
bound_offset_ += offset;
bound_length_ = length;
return true;
}
bool FontData::Bound(int32_t offset) {
if (offset > Size() || offset < 0)
return false;
bound_offset_ += offset;
return true;
}
int32_t FontData::Length() const {
return std::min<int32_t>(array_->Length() - bound_offset_, bound_length_);
}
FontData::FontData(ByteArray* ba) {
Init(ba);
}
FontData::FontData(FontData* data, int32_t offset, int32_t length) {
Init(data->array_);
Bound(data->bound_offset_ + offset, length);
}
FontData::FontData(FontData* data, int32_t offset) {
Init(data->array_);
Bound(data->bound_offset_ + offset,
(data->bound_length_ == GROWABLE_SIZE)
? GROWABLE_SIZE : data->bound_length_ - offset);
}
FontData::~FontData() {}
void FontData::Init(ByteArray* ba) {
array_ = ba;
bound_offset_ = 0;
bound_length_ = GROWABLE_SIZE;
}
int32_t FontData::BoundOffset(int32_t offset) {
return offset + bound_offset_;
}
int32_t FontData::BoundLength(int32_t offset, int32_t length) {
return std::min<int32_t>(length, bound_length_ - offset);
}
} // namespace sfntly

View File

@ -0,0 +1,135 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
#include <limits.h>
#include <vector>
#include "sfntly/port/type.h"
#include "sfntly/data/byte_array.h"
#include "sfntly/port/refcount.h"
namespace sfntly {
struct DataSize {
enum {
kBYTE = 1,
kCHAR = 1,
kUSHORT = 2,
kSHORT = 2,
kUINT24 = 3,
kULONG = 4,
kLONG = 4,
kFixed = 4,
kFUNIT = 4,
kFWORD = 2,
kUFWORD = 2,
kF2DOT14 = 2,
kLONGDATETIME = 8,
kTag = 4,
kGlyphID = 2,
kOffset = 2
};
};
class FontData : virtual public RefCount {
public:
// Gets the maximum size of the FontData. This is the maximum number of bytes
// that the font data can hold and all of it may not be filled with data or
// even fully allocated yet.
// @return the maximum size of this font data
virtual int32_t Size() const;
// Sets limits on the size of the FontData. The FontData is then only
// visible within the bounds set.
// @param offset the start of the new bounds
// @param length the number of bytes in the bounded array
// @return true if the bounding range was successful; false otherwise
virtual bool Bound(int32_t offset, int32_t length);
// Sets limits on the size of the FontData. This is a offset bound only so if
// the FontData is writable and growable then there is no limit to that growth
// from the bounding operation.
// @param offset the start of the new bounds which must be within the current
// size of the FontData
// @return true if the bounding range was successful; false otherwise
virtual bool Bound(int32_t offset);
// Makes a slice of this FontData. The returned slice will share the data with
// the original <code>FontData</code>.
// @param offset the start of the slice
// @param length the number of bytes in the slice
// @return a slice of the original FontData
virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length) = 0;
// Makes a bottom bound only slice of this array. The returned slice will
// share the data with the original <code>FontData</code>.
// @param offset the start of the slice
// @return a slice of the original FontData
virtual CALLER_ATTACH FontData* Slice(int32_t offset) = 0;
// Gets the length of the data.
virtual int32_t Length() const;
protected:
// Constructor.
// @param ba the byte array to use for the backing data
explicit FontData(ByteArray* ba);
// Constructor.
// @param data the data to wrap
// @param offset the offset to start the wrap from
// @param length the length of the data wrapped
FontData(FontData* data, int32_t offset, int32_t length);
// Constructor.
// @param data the data to wrap
// @param offset the offset to start the wrap from
FontData(FontData* data, int32_t offset);
virtual ~FontData();
void Init(ByteArray* ba);
// Gets the offset in the underlying data taking into account any bounds on
// the data.
// @param offset the offset to get the bound compensated offset for
// @return the bound compensated offset
int32_t BoundOffset(int32_t offset);
// Gets the length in the underlying data taking into account any bounds on
// the data.
// @param offset the offset that the length is being used at
// @param length the length to get the bound compensated length for
// @return the bound compensated length
int32_t BoundLength(int32_t offset, int32_t length);
static const int32_t GROWABLE_SIZE = INT_MAX;
// TODO(arthurhsu): style guide violation: refactor this protected member
ByteArrayPtr array_;
private:
int32_t bound_offset_;
int32_t bound_length_;
};
typedef Ptr<FontData> FontDataPtr;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_

View File

@ -0,0 +1,141 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/font_input_stream.h"
#include <algorithm>
namespace sfntly {
FontInputStream::FontInputStream(InputStream* is)
: stream_(is), position_(0), length_(0), bounded_(false) {
}
FontInputStream::FontInputStream(InputStream* is, size_t length)
: stream_(is), position_(0), length_(length), bounded_(true) {
}
FontInputStream::~FontInputStream() {
// Do not close here, underlying InputStream will close themselves.
}
int32_t FontInputStream::Available() {
if (stream_) {
return stream_->Available();
}
return 0;
}
void FontInputStream::Close() {
if (stream_) {
stream_->Close();
}
}
void FontInputStream::Mark(int32_t readlimit) {
if (stream_) {
stream_->Mark(readlimit);
}
}
bool FontInputStream::MarkSupported() {
if (stream_) {
return stream_->MarkSupported();
}
return false;
}
void FontInputStream::Reset() {
if (stream_) {
stream_->Reset();
}
}
int32_t FontInputStream::Read() {
if (!stream_ || (bounded_ && position_ >= length_)) {
return -1;
}
int32_t b = stream_->Read();
if (b >= 0) {
position_++;
}
return b;
}
int32_t FontInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
if (!stream_ || offset < 0 || length < 0 ||
(bounded_ && position_ >= length_)) {
return -1;
}
int32_t bytes_to_read =
bounded_ ? std::min<int32_t>(length, (int32_t)(length_ - position_)) :
length;
int32_t bytes_read = stream_->Read(b, offset, bytes_to_read);
position_ += bytes_read;
return bytes_read;
}
int32_t FontInputStream::Read(ByteVector* b) {
return Read(b, 0, b->size());
}
int32_t FontInputStream::ReadChar() {
return Read();
}
int32_t FontInputStream::ReadUShort() {
return 0xffff & (Read() << 8 | Read());
}
int32_t FontInputStream::ReadShort() {
return ((Read() << 8 | Read()) << 16) >> 16;
}
int32_t FontInputStream::ReadUInt24() {
return 0xffffff & (Read() << 16 | Read() << 8 | Read());
}
int64_t FontInputStream::ReadULong() {
return 0xffffffffL & ReadLong();
}
int32_t FontInputStream::ReadULongAsInt() {
int64_t ulong = ReadULong();
return ((int32_t)ulong) & ~0x80000000;
}
int32_t FontInputStream::ReadLong() {
return Read() << 24 | Read() << 16 | Read() << 8 | Read();
}
int32_t FontInputStream::ReadFixed() {
return ReadLong();
}
int64_t FontInputStream::ReadDateTimeAsLong() {
return (int64_t)ReadULong() << 32 | ReadULong();
}
int64_t FontInputStream::Skip(int64_t n) {
if (stream_) {
int64_t skipped = stream_->Skip(n);
position_ += skipped;
return skipped;
}
return 0;
}
} // namespace sfntly

View File

@ -0,0 +1,97 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
#include "sfntly/port/type.h"
#include "sfntly/port/input_stream.h"
namespace sfntly {
// An input stream for reading font data.
// The data types used are as listed:
// BYTE 8-bit unsigned integer.
// CHAR 8-bit signed integer.
// USHORT 16-bit unsigned integer.
// SHORT 16-bit signed integer.
// UINT24 24-bit unsigned integer.
// ULONG 32-bit unsigned integer.
// LONG 32-bit signed integer.
// Fixed 32-bit signed fixed-point number (16.16)
// FUNIT Smallest measurable distance in the em space.
// FWORD 16-bit signed integer (SHORT) that describes a quantity in FUnits.
// UFWORD 16-bit unsigned integer (USHORT) that describes a quantity in
// FUnits.
// F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14)
// LONGDATETIME Date represented in number of seconds since 12:00 midnight,
// January 1, 1904. The value is represented as a signed 64-bit
// integer.
// Note: Original class inherits from Java's FilterOutputStream, which wraps
// an InputStream within. In C++, we directly do the wrapping without
// defining another layer of abstraction. The wrapped output stream is
// *NOT* reference counted (because it's meaningless to ref-count an I/O
// stream).
class FontInputStream : public InputStream {
public:
// Constructor.
// @param is input stream to wrap
explicit FontInputStream(InputStream* is);
// Constructor for a bounded font input stream.
// @param is input stream to wrap
// @param length the maximum length of bytes to read
FontInputStream(InputStream* is, size_t length);
virtual ~FontInputStream();
virtual int32_t Available();
virtual void Close();
virtual void Mark(int32_t readlimit);
virtual bool MarkSupported();
virtual void Reset();
virtual int32_t Read();
virtual int32_t Read(ByteVector* buffer);
virtual int32_t Read(ByteVector* buffer, int32_t offset, int32_t length);
// Get the current position in the stream in bytes.
// @return the current position in bytes
virtual int64_t position() { return position_; }
virtual int32_t ReadChar();
virtual int32_t ReadUShort();
virtual int32_t ReadShort();
virtual int32_t ReadUInt24();
virtual int64_t ReadULong();
virtual int32_t ReadULongAsInt();
virtual int32_t ReadLong();
virtual int32_t ReadFixed();
virtual int64_t ReadDateTimeAsLong();
virtual int64_t Skip(int64_t n); // n can be negative.
private:
InputStream* stream_;
int64_t position_;
int64_t length_; // Bound on length of data to read.
bool bounded_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_

View File

@ -0,0 +1,130 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/font_output_stream.h"
#include <algorithm>
namespace sfntly {
FontOutputStream::FontOutputStream(OutputStream* os)
: stream_(os),
position_(0) {
}
FontOutputStream::~FontOutputStream() {
// Do not close, underlying stream shall clean up themselves.
}
void FontOutputStream::Write(byte_t b) {
if (stream_) {
stream_->Write(b);
position_++;
}
}
void FontOutputStream::Write(ByteVector* b) {
if (b) {
Write(b, 0, b->size());
position_ += b->size();
}
}
void FontOutputStream::Write(ByteVector* b, int32_t off, int32_t len) {
assert(b);
assert(stream_);
if (off < 0 || len < 0 || off + len < 0 ||
static_cast<size_t>(off + len) > b->size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundException();
#else
return;
#endif
}
stream_->Write(b, off, len);
position_ += len;
}
void FontOutputStream::Write(byte_t* b, int32_t off, int32_t len) {
assert(b);
assert(stream_);
if (off < 0 || len < 0 || off + len < 0) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundException();
#else
return;
#endif
}
stream_->Write(b, off, len);
position_ += len;
}
void FontOutputStream::WriteChar(byte_t c) {
Write(c);
}
void FontOutputStream::WriteUShort(int32_t us) {
Write((byte_t)((us >> 8) & 0xff));
Write((byte_t)(us & 0xff));
}
void FontOutputStream::WriteShort(int32_t s) {
WriteUShort(s);
}
void FontOutputStream::WriteUInt24(int32_t ui) {
Write((byte_t)(ui >> 16) & 0xff);
Write((byte_t)(ui >> 8) & 0xff);
Write((byte_t)ui & 0xff);
}
void FontOutputStream::WriteULong(int64_t ul) {
Write((byte_t)((ul >> 24) & 0xff));
Write((byte_t)((ul >> 16) & 0xff));
Write((byte_t)((ul >> 8) & 0xff));
Write((byte_t)(ul & 0xff));
}
void FontOutputStream::WriteLong(int64_t l) {
WriteULong(l);
}
void FontOutputStream::WriteFixed(int32_t f) {
WriteULong(f);
}
void FontOutputStream::WriteDateTime(int64_t date) {
WriteULong((date >> 32) & 0xffffffff);
WriteULong(date & 0xffffffff);
}
void FontOutputStream::Flush() {
if (stream_) {
stream_->Flush();
}
}
void FontOutputStream::Close() {
if (stream_) {
stream_->Flush();
stream_->Close();
position_ = 0;
}
}
} // namespace sfntly

View File

@ -0,0 +1,79 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
#include "sfntly/port/type.h"
#include "sfntly/port/output_stream.h"
namespace sfntly {
// An output stream for writing font data.
// The data types used are as listed:
// BYTE 8-bit unsigned integer.
// CHAR 8-bit signed integer.
// USHORT 16-bit unsigned integer.
// SHORT 16-bit signed integer.
// UINT24 24-bit unsigned integer.
// ULONG 32-bit unsigned integer.
// LONG 32-bit signed integer.
// Fixed 32-bit signed fixed-point number (16.16)
// FUNIT Smallest measurable distance in the em space.
// FWORD 16-bit signed integer (SHORT) that describes a quantity in FUnits.
// UFWORD 16-bit unsigned integer (USHORT) that describes a quantity in
// FUnits.
// F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14)
// LONGDATETIME Date represented in number of seconds since 12:00 midnight,
// January 1, 1904. The value is represented as a signed 64-bit
// integer.
// Note: The wrapped output stream is *NOT* reference counted (because it's
// meaningless to ref-count an I/O stream).
class FontOutputStream : public OutputStream {
public:
explicit FontOutputStream(OutputStream* os);
virtual ~FontOutputStream();
virtual size_t position() { return position_; }
virtual void Write(byte_t b);
virtual void Write(ByteVector* b);
virtual void Write(ByteVector* b, int32_t off, int32_t len);
virtual void Write(byte_t* b, int32_t off, int32_t len);
virtual void WriteChar(byte_t c);
virtual void WriteUShort(int32_t us);
virtual void WriteShort(int32_t s);
virtual void WriteUInt24(int32_t ui);
virtual void WriteULong(int64_t ul);
virtual void WriteLong(int64_t l);
virtual void WriteFixed(int32_t l);
virtual void WriteDateTime(int64_t date);
// Note: C++ port only.
virtual void Flush();
virtual void Close();
private:
// Note: we do not use the variable name out as in Java because it has
// special meaning in VC++ and will be very confusing.
OutputStream* stream_;
size_t position_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_

View File

@ -0,0 +1,82 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/growable_memory_byte_array.h"
#include <limits.h>
#include <string.h>
#include <algorithm>
namespace sfntly {
GrowableMemoryByteArray::GrowableMemoryByteArray()
: ByteArray(0, INT_MAX, true) {
// Note: We did not set an initial size of array like Java because STL
// implementation will determine the best strategy.
}
GrowableMemoryByteArray::~GrowableMemoryByteArray() {}
int32_t GrowableMemoryByteArray::CopyTo(OutputStream* os,
int32_t offset,
int32_t length) {
assert(os);
os->Write(&b_, offset, length);
return length;
}
void GrowableMemoryByteArray::InternalPut(int32_t index, byte_t b) {
if ((size_t)index >= b_.size()) {
b_.resize((size_t)(index + 1));
}
b_[index] = b;
}
int32_t GrowableMemoryByteArray::InternalPut(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
if ((size_t)index + length >= b_.size()) {
// Note: We grow one byte more than Java version. VC debuggers shows
// data better this way.
b_.resize((size_t)(index + length + 1));
}
std::copy(b + offset, b + offset + length, b_.begin() + index);
return length;
}
byte_t GrowableMemoryByteArray::InternalGet(int32_t index) {
return b_[index];
}
int32_t GrowableMemoryByteArray::InternalGet(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
memcpy(b + offset, &(b_[0]) + index, length);
return length;
}
void GrowableMemoryByteArray::Close() {
b_.clear();
}
byte_t* GrowableMemoryByteArray::Begin() {
return &(b_[0]);
}
} // namespace sfntly

View File

@ -0,0 +1,66 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
#include "sfntly/data/byte_array.h"
namespace sfntly {
// Note: This is not really a port of Java version. Instead, this wraps a
// std::vector inside and let it grow by calling resize().
class GrowableMemoryByteArray : public ByteArray,
public RefCounted<GrowableMemoryByteArray> {
public:
GrowableMemoryByteArray();
virtual ~GrowableMemoryByteArray();
virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
// Make gcc -Woverloaded-virtual happy.
virtual int32_t CopyTo(ByteArray* array) { return ByteArray::CopyTo(array); }
virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length) {
return ByteArray::CopyTo(array, offset, length);
}
virtual int32_t CopyTo(int32_t dst_offset,
ByteArray* array,
int32_t src_offset,
int32_t length) {
return ByteArray::CopyTo(dst_offset, array, src_offset, length);
}
virtual int32_t CopyTo(OutputStream* os) { return ByteArray::CopyTo(os); }
protected:
virtual void InternalPut(int32_t index, byte_t b);
virtual int32_t InternalPut(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
virtual byte_t InternalGet(int32_t index);
virtual int32_t InternalGet(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
virtual void Close();
virtual byte_t* Begin();
private:
ByteVector b_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_

View File

@ -0,0 +1,93 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/memory_byte_array.h"
#include <string.h>
namespace sfntly {
MemoryByteArray::MemoryByteArray(int32_t length)
: ByteArray(0, length), b_(NULL), allocated_(true) {
}
MemoryByteArray::MemoryByteArray(byte_t* b, int32_t filled_length)
: ByteArray(filled_length, filled_length), b_(b), allocated_(false) {
assert(b);
}
MemoryByteArray::~MemoryByteArray() {
Close();
}
int32_t MemoryByteArray::CopyTo(OutputStream* os,
int32_t offset,
int32_t length) {
assert(os);
os->Write(b_, offset, length);
return length;
}
void MemoryByteArray::Init() {
if (allocated_ && b_ == NULL) {
b_ = new byte_t[Size()];
memset(b_, 0, Size());
}
}
void MemoryByteArray::InternalPut(int32_t index, byte_t b) {
Init();
b_[index] = b;
}
int32_t MemoryByteArray::InternalPut(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
assert(b);
Init();
memcpy(b_ + index, b + offset, length);
return length;
}
byte_t MemoryByteArray::InternalGet(int32_t index) {
Init();
return b_[index];
}
int32_t MemoryByteArray::InternalGet(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
assert(b);
Init();
memcpy(b + offset, b_ + index, length);
return length;
}
void MemoryByteArray::Close() {
if (allocated_ && b_) {
delete[] b_;
}
b_ = NULL;
}
byte_t* MemoryByteArray::Begin() {
Init();
return b_;
}
} // namespace sfntly

View File

@ -0,0 +1,81 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
#include "sfntly/data/byte_array.h"
namespace sfntly {
class MemoryByteArray : public ByteArray, public RefCounted<MemoryByteArray> {
public:
// Construct a new MemoryByteArray with a new array of the size given. It is
// assumed that none of the array is filled and readable.
explicit MemoryByteArray(int32_t length);
// Note: not implemented due to dangerous operations in constructor.
//explicit MemoryByteArray(ByteVector* b);
// Construct a new MemoryByteArray using byte array.
// @param b the byte array that provides the actual storage
// @param filled_length the index of the last byte in the array has data
// Note: This is different from Java version, it does not take over the
// ownership of b. Caller is responsible for handling the lifetime
// of b. C++ port also assumes filled_length is buffer_length since
// there is not a reliable way to identify the actual size of buffer.
MemoryByteArray(byte_t* b, int32_t filled_length);
virtual ~MemoryByteArray();
virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
// Make gcc -Woverloaded-virtual happy.
virtual int32_t CopyTo(ByteArray* array) { return ByteArray::CopyTo(array); }
virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length) {
return ByteArray::CopyTo(array, offset, length);
}
virtual int32_t CopyTo(int32_t dst_offset,
ByteArray* array,
int32_t src_offset,
int32_t length) {
return ByteArray::CopyTo(dst_offset, array, src_offset, length);
}
virtual int32_t CopyTo(OutputStream* os) { return ByteArray::CopyTo(os); }
protected:
virtual void InternalPut(int32_t index, byte_t b);
virtual int32_t InternalPut(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
virtual byte_t InternalGet(int32_t index);
virtual int32_t InternalGet(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
virtual void Close();
virtual byte_t* Begin();
private:
void Init(); // C++ port only, used to allocate memory outside constructor.
byte_t* b_;
bool allocated_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_

View File

@ -0,0 +1,336 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/readable_font_data.h"
#include <stdio.h>
#include "sfntly/data/memory_byte_array.h"
#include "sfntly/data/writable_font_data.h"
#include "sfntly/port/exception_type.h"
namespace sfntly {
ReadableFontData::ReadableFontData(ByteArray* array)
: FontData(array),
checksum_set_(false),
checksum_(0) {
}
ReadableFontData::~ReadableFontData() {}
// TODO(arthurhsu): re-investigate the memory model of this function. It's
// not too useful without copying, but it's not performance
// savvy to do copying.
CALLER_ATTACH
ReadableFontData* ReadableFontData::CreateReadableFontData(ByteVector* b) {
assert(b);
ByteArrayPtr ba = new MemoryByteArray(b->size());
ba->Put(0, b);
ReadableFontDataPtr wfd = new ReadableFontData(ba);
return wfd.Detach();
}
int64_t ReadableFontData::Checksum() {
AutoLock lock(checksum_lock_);
if (!checksum_set_) {
ComputeChecksum();
}
return checksum_;
}
void ReadableFontData::SetCheckSumRanges(const IntegerList& ranges) {
checksum_range_ = ranges;
checksum_set_ = false; // UNIMPLEMENTED: atomicity
}
int32_t ReadableFontData::ReadUByte(int32_t index) {
int32_t b = array_->Get(BoundOffset(index));
#if !defined (SFNTLY_NO_EXCEPTION)
if (b < 0) {
throw IndexOutOfBoundException(
"Index attempted to be read from is out of bounds", index);
}
#endif
return b;
}
int32_t ReadableFontData::ReadByte(int32_t index) {
int32_t b = array_->Get(BoundOffset(index));
#if !defined (SFNTLY_NO_EXCEPTION)
if (b < 0) {
throw IndexOutOfBoundException(
"Index attempted to be read from is out of bounds", index);
}
#endif
return (b << 24) >> 24;
}
int32_t ReadableFontData::ReadBytes(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
return array_->Get(BoundOffset(index), b, offset, BoundLength(index, length));
}
int32_t ReadableFontData::ReadChar(int32_t index) {
return ReadUByte(index);
}
int32_t ReadableFontData::ReadUShort(int32_t index) {
return 0xffff & (ReadUByte(index) << 8 | ReadUByte(index + 1));
}
int32_t ReadableFontData::ReadShort(int32_t index) {
return ((ReadByte(index) << 8 | ReadUByte(index + 1)) << 16) >> 16;
}
int32_t ReadableFontData::ReadUInt24(int32_t index) {
return 0xffffff & (ReadUByte(index) << 16 |
ReadUByte(index + 1) << 8 |
ReadUByte(index + 2));
}
int64_t ReadableFontData::ReadULong(int32_t index) {
return 0xffffffffL & (ReadUByte(index) << 24 |
ReadUByte(index + 1) << 16 |
ReadUByte(index + 2) << 8 |
ReadUByte(index + 3));
}
int32_t ReadableFontData::ReadULongAsInt(int32_t index) {
int64_t ulong = ReadULong(index);
#if !defined (SFNTLY_NO_EXCEPTION)
if ((ulong & 0x80000000) == 0x80000000) {
throw ArithmeticException("Long value too large to fit into an integer.");
}
#endif
return static_cast<int32_t>(ulong);
}
int64_t ReadableFontData::ReadULongLE(int32_t index) {
return 0xffffffffL & (ReadUByte(index) |
ReadUByte(index + 1) << 8 |
ReadUByte(index + 2) << 16 |
ReadUByte(index + 3) << 24);
}
int32_t ReadableFontData::ReadLong(int32_t index) {
return ReadByte(index) << 24 |
ReadUByte(index + 1) << 16 |
ReadUByte(index + 2) << 8 |
ReadUByte(index + 3);
}
int32_t ReadableFontData::ReadFixed(int32_t index) {
return ReadLong(index);
}
int64_t ReadableFontData::ReadDateTimeAsLong(int32_t index) {
return (int64_t)ReadULong(index) << 32 | ReadULong(index + 4);
}
int32_t ReadableFontData::ReadFWord(int32_t index) {
return ReadShort(index);
}
int32_t ReadableFontData::ReadFUFWord(int32_t index) {
return ReadUShort(index);
}
int32_t ReadableFontData::CopyTo(OutputStream* os) {
return array_->CopyTo(os, BoundOffset(0), Length());
}
int32_t ReadableFontData::CopyTo(WritableFontData* wfd) {
return array_->CopyTo(wfd->BoundOffset(0),
wfd->array_,
BoundOffset(0),
Length());
}
int32_t ReadableFontData::CopyTo(ByteArray* ba) {
return array_->CopyTo(ba, BoundOffset(0), Length());
}
int32_t ReadableFontData::SearchUShort(int32_t start_index,
int32_t start_offset,
int32_t end_index,
int32_t end_offset,
int32_t length,
int32_t key) {
int32_t location = 0;
int32_t bottom = 0;
int32_t top = length;
while (top != bottom) {
location = (top + bottom) / 2;
int32_t location_start = ReadUShort(start_index + location * start_offset);
if (key < location_start) {
// location is below current location
top = location;
} else {
// is key below the upper bound?
int32_t location_end = ReadUShort(end_index + location * end_offset);
#if defined (SFNTLY_DEBUG_FONTDATA)
fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
#endif
if (key <= location_end) {
return location;
} else {
// location is above the current location
bottom = location + 1;
}
}
}
return -1;
}
int32_t ReadableFontData::SearchUShort(int32_t start_index,
int32_t start_offset,
int32_t length,
int32_t key) {
int32_t location = 0;
int32_t bottom = 0;
int32_t top = length;
while (top != bottom) {
location = (top + bottom) / 2;
int32_t location_start = ReadUShort(start_index + location * start_offset);
if (key < location_start) {
// location is below current location
top = location;
} else if (key > location_start) {
// location is above current location
bottom = location + 1;
} else {
return location;
}
}
return -1;
}
int32_t ReadableFontData::SearchULong(int32_t start_index,
int32_t start_offset,
int32_t end_index,
int32_t end_offset,
int32_t length,
int32_t key) {
int32_t location = 0;
int32_t bottom = 0;
int32_t top = length;
while (top != bottom) {
location = (top + bottom) / 2;
int32_t location_start = ReadULongAsInt(start_index
+ location * start_offset);
if (key < location_start) {
// location is below current location
top = location;
} else {
// is key below the upper bound?
int32_t location_end = ReadULongAsInt(end_index + location * end_offset);
#if defined (SFNTLY_DEBUG_FONTDATA)
fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
#endif
if (key <= location_end) {
return location;
} else {
// location is above the current location
bottom = location + 1;
}
}
}
return -1;
}
CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset,
int32_t length) {
if (offset < 0 || offset + length > Size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundsException(
"Attempt to bind data outside of its limits");
#endif
return NULL;
}
FontDataPtr slice = new ReadableFontData(this, offset, length);
return slice.Detach();
}
CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset) {
if (offset < 0 || offset > Size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundsException(
"Attempt to bind data outside of its limits");
#endif
return NULL;
}
FontDataPtr slice = new ReadableFontData(this, offset);
return slice.Detach();
}
ReadableFontData::ReadableFontData(ReadableFontData* data, int32_t offset)
: FontData(data, offset),
checksum_set_(false),
checksum_(0) {
}
ReadableFontData::ReadableFontData(ReadableFontData* data,
int32_t offset,
int32_t length)
: FontData(data, offset, length),
checksum_set_(false),
checksum_(0) {
}
void ReadableFontData::ComputeChecksum() {
// TODO(arthurhsu): IMPLEMENT: synchronization/atomicity
int64_t sum = 0;
if (checksum_range_.empty()) {
sum = ComputeCheckSum(0, Length());
} else {
for (uint32_t low_bound_index = 0; low_bound_index < checksum_range_.size();
low_bound_index += 2) {
int32_t low_bound = checksum_range_[low_bound_index];
int32_t high_bound = (low_bound_index == checksum_range_.size() - 1) ?
Length() :
checksum_range_[low_bound_index + 1];
sum += ComputeCheckSum(low_bound, high_bound);
}
}
checksum_ = sum & 0xffffffffL;
checksum_set_ = true;
}
int64_t ReadableFontData::ComputeCheckSum(int32_t low_bound,
int32_t high_bound) {
int64_t sum = 0;
// Checksum all whole 4-byte chunks.
for (int32_t i = low_bound; i <= high_bound - 4; i += 4) {
sum += ReadULong(i);
}
// Add last fragment if not 4-byte multiple
int32_t off = high_bound & -4;
if (off < high_bound) {
int32_t b3 = ReadUByte(off);
int32_t b2 = (off + 1 < high_bound) ? ReadUByte(off + 1) : 0;
int32_t b1 = (off + 2 < high_bound) ? ReadUByte(off + 2) : 0;
int32_t b0 = 0;
sum += (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
}
return sum;
}
} // namespace sfntly

View File

@ -0,0 +1,308 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
#include "sfntly/data/font_data.h"
#include "sfntly/port/lock.h"
namespace sfntly {
class WritableFontData;
class OutputStream;
// Writable font data wrapper. Supports reading of data primitives in the
// TrueType / OpenType spec.
// The data types used are as listed:
// BYTE 8-bit unsigned integer.
// CHAR 8-bit signed integer.
// USHORT 16-bit unsigned integer.
// SHORT 16-bit signed integer.
// UINT24 24-bit unsigned integer.
// ULONG 32-bit unsigned integer.
// LONG 32-bit signed integer.
// Fixed 32-bit signed fixed-point number (16.16)
// FUNIT Smallest measurable distance in the em space.
// FWORD 16-bit signed integer (SHORT) that describes a quantity in FUnits.
// UFWORD 16-bit unsigned integer (USHORT) that describes a quantity in
// FUnits.
// F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14)
// LONGDATETIME Date represented in number of seconds since 12:00 midnight,
// January 1, 1904. The value is represented as a signed 64-bit
// integer.
class ReadableFontData : public FontData,
public RefCounted<ReadableFontData> {
public:
explicit ReadableFontData(ByteArray* array);
virtual ~ReadableFontData();
static CALLER_ATTACH ReadableFontData* CreateReadableFontData(ByteVector* b);
// Gets a computed checksum for the data. This checksum uses the OpenType spec
// calculation. Every ULong value (32 bit unsigned) in the data is summed and
// the resulting value is truncated to 32 bits. If the data length in bytes is
// not an integral multiple of 4 then any remaining bytes are treated as the
// start of a 4 byte sequence whose remaining bytes are zero.
// @return the checksum
int64_t Checksum();
// Sets the bounds to use for computing the checksum. These bounds are in
// begin and end pairs. If an odd number is given then the final range is
// assumed to extend to the end of the data. The lengths of each range must be
// a multiple of 4.
// @param ranges the range bounds to use for the checksum
void SetCheckSumRanges(const IntegerList& ranges);
// Read the UBYTE at the given index.
// @param index index into the font data
// @return the UBYTE; -1 if outside the bounds of the font data
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadUByte(int32_t index);
// Read the BYTE at the given index.
// @param index index into the font data
// @return the BYTE
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadByte(int32_t index);
// Read the bytes at the given index into the array.
// @param index index into the font data
// @param b the destination for the bytes read
// @param offset offset in the byte array to place the bytes
// @param length the length of bytes to read
// @return the number of bytes actually read; -1 if the index is outside the
// bounds of the font data
virtual int32_t ReadBytes(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
// Read the CHAR at the given index.
// @param index index into the font data
// @return the CHAR
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadChar(int32_t index);
// Read the USHORT at the given index.
// @param index index into the font data
// @return the USHORT
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadUShort(int32_t index);
// Read the SHORT at the given index.
// @param index index into the font data
// @return the SHORT
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadShort(int32_t index);
// Read the UINT24 at the given index.
// @param index index into the font data
// @return the UINT24
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadUInt24(int32_t index);
// Read the ULONG at the given index.
// @param index index into the font data
// @return the ULONG
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int64_t ReadULong(int32_t index);
// Read the ULONG at the given index as int32_t.
// @param index index into the font data
// @return the ULONG
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadULongAsInt(int32_t index);
// Read the ULONG at the given index, little-endian variant
// @param index index into the font data
// @return the ULONG
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int64_t ReadULongLE(int32_t index);
// Read the LONG at the given index.
// @param index index into the font data
// @return the LONG
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadLong(int32_t index);
// Read the Fixed at the given index.
// @param index index into the font data
// @return the Fixed
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadFixed(int32_t index);
// Read the LONGDATETIME at the given index.
// @param index index into the font data
// @return the LONGDATETIME
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int64_t ReadDateTimeAsLong(int32_t index);
// Read the FWORD at the given index.
// @param index index into the font data
// @return the FWORD
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadFWord(int32_t index);
// Read the UFWORD at the given index.
// @param index index into the font data
// @return the UFWORD
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t ReadFUFWord(int32_t index);
// Note: Not ported because they just throw UnsupportedOperationException()
// in Java.
/*
virtual int32_t ReadFUnit(int32_t index);
virtual int64_t ReadF2Dot14(int32_t index);
*/
// Copy the FontData to an OutputStream.
// @param os the destination
// @return number of bytes copied
// @throws IOException
virtual int32_t CopyTo(OutputStream* os);
// Copy the FontData to a WritableFontData.
// @param wfd the destination
// @return number of bytes copied
// @throws IOException
virtual int32_t CopyTo(WritableFontData* wfd);
// Make gcc -Woverloaded-virtual happy.
virtual int32_t CopyTo(ByteArray* ba);
// Search for the key value in the range tables provided.
// The search looks through the start-end pairs looking for the key value. It
// is assumed that the start-end pairs are both represented by UShort values,
// ranges do not overlap, and are monotonically increasing.
// @param startIndex the position to read the first start value from
// @param startOffset the offset between subsequent start values
// @param endIndex the position to read the first end value from
// @param endOffset the offset between subsequent end values
// @param length the number of start-end pairs
// @param key the value to search for
// @return the index of the start-end pairs in which the key was found; -1
// otherwise
int32_t SearchUShort(int32_t start_index,
int32_t start_offset,
int32_t end_index,
int32_t end_offset,
int32_t length,
int32_t key);
// Search for the key value in the table provided.
// The search looks through the values looking for the key value. It is
// assumed that the are represented by UShort values and are monotonically
// increasing.
// @param startIndex the position to read the first start value from
// @param startOffset the offset between subsequent start values
// @param length the number of start-end pairs
// @param key the value to search for
// @return the index of the start-end pairs in which the key was found; -1
// otherwise
int32_t SearchUShort(int32_t start_index,
int32_t start_offset,
int32_t length,
int32_t key);
// Search for the key value in the range tables provided.
// The search looks through the start-end pairs looking for the key value. It
// is assumed that the start-end pairs are both represented by ULong values
// that can be represented within 31 bits, ranges do not overlap, and are
// monotonically increasing.
// @param startIndex the position to read the first start value from
// @param startOffset the offset between subsequent start values
// @param endIndex the position to read the first end value from
// @param endOffset the offset between subsequent end values
// @param length the number of start-end pairs
// @param key the value to search for
// @return the index of the start-end pairs in which the key was found; -1
// otherwise
int32_t SearchULong(int32_t start_index,
int32_t start_offset,
int32_t end_index,
int32_t end_offset,
int32_t length,
int32_t key);
// TODO(arthurhsu): IMPLEMENT
/*
virtual int32_t ReadFUnit(int32_t index);
virtual int64_t ReadF2Dot14(int32_t index);
virtual int64_t ReadLongDateTime(int32_t index);
*/
// Makes a slice of this FontData. The returned slice will share the data with
// the original FontData.
// @param offset the start of the slice
// @param length the number of bytes in the slice
// @return a slice of the original FontData
// Note: C++ polymorphism requires return type to be consistent
virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length);
// Makes a bottom bound only slice of this array. The returned slice will
// share the data with the original FontData.
// @param offset the start of the slice
// @return a slice of the original FontData
// Note: C++ polymorphism requires return type to be consistent
virtual CALLER_ATTACH FontData* Slice(int32_t offset);
// Not Ported: toString()
protected:
// Constructor. Creates a bounded wrapper of another ReadableFontData from the
// given offset until the end of the original ReadableFontData.
// @param data data to wrap
// @param offset the start of this data's view of the original data
ReadableFontData(ReadableFontData* data, int32_t offset);
// Constructor. Creates a bounded wrapper of another ReadableFontData from the
// given offset until the end of the original ReadableFontData.
// @param data data to wrap
// @param offset the start of this data's view of the original data
// @param length the length of the other FontData to use
ReadableFontData(ReadableFontData* data, int32_t offset, int32_t length);
private:
// Compute the checksum for the font data using any ranges set for the
// calculation.
void ComputeChecksum();
// Do the actual computation of the checksum for a range using the
// TrueType/OpenType checksum algorithm. The range used is from the low bound
// to the high bound in steps of four bytes. If any of the bytes within that 4
// byte segment are not readable then it will considered a zero for
// calculation.
// Only called from within a synchronized method so it does not need to be
// synchronized itself.
// @param lowBound first position to start a 4 byte segment on
// @param highBound last possible position to start a 4 byte segment on
// @return the checksum for the total range
int64_t ComputeCheckSum(int32_t low_bound, int32_t high_bound);
Lock checksum_lock_;
bool checksum_set_;
int64_t checksum_;
IntegerList checksum_range_;
};
typedef Ptr<ReadableFontData> ReadableFontDataPtr;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_

View File

@ -0,0 +1,201 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/writable_font_data.h"
#include "sfntly/data/memory_byte_array.h"
#include "sfntly/data/growable_memory_byte_array.h"
namespace sfntly {
WritableFontData::WritableFontData(ByteArray* ba) : ReadableFontData(ba) {
}
WritableFontData::~WritableFontData() {}
// static
CALLER_ATTACH
WritableFontData* WritableFontData::CreateWritableFontData(int32_t length) {
ByteArrayPtr ba;
if (length > 0) {
ba = new MemoryByteArray(length);
ba->SetFilledLength(length);
} else {
ba = new GrowableMemoryByteArray();
}
WritableFontDataPtr wfd = new WritableFontData(ba);
return wfd.Detach();
}
// TODO(arthurhsu): re-investigate the memory model of this function. It's
// not too useful without copying, but it's not performance
// savvy to do copying.
CALLER_ATTACH
WritableFontData* WritableFontData::CreateWritableFontData(ByteVector* b) {
ByteArrayPtr ba = new GrowableMemoryByteArray();
ba->Put(0, b);
WritableFontDataPtr wfd = new WritableFontData(ba);
return wfd.Detach();
}
int32_t WritableFontData::WriteByte(int32_t index, byte_t b) {
array_->Put(BoundOffset(index), b);
return 1;
}
int32_t WritableFontData::WriteBytes(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
return array_->Put(BoundOffset(index),
b,
offset,
BoundLength(index, length));
}
int32_t WritableFontData::WriteBytes(int32_t index, ByteVector* b) {
assert(b);
return WriteBytes(index, &((*b)[0]), 0, b->size());
}
int32_t WritableFontData::WriteBytesPad(int32_t index,
ByteVector* b,
int32_t offset,
int32_t length,
byte_t pad) {
int32_t written =
array_->Put(BoundOffset(index),
&((*b)[0]),
offset,
BoundLength(index,
std::min<int32_t>(length, b->size() - offset)));
written += WritePadding(written + index, length - written, pad);
return written;
}
int32_t WritableFontData::WritePadding(int32_t index, int32_t count) {
return WritePadding(index, count, (byte_t)0);
}
int32_t WritableFontData::WritePadding(int32_t index, int32_t count,
byte_t pad) {
for (int32_t i = 0; i < count; ++i) {
array_->Put(index + i, pad);
}
return count;
}
int32_t WritableFontData::WriteChar(int32_t index, byte_t c) {
return WriteByte(index, c);
}
int32_t WritableFontData::WriteUShort(int32_t index, int32_t us) {
WriteByte(index, (byte_t)((us >> 8) & 0xff));
WriteByte(index + 1, (byte_t)(us & 0xff));
return 2;
}
int32_t WritableFontData::WriteUShortLE(int32_t index, int32_t us) {
WriteByte(index, (byte_t)(us & 0xff));
WriteByte(index + 1, (byte_t)((us >> 8) & 0xff));
return 2;
}
int32_t WritableFontData::WriteShort(int32_t index, int32_t s) {
return WriteUShort(index, s);
}
int32_t WritableFontData::WriteUInt24(int32_t index, int32_t ui) {
WriteByte(index, (byte_t)((ui >> 16) & 0xff));
WriteByte(index + 1, (byte_t)((ui >> 8) & 0xff));
WriteByte(index + 2, (byte_t)(ui & 0xff));
return 3;
}
int32_t WritableFontData::WriteULong(int32_t index, int64_t ul) {
WriteByte(index, (byte_t)((ul >> 24) & 0xff));
WriteByte(index + 1, (byte_t)((ul >> 16) & 0xff));
WriteByte(index + 2, (byte_t)((ul >> 8) & 0xff));
WriteByte(index + 3, (byte_t)(ul & 0xff));
return 4;
}
int32_t WritableFontData::WriteULongLE(int32_t index, int64_t ul) {
WriteByte(index, (byte_t)(ul & 0xff));
WriteByte(index + 1, (byte_t)((ul >> 8) & 0xff));
WriteByte(index + 2, (byte_t)((ul >> 16) & 0xff));
WriteByte(index + 3, (byte_t)((ul >> 24) & 0xff));
return 4;
}
int32_t WritableFontData::WriteLong(int32_t index, int64_t l) {
return WriteULong(index, l);
}
int32_t WritableFontData::WriteFixed(int32_t index, int32_t f) {
return WriteLong(index, f);
}
int32_t WritableFontData::WriteDateTime(int32_t index, int64_t date) {
WriteULong(index, (date >> 32) & 0xffffffff);
WriteULong(index + 4, date & 0xffffffff);
return 8;
}
void WritableFontData::CopyFrom(InputStream* is, int32_t length) {
array_->CopyFrom(is, length);
}
void WritableFontData::CopyFrom(InputStream* is) {
array_->CopyFrom(is);
}
CALLER_ATTACH FontData* WritableFontData::Slice(int32_t offset,
int32_t length) {
if (offset < 0 || offset + length > Size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundsException(
"Attempt to bind data outside of its limits");
#endif
return NULL;
}
FontDataPtr slice = new WritableFontData(this, offset, length);
return slice.Detach();
}
CALLER_ATTACH FontData* WritableFontData::Slice(int32_t offset) {
if (offset > Size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundsException(
"Attempt to bind data outside of its limits");
#endif
return NULL;
}
FontDataPtr slice = new WritableFontData(this, offset);
return slice.Detach();
}
WritableFontData::WritableFontData(WritableFontData* data, int32_t offset)
: ReadableFontData(data, offset) {
}
WritableFontData::WritableFontData(WritableFontData* data,
int32_t offset,
int32_t length)
: ReadableFontData(data, offset, length) {
}
} // namespace sfntly

View File

@ -0,0 +1,211 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
#define SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
#include "sfntly/data/readable_font_data.h"
namespace sfntly {
// Writable font data wrapper. Supports writing of data primitives in the
// TrueType / OpenType spec.
class WritableFontData : public ReadableFontData {
public:
explicit WritableFontData(ByteArray* ba);
virtual ~WritableFontData();
// Constructs a writable font data object. If the length is specified as
// positive then a fixed size font data object will be created. If the length
// is zero or less then a growable font data object will be created and the
// size will be used as an estimate to help in allocating the original space.
// @param length if length > 0 create a fixed length font data; otherwise
// create a growable font data
// @return a new writable font data
static CALLER_ATTACH WritableFontData* CreateWritableFontData(int32_t length);
// Constructs a writable font data object. The new font data object will wrap
// the bytes passed in to the factory and it will take make a copy of those
// bytes.
// @param b the byte vector to wrap
// @return a new writable font data
static CALLER_ATTACH WritableFontData* CreateWritableFontData(ByteVector* b);
// Write a byte at the given index.
// @param index index into the font data
// @param b the byte to write
// @return the number of bytes written
virtual int32_t WriteByte(int32_t index, byte_t b);
// Write the bytes from the array.
// @param index index into the font data
// @param b the source for the bytes to be written
// @param offset offset in the byte array
// @param length the length of the bytes to be written
// @return the number of bytes actually written; -1 if the index is outside
// the FontData's range
virtual int32_t WriteBytes(int32_t index,
byte_t* b,
int32_t offset,
int32_t length);
// Write the bytes from the array.
// @param index index into the font data
// @param b the source for the bytes to be written
// @return the number of bytes actually written; -1 if the index is outside
// the FontData's range
virtual int32_t WriteBytes(int32_t index, ByteVector* b);
// Write the bytes from the array and pad if necessary.
// Write to the length given using the byte array provided and if there are
// not enough bytes in the array then pad to the requested length using the
// pad byte specified.
// @param index index into the font data
// @param b the source for the bytes to be written
// @param offset offset in the byte array
// @param length the length of the bytes to be written
// @param pad the padding byte to be used if necessary
// @return the number of bytes actually written
virtual int32_t WriteBytesPad(int32_t index,
ByteVector* b,
int32_t offset,
int32_t length,
byte_t pad);
// Writes padding to the FontData. The padding byte written is 0x00.
// @param index index into the font data
// @param count the number of pad bytes to write
// @return the number of pad bytes written
virtual int32_t WritePadding(int32_t index, int32_t count);
// Writes padding to the FontData.
// @param index index into the font data
// @param count the number of pad bytes to write
// @param pad the byte value to use as padding
// @return the number of pad bytes written
virtual int32_t WritePadding(int32_t index, int32_t count, byte_t pad);
// Write the CHAR at the given index.
// @param index index into the font data
// @param c the CHAR
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteChar(int32_t index, byte_t c);
// Write the USHORT at the given index.
// @param index index into the font data
// @param us the USHORT
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteUShort(int32_t index, int32_t us);
// Write the USHORT at the given index in little endian format.
// @param index index into the font data
// @param us the USHORT
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteUShortLE(int32_t index, int32_t us);
// Write the SHORT at the given index.
// @param index index into the font data
// @param s the SHORT
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteShort(int32_t index, int32_t s);
// Write the UINT24 at the given index.
// @param index index into the font data
// @param ui the UINT24
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteUInt24(int32_t index, int32_t ui);
// Write the ULONG at the given index.
// @param index index into the font data
// @param ul the ULONG
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteULong(int32_t index, int64_t ul);
// Write the ULONG at the given index in little endian format.
// @param index index into the font data
// @param ul the ULONG
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteULongLE(int32_t index, int64_t ul);
// Write the LONG at the given index.
// @param index index into the font data
// @param l the LONG
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteLong(int32_t index, int64_t l);
// Write the Fixed at the given index.
// @param index index into the font data
// @param f the Fixed
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteFixed(int32_t index, int32_t f);
// Write the LONGDATETIME at the given index.
// @param index index into the font data
// @param date the LONGDATETIME
// @return the number of bytes actually written
// @throws IndexOutOfBoundsException if index is outside the FontData's range
virtual int32_t WriteDateTime(int32_t index, int64_t date);
// Copy from the InputStream into this FontData.
// @param is the source
// @param length the number of bytes to copy
// @throws IOException
virtual void CopyFrom(InputStream* is, int32_t length);
// Copy everything from the InputStream into this FontData.
// @param is the source
// @throws IOException
virtual void CopyFrom(InputStream* is);
// Makes a slice of this FontData. The returned slice will share the data with
// the original FontData.
// @param offset the start of the slice
// @param length the number of bytes in the slice
// @return a slice of the original FontData
virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length);
// Makes a bottom bound only slice of this array. The returned slice will
// share the data with the original FontData.
// @param offset the start of the slice
// @return a slice of the original FontData
virtual CALLER_ATTACH FontData* Slice(int32_t offset);
private:
// Constructor with a lower bound.
// @param data other WritableFontData object to share data with
// @param offset offset from the other WritableFontData's data
WritableFontData(WritableFontData* data, int32_t offset);
// Constructor with lower bound and a length bound.
// @param data other WritableFontData object to share data with
// @param offset offset from the other WritableFontData's data
// @param length length of other WritableFontData's data to use
WritableFontData(WritableFontData* data, int32_t offset, int32_t length);
};
typedef Ptr<WritableFontData> WritableFontDataPtr;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_

View File

@ -0,0 +1,568 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/font.h"
#include <stdio.h>
#include <functional>
#include <algorithm>
#include <map>
#include <string>
#include <typeinfo>
#include <iterator>
#include "sfntly/data/font_input_stream.h"
#include "sfntly/font_factory.h"
#include "sfntly/math/fixed1616.h"
#include "sfntly/math/font_math.h"
#include "sfntly/port/exception_type.h"
#include "sfntly/table/core/font_header_table.h"
#include "sfntly/table/core/horizontal_device_metrics_table.h"
#include "sfntly/table/core/horizontal_header_table.h"
#include "sfntly/table/core/horizontal_metrics_table.h"
#include "sfntly/table/core/maximum_profile_table.h"
#include "sfntly/table/truetype/loca_table.h"
#include "sfntly/tag.h"
namespace sfntly {
const int32_t SFNTVERSION_MAJOR = 1;
const int32_t SFNTVERSION_MINOR = 0;
/******************************************************************************
* Font class
******************************************************************************/
Font::~Font() {}
bool Font::HasTable(int32_t tag) {
TableMap::const_iterator result = tables_.find(tag);
TableMap::const_iterator end = tables_.end();
return (result != end);
}
// Changed by Kovid: these four methods cannot be inlined, if they are they
// return incorrect values when compiled with -fPIC
int32_t Font::sfnt_version() { return sfnt_version_; }
ByteVector* Font::digest() { return &digest_; }
int64_t Font::checksum() { return checksum_; }
int32_t Font::num_tables() { return (int32_t)tables_.size(); }
Table* Font::GetTable(int32_t tag) {
if (!HasTable(tag)) {
return NULL;
}
return tables_[tag];
}
const TableMap* Font::GetTableMap() {
return &tables_;
}
void Font::Serialize(OutputStream* os, IntegerList* table_ordering) {
assert(table_ordering);
IntegerList final_table_ordering;
GenerateTableOrdering(table_ordering, &final_table_ordering);
TableHeaderList table_records;
BuildTableHeadersForSerialization(&final_table_ordering, &table_records);
FontOutputStream fos(os);
SerializeHeader(&fos, &table_records);
SerializeTables(&fos, &table_records);
}
Font::Font(int32_t sfnt_version, ByteVector* digest)
: sfnt_version_(sfnt_version) {
// non-trivial assignments that makes debugging hard if placed in
// initialization list
digest_ = *digest;
}
void Font::BuildTableHeadersForSerialization(IntegerList* table_ordering,
TableHeaderList* table_headers) {
assert(table_headers);
assert(table_ordering);
IntegerList final_table_ordering;
GenerateTableOrdering(table_ordering, &final_table_ordering);
int32_t table_offset = Offset::kTableRecordBegin + num_tables() *
Offset::kTableRecordSize;
for (IntegerList::iterator tag = final_table_ordering.begin(),
tag_end = final_table_ordering.end();
tag != tag_end; ++tag) {
if (tables_.find(*tag) == tables_.end()) {
continue;
}
TablePtr table = tables_[*tag];
if (table != NULL) {
HeaderPtr header =
new Header(*tag, table->CalculatedChecksum(), table_offset,
table->header()->length());
table_headers->push_back(header);
table_offset += (table->DataLength() + 3) & ~3;
}
}
}
void Font::SerializeHeader(FontOutputStream* fos,
TableHeaderList* table_headers) {
fos->WriteFixed(sfnt_version_);
fos->WriteUShort(table_headers->size());
int32_t log2_of_max_power_of_2 = FontMath::Log2(table_headers->size());
int32_t search_range = 2 << (log2_of_max_power_of_2 - 1 + 4);
fos->WriteUShort(search_range);
fos->WriteUShort(log2_of_max_power_of_2);
fos->WriteUShort((table_headers->size() * 16) - search_range);
HeaderTagSortedSet sorted_headers;
std::copy(table_headers->begin(),
table_headers->end(),
std::inserter(sorted_headers, sorted_headers.end()));
for (HeaderTagSortedSet::iterator record = sorted_headers.begin(),
record_end = sorted_headers.end();
record != record_end; ++record) {
fos->WriteULong((*record)->tag());
fos->WriteULong((int32_t)((*record)->checksum()));
fos->WriteULong((*record)->offset());
fos->WriteULong((*record)->length());
}
}
void Font::SerializeTables(FontOutputStream* fos,
TableHeaderList* table_headers) {
assert(fos);
assert(table_headers);
for (TableHeaderList::iterator record = table_headers->begin(),
end_of_headers = table_headers->end();
record != end_of_headers; ++record) {
TablePtr target_table = GetTable((*record)->tag());
if (target_table == NULL) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("Table out of sync with font header.");
#endif
return;
}
int32_t table_size = target_table->Serialize(fos);
if (table_size != (*record)->length()) {
assert(false);
}
int32_t filler_size = ((table_size + 3) & ~3) - table_size;
for (int32_t i = 0; i < filler_size; ++i) {
fos->Write(static_cast<byte_t>(0));
}
}
}
void Font::GenerateTableOrdering(IntegerList* default_table_ordering,
IntegerList* table_ordering) {
assert(default_table_ordering);
assert(table_ordering);
table_ordering->clear();
if (default_table_ordering->empty()) {
DefaultTableOrdering(default_table_ordering);
}
typedef std::map<int32_t, bool> Int2Bool;
typedef std::pair<int32_t, bool> Int2BoolEntry;
Int2Bool tables_in_font;
for (TableMap::iterator table = tables_.begin(), table_end = tables_.end();
table != table_end; ++table) {
tables_in_font.insert(Int2BoolEntry(table->first, false));
}
for (IntegerList::iterator tag = default_table_ordering->begin(),
tag_end = default_table_ordering->end();
tag != tag_end; ++tag) {
if (HasTable(*tag)) {
table_ordering->push_back(*tag);
tables_in_font[*tag] = true;
}
}
for (Int2Bool::iterator table = tables_in_font.begin(),
table_end = tables_in_font.end();
table != table_end; ++table) {
if (table->second == false)
table_ordering->push_back(table->first);
}
}
void Font::DefaultTableOrdering(IntegerList* default_table_ordering) {
assert(default_table_ordering);
default_table_ordering->clear();
if (HasTable(Tag::CFF)) {
default_table_ordering->resize(CFF_TABLE_ORDERING_SIZE);
std::copy(CFF_TABLE_ORDERING, CFF_TABLE_ORDERING + CFF_TABLE_ORDERING_SIZE,
default_table_ordering->begin());
return;
}
default_table_ordering->resize(TRUE_TYPE_TABLE_ORDERING_SIZE);
std::copy(TRUE_TYPE_TABLE_ORDERING,
TRUE_TYPE_TABLE_ORDERING + TRUE_TYPE_TABLE_ORDERING_SIZE,
default_table_ordering->begin());
}
/******************************************************************************
* Font::Builder class
******************************************************************************/
Font::Builder::~Builder() {}
CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(FontFactory* factory,
InputStream* is) {
FontBuilderPtr builder = new Builder(factory);
builder->LoadFont(is);
return builder.Detach();
}
CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
FontFactory* factory,
WritableFontData* wfd,
int32_t offset_to_offset_table) {
FontBuilderPtr builder = new Builder(factory);
builder->LoadFont(wfd, offset_to_offset_table);
return builder.Detach();
}
CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
FontFactory* factory) {
FontBuilderPtr builder = new Builder(factory);
return builder.Detach();
}
bool Font::Builder::ReadyToBuild() {
// just read in data with no manipulation
if (table_builders_.empty() && !data_blocks_.empty()) {
return true;
}
// TODO(stuartg): font level checks - required tables etc?
for (TableBuilderMap::iterator table_builder = table_builders_.begin(),
table_builder_end = table_builders_.end();
table_builder != table_builder_end;
++table_builder) {
if (!table_builder->second->ReadyToBuild())
return false;
}
return true;
}
CALLER_ATTACH Font* Font::Builder::Build() {
FontPtr font = new Font(sfnt_version_, &digest_);
if (!table_builders_.empty()) {
// Note: Different from Java. Directly use font->tables_ here to avoid
// STL container copying.
BuildTablesFromBuilders(font, &table_builders_, &font->tables_);
}
table_builders_.clear();
data_blocks_.clear();
return font.Detach();
}
void Font::Builder::SetDigest(ByteVector* digest) {
digest_.clear();
digest_ = *digest;
}
void Font::Builder::ClearTableBuilders() {
table_builders_.clear();
}
bool Font::Builder::HasTableBuilder(int32_t tag) {
return (table_builders_.find(tag) != table_builders_.end());
}
Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) {
if (HasTableBuilder(tag))
return table_builders_[tag];
return NULL;
}
Table::Builder* Font::Builder::NewTableBuilder(int32_t tag) {
HeaderPtr header = new Header(tag);
TableBuilderPtr builder;
builder.Attach(Table::Builder::GetBuilder(header, NULL));
table_builders_.insert(TableBuilderEntry(header->tag(), builder));
return builder;
}
Table::Builder* Font::Builder::NewTableBuilder(int32_t tag,
ReadableFontData* src_data) {
assert(src_data);
WritableFontDataPtr data;
data.Attach(WritableFontData::CreateWritableFontData(src_data->Length()));
// TODO(stuarg): take over original data instead?
src_data->CopyTo(data);
HeaderPtr header = new Header(tag, data->Length());
TableBuilderPtr builder;
builder.Attach(Table::Builder::GetBuilder(header, data));
table_builders_.insert(TableBuilderEntry(tag, builder));
return builder;
}
void Font::Builder::RemoveTableBuilder(int32_t tag) {
TableBuilderMap::iterator target = table_builders_.find(tag);
if (target != table_builders_.end()) {
table_builders_.erase(target);
}
}
Font::Builder::Builder(FontFactory* factory)
: factory_(factory),
sfnt_version_(Fixed1616::Fixed(SFNTVERSION_MAJOR, SFNTVERSION_MINOR)) {
}
void Font::Builder::LoadFont(InputStream* is) {
// Note: we do not throw exception here for is. This is more of an assertion.
assert(is);
FontInputStream font_is(is);
HeaderOffsetSortedSet records;
ReadHeader(&font_is, &records);
LoadTableData(&records, &font_is, &data_blocks_);
BuildAllTableBuilders(&data_blocks_, &table_builders_);
font_is.Close();
}
void Font::Builder::LoadFont(WritableFontData* wfd,
int32_t offset_to_offset_table) {
// Note: we do not throw exception here for is. This is more of an assertion.
assert(wfd);
HeaderOffsetSortedSet records;
ReadHeader(wfd, offset_to_offset_table, &records);
LoadTableData(&records, wfd, &data_blocks_);
BuildAllTableBuilders(&data_blocks_, &table_builders_);
}
int32_t Font::Builder::SfntWrapperSize() {
return Offset::kSfntHeaderSize +
(Offset::kTableRecordSize * table_builders_.size());
}
void Font::Builder::BuildAllTableBuilders(DataBlockMap* table_data,
TableBuilderMap* builder_map) {
for (DataBlockMap::iterator record = table_data->begin(),
record_end = table_data->end();
record != record_end; ++record) {
TableBuilderPtr builder;
builder.Attach(GetTableBuilder(record->first.p_, record->second.p_));
builder_map->insert(TableBuilderEntry(record->first->tag(), builder));
}
InterRelateBuilders(&table_builders_);
}
CALLER_ATTACH
Table::Builder* Font::Builder::GetTableBuilder(Header* header,
WritableFontData* data) {
return Table::Builder::GetBuilder(header, data);
}
void Font::Builder::BuildTablesFromBuilders(Font* font,
TableBuilderMap* builder_map,
TableMap* table_map) {
UNREFERENCED_PARAMETER(font);
InterRelateBuilders(builder_map);
// Now build all the tables.
for (TableBuilderMap::iterator builder = builder_map->begin(),
builder_end = builder_map->end();
builder != builder_end; ++builder) {
TablePtr table;
if (builder->second && builder->second->ReadyToBuild()) {
table.Attach(down_cast<Table*>(builder->second->Build()));
}
if (table == NULL) {
table_map->clear();
#if !defined (SFNTLY_NO_EXCEPTION)
std::string builder_string = "Unable to build table - ";
char* table_name = TagToString(builder->first);
builder_string += table_name;
delete[] table_name;
throw RuntimeException(builder_string.c_str());
#endif
return;
}
table_map->insert(TableMapEntry(table->header()->tag(), table));
}
}
static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) {
if (builder_map) {
TableBuilderMap::iterator target = builder_map->find(tag);
if (target != builder_map->end()) {
return target->second.p_;
}
}
return NULL;
}
void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) {
Table::Builder* raw_head_builder = GetBuilder(builder_map, Tag::head);
FontHeaderTableBuilderPtr header_table_builder;
if (raw_head_builder != NULL) {
header_table_builder =
down_cast<FontHeaderTable::Builder*>(raw_head_builder);
}
Table::Builder* raw_hhea_builder = GetBuilder(builder_map, Tag::hhea);
HorizontalHeaderTableBuilderPtr horizontal_header_builder;
if (raw_head_builder != NULL) {
horizontal_header_builder =
down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder);
}
Table::Builder* raw_maxp_builder = GetBuilder(builder_map, Tag::maxp);
MaximumProfileTableBuilderPtr max_profile_builder;
if (raw_maxp_builder != NULL) {
max_profile_builder =
down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder);
}
Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca);
LocaTableBuilderPtr loca_table_builder;
if (raw_loca_builder != NULL) {
loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder);
}
Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx);
HorizontalMetricsTableBuilderPtr horizontal_metrics_builder;
if (raw_hmtx_builder != NULL) {
horizontal_metrics_builder =
down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder);
}
#if defined (SFNTLY_EXPERIMENTAL)
Table::Builder* raw_hdmx_builder = GetBuilder(builder_map, Tag::hdmx);
HorizontalDeviceMetricsTableBuilderPtr hdmx_table_builder;
if (raw_hdmx_builder != NULL) {
hdmx_table_builder =
down_cast<HorizontalDeviceMetricsTable::Builder*>(raw_hdmx_builder);
}
#endif
// set the inter table data required to build certain tables
if (horizontal_metrics_builder != NULL) {
if (max_profile_builder != NULL) {
horizontal_metrics_builder->SetNumGlyphs(
max_profile_builder->NumGlyphs());
}
if (horizontal_header_builder != NULL) {
horizontal_metrics_builder->SetNumberOfHMetrics(
horizontal_header_builder->NumberOfHMetrics());
}
}
if (loca_table_builder != NULL) {
if (max_profile_builder != NULL) {
loca_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
}
if (header_table_builder != NULL) {
loca_table_builder->set_format_version(
header_table_builder->IndexToLocFormat());
}
}
#if defined (SFNTLY_EXPERIMENTAL)
// Note: In C++, hdmx_table_builder can be NULL in a subsetter.
if (max_profile_builder != NULL && hdmx_table_builder != NULL) {
hdmx_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
}
#endif
}
void Font::Builder::ReadHeader(FontInputStream* is,
HeaderOffsetSortedSet* records) {
assert(records);
sfnt_version_ = is->ReadFixed();
num_tables_ = is->ReadUShort();
search_range_ = is->ReadUShort();
entry_selector_ = is->ReadUShort();
range_shift_ = is->ReadUShort();
for (int32_t table_number = 0; table_number < num_tables_; ++table_number) {
// Need to use temporary vars here. C++ evaluates function parameters from
// right to left and thus breaks the order of input stream.
int32_t tag = is->ReadULongAsInt();
int64_t checksum = is->ReadULong();
int32_t offset = is->ReadULongAsInt();
int32_t length = is->ReadULongAsInt();
HeaderPtr table = new Header(tag, checksum, offset, length);
records->insert(table);
}
}
void Font::Builder::ReadHeader(ReadableFontData* fd,
int32_t offset,
HeaderOffsetSortedSet* records) {
assert(records);
sfnt_version_ = fd->ReadFixed(offset + Offset::kSfntVersion);
num_tables_ = fd->ReadUShort(offset + Offset::kNumTables);
search_range_ = fd->ReadUShort(offset + Offset::kSearchRange);
entry_selector_ = fd->ReadUShort(offset + Offset::kEntrySelector);
range_shift_ = fd->ReadUShort(offset + Offset::kRangeShift);
int32_t table_offset = offset + Offset::kTableRecordBegin;
for (int32_t table_number = 0;
table_number < num_tables_;
table_number++, table_offset += Offset::kTableRecordSize) {
int32_t tag = fd->ReadULongAsInt(table_offset + Offset::kTableTag);
int64_t checksum = fd->ReadULong(table_offset + Offset::kTableCheckSum);
int32_t offset = fd->ReadULongAsInt(table_offset + Offset::kTableOffset);
int32_t length = fd->ReadULongAsInt(table_offset + Offset::kTableLength);
HeaderPtr table = new Header(tag, checksum, offset, length);
records->insert(table);
}
}
void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
FontInputStream* is,
DataBlockMap* table_data) {
assert(table_data);
for (HeaderOffsetSortedSet::iterator table_header = headers->begin(),
table_end = headers->end();
table_header != table_end;
++table_header) {
is->Skip((*table_header)->offset() - is->position());
FontInputStream table_is(is, (*table_header)->length());
WritableFontDataPtr data;
data.Attach(
WritableFontData::CreateWritableFontData((*table_header)->length()));
data->CopyFrom(&table_is, (*table_header)->length());
table_data->insert(DataBlockEntry(*table_header, data));
}
}
void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
WritableFontData* fd,
DataBlockMap* table_data) {
for (HeaderOffsetSortedSet::iterator table_header = headers->begin(),
table_end = headers->end();
table_header != table_end;
++table_header) {
FontDataPtr sliced_data;
sliced_data.Attach(
fd->Slice((*table_header)->offset(), (*table_header)->length()));
WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_);
table_data->insert(DataBlockEntry(*table_header, data));
}
}
} // namespace sfntly

View File

@ -0,0 +1,352 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_FONT_H_
#define SFNTLY_CPP_SRC_SFNTLY_FONT_H_
#include <vector>
#include "sfntly/port/refcount.h"
#include "sfntly/port/type.h"
#include "sfntly/port/endian.h"
#include "sfntly/data/font_input_stream.h"
#include "sfntly/data/font_output_stream.h"
#include "sfntly/data/writable_font_data.h"
#include "sfntly/table/table.h"
namespace sfntly {
// Note: following constants are embedded in Font class in Java. They are
// extracted out for easier reference from other classes. Offset is the
// one that is kept within class.
// Platform ids. These are used in a number of places within the font whenever
// the platform needs to be specified.
struct PlatformId {
enum {
kUnknown = -1,
kUnicode = 0,
kMacintosh = 1,
kISO = 2,
kWindows = 3,
kCustom = 4
};
};
// Unicode encoding ids. These are used in a number of places within the font
// whenever character encodings need to be specified.
struct UnicodeEncodingId {
enum {
kUnknown = -1,
kUnicode1_0 = 0,
kUnicode1_1 = 1,
kISO10646 = 2,
kUnicode2_0_BMP = 3,
kUnicode2_0 = 4,
kUnicodeVariationSequences = 5
};
};
// Windows encoding ids. These are used in a number of places within the font
// whenever character encodings need to be specified.
struct WindowsEncodingId {
enum {
kUnknown = 0xffffffff,
kSymbol = 0,
kUnicodeUCS2 = 1,
kShiftJIS = 2,
kPRC = 3,
kBig5 = 4,
kWansung = 5,
kJohab = 6,
kUnicodeUCS4 = 10
};
};
// Macintosh encoding ids. These are used in a number of places within the
// font whenever character encodings need to be specified.
struct MacintoshEncodingId {
// Macintosh Platform Encodings
enum {
kUnknown = -1,
kRoman = 0,
kJapanese = 1,
kChineseTraditional = 2,
kKorean = 3,
kArabic = 4,
kHebrew = 5,
kGreek = 6,
kRussian = 7,
kRSymbol = 8,
kDevanagari = 9,
kGurmukhi = 10,
kGujarati = 11,
kOriya = 12,
kBengali = 13,
kTamil = 14,
kTelugu = 15,
kKannada = 16,
kMalayalam = 17,
kSinhalese = 18,
kBurmese = 19,
kKhmer = 20,
kThai = 21,
kLaotian = 22,
kGeorgian = 23,
kArmenian = 24,
kChineseSimplified = 25,
kTibetan = 26,
kMongolian = 27,
kGeez = 28,
kSlavic = 29,
kVietnamese = 30,
kSindhi = 31,
kUninterpreted = 32
};
};
class FontFactory;
// An sfnt container font object. This object is immutable and thread safe. To
// construct one use an instance of Font::Builder.
class Font : public RefCounted<Font> {
public:
// A builder for a font object. The builder allows the for the creation of
// immutable Font objects. The builder is a one use non-thread safe object and
// once the Font object has been created it is no longer usable. To create a
// further Font object new builder will be required.
class Builder : public RefCounted<Builder> {
public:
virtual ~Builder();
static CALLER_ATTACH Builder*
GetOTFBuilder(FontFactory* factory, InputStream* is);
static CALLER_ATTACH Builder*
GetOTFBuilder(FontFactory* factory,
WritableFontData* ba,
int32_t offset_to_offset_table);
static CALLER_ATTACH Builder* GetOTFBuilder(FontFactory* factory);
// Get the font factory that created this font builder.
FontFactory* GetFontFactory() { return factory_; }
// Is the font ready to build?
bool ReadyToBuild();
// Build the Font. After this call this builder will no longer be usable.
CALLER_ATTACH Font* Build();
// Set a unique fingerprint for the font object.
void SetDigest(ByteVector* digest);
// Clear all table builders.
void ClearTableBuilders();
// Does this font builder have the specified table builder.
bool HasTableBuilder(int32_t tag);
// Get the table builder for the given tag. If there is no builder for that
// tag then return a null.
Table::Builder* GetTableBuilder(int32_t tag);
// Creates a new table builder for the table type given by the table id tag.
// This new table has been added to the font and will replace any existing
// builder for that table.
// @return new empty table of the type specified by tag; if tag is not known
// then a generic OpenTypeTable is returned
virtual Table::Builder* NewTableBuilder(int32_t tag);
// Creates a new table builder for the table type given by the table id tag.
// It makes a copy of the data provided and uses that copy for the table.
// This new table has been added to the font and will replace any existing
// builder for that table.
virtual Table::Builder* NewTableBuilder(int32_t tag,
ReadableFontData* src_data);
// Get a map of the table builders in this font builder accessed by table
// tag.
virtual TableBuilderMap* table_builders() { return &table_builders_; }
// Remove the specified table builder from the font builder.
// Note: different from Java: we don't return object in removeTableBuilder
virtual void RemoveTableBuilder(int32_t tag);
// Get the number of table builders in the font builder.
virtual int32_t number_of_table_builders() {
return (int32_t)table_builders_.size();
}
private:
explicit Builder(FontFactory* factory);
virtual void LoadFont(InputStream* is);
virtual void LoadFont(WritableFontData* wfd,
int32_t offset_to_offset_table);
int32_t SfntWrapperSize();
void BuildAllTableBuilders(DataBlockMap* table_data,
TableBuilderMap* builder_map);
CALLER_ATTACH Table::Builder*
GetTableBuilder(Header* header, WritableFontData* data);
void BuildTablesFromBuilders(Font* font,
TableBuilderMap* builder_map,
TableMap* tables);
static void InterRelateBuilders(TableBuilderMap* builder_map);
void ReadHeader(FontInputStream* is,
HeaderOffsetSortedSet* records);
void ReadHeader(ReadableFontData* fd,
int32_t offset,
HeaderOffsetSortedSet* records);
void LoadTableData(HeaderOffsetSortedSet* headers,
FontInputStream* is,
DataBlockMap* table_data);
void LoadTableData(HeaderOffsetSortedSet* headers,
WritableFontData* fd,
DataBlockMap* table_data);
TableBuilderMap table_builders_;
FontFactory* factory_; // dumb pointer, avoid circular refcounting
int32_t sfnt_version_;
int32_t num_tables_;
int32_t search_range_;
int32_t entry_selector_;
int32_t range_shift_;
DataBlockMap data_blocks_;
ByteVector digest_;
};
virtual ~Font();
// Gets the sfnt version set in the sfnt wrapper of the font.
int32_t sfnt_version();
// Gets a copy of the fonts digest that was created when the font was read. If
// no digest was set at creation time then the return result will be null.
ByteVector* digest();
// Get the checksum for this font.
int64_t checksum();
// Get the number of tables in this font.
int32_t num_tables();
// Whether the font has a particular table.
bool HasTable(int32_t tag);
// UNIMPLEMENTED: public Iterator<? extends Table> iterator
// Get the table in this font with the specified id.
// @param tag the identifier of the table
// @return the table specified if it exists; null otherwise
// C++ port: rename table() to GetTable()
Table* GetTable(int32_t tag);
// Get a map of the tables in this font accessed by table tag.
// @return an unmodifiable view of the tables in this font
// Note: renamed tableMap() to GetTableMap()
const TableMap* GetTableMap();
// UNIMPLEMENTED: toString()
// Serialize the font to the output stream.
// @param os the destination for the font serialization
// @param tableOrdering the table ordering to apply
void Serialize(OutputStream* os, IntegerList* table_ordering);
private:
// Offsets to specific elements in the underlying data. These offsets are
// relative to the start of the table or the start of sub-blocks within the
// table.
struct Offset {
enum {
// Offsets within the main directory
kSfntVersion = 0,
kNumTables = 4,
kSearchRange = 6,
kEntrySelector = 8,
kRangeShift = 10,
kTableRecordBegin = 12,
kSfntHeaderSize = 12,
// Offsets within a specific table record
kTableTag = 0,
kTableCheckSum = 4,
kTableOffset = 8,
kTableLength = 12,
kTableRecordSize = 16
};
};
// Note: the two constants are moved to tag.h to avoid VC++ bug.
// static const int32_t CFF_TABLE_ORDERING[];
// static const int32_t TRUE_TYPE_TABLE_ORDERING[];
// Constructor.
// @param sfntVersion the sfnt version
// @param digest the computed digest for the font; null if digest was not
// computed
// Note: Current C++ port does not support SHA digest validation.
Font(int32_t sfnt_version, ByteVector* digest);
// Build the table headers to be used for serialization. These headers will be
// filled out with the data required for serialization. The headers will be
// sorted in the order specified and only those specified will have headers
// generated.
// @param tableOrdering the tables to generate headers for and the order to
// sort them
// @return a list of table headers ready for serialization
void BuildTableHeadersForSerialization(IntegerList* table_ordering,
TableHeaderList* table_headers);
// Searialize the headers.
// @param fos the destination stream for the headers
// @param tableHeaders the headers to serialize
// @throws IOException
void SerializeHeader(FontOutputStream* fos, TableHeaderList* table_headers);
// Serialize the tables.
// @param fos the destination stream for the headers
// @param tableHeaders the headers for the tables to serialize
// @throws IOException
void SerializeTables(FontOutputStream* fos, TableHeaderList* table_headers);
// Generate the full table ordering to used for serialization. The full
// ordering uses the partial ordering as a seed and then adds all remaining
// tables in the font in an undefined order.
// @param defaultTableOrdering the partial ordering to be used as a seed for
// the full ordering
// @param (out) table_ordering the full ordering for serialization
void GenerateTableOrdering(IntegerList* default_table_ordering,
IntegerList* table_ordering);
// Get the default table ordering based on the type of the font.
// @param (out) default_table_ordering the default table ordering
void DefaultTableOrdering(IntegerList* default_table_ordering);
int32_t sfnt_version_;
ByteVector digest_;
int64_t checksum_;
TableMap tables_;
};
typedef Ptr<Font> FontPtr;
typedef std::vector<FontPtr> FontArray;
typedef Ptr<Font::Builder> FontBuilderPtr;
typedef std::vector<FontBuilderPtr> FontBuilderArray;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_FONT_H_

View File

@ -0,0 +1,214 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/font_factory.h"
#include <string.h>
#include "sfntly/tag.h"
namespace sfntly {
FontFactory::~FontFactory() {
}
CALLER_ATTACH FontFactory* FontFactory::GetInstance() {
FontFactoryPtr instance = new FontFactory();
return instance.Detach();
}
void FontFactory::FingerprintFont(bool fingerprint) {
fingerprint_ = fingerprint;
}
bool FontFactory::FingerprintFont() {
return fingerprint_;
}
void FontFactory::LoadFonts(InputStream* is, FontArray* output) {
assert(output);
PushbackInputStream* pbis = down_cast<PushbackInputStream*>(is);
if (IsCollection(pbis)) {
LoadCollection(pbis, output);
return;
}
FontPtr font;
font.Attach(LoadSingleOTF(pbis));
if (font) {
output->push_back(font);
}
}
void FontFactory::LoadFonts(ByteVector* b, FontArray* output) {
WritableFontDataPtr wfd;
wfd.Attach(WritableFontData::CreateWritableFontData(b));
if (IsCollection(wfd)) {
LoadCollection(wfd, output);
return;
}
FontPtr font;
font.Attach(LoadSingleOTF(wfd));
if (font) {
output->push_back(font);
}
}
void FontFactory::LoadFontsForBuilding(InputStream* is,
FontBuilderArray* output) {
PushbackInputStream* pbis = down_cast<PushbackInputStream*>(is);
if (IsCollection(pbis)) {
LoadCollectionForBuilding(pbis, output);
return;
}
FontBuilderPtr builder;
builder.Attach(LoadSingleOTFForBuilding(pbis));
if (builder) {
output->push_back(builder);
}
}
void FontFactory::LoadFontsForBuilding(ByteVector* b,
FontBuilderArray* output) {
WritableFontDataPtr wfd;
wfd.Attach(WritableFontData::CreateWritableFontData(b));
if (IsCollection(wfd)) {
LoadCollectionForBuilding(wfd, output);
return;
}
FontBuilderPtr builder;
builder.Attach(LoadSingleOTFForBuilding(wfd, 0));
if (builder) {
output->push_back(builder);
}
}
void FontFactory::SerializeFont(Font* font, OutputStream* os) {
font->Serialize(os, &table_ordering_);
}
void FontFactory::SetSerializationTableOrdering(
const IntegerList& table_ordering) {
table_ordering_ = table_ordering;
}
CALLER_ATTACH Font::Builder* FontFactory::NewFontBuilder() {
return Font::Builder::GetOTFBuilder(this);
}
CALLER_ATTACH Font* FontFactory::LoadSingleOTF(InputStream* is) {
FontBuilderPtr builder;
builder.Attach(LoadSingleOTFForBuilding(is));
return builder->Build();
}
CALLER_ATTACH Font* FontFactory::LoadSingleOTF(WritableFontData* wfd) {
FontBuilderPtr builder;
builder.Attach(LoadSingleOTFForBuilding(wfd, 0));
return builder->Build();
}
void FontFactory::LoadCollection(InputStream* is, FontArray* output) {
FontBuilderArray ba;
LoadCollectionForBuilding(is, &ba);
output->reserve(ba.size());
for (FontBuilderArray::iterator builder = ba.begin(), builders_end = ba.end();
builder != builders_end; ++builder) {
FontPtr font;
font.Attach((*builder)->Build());
output->push_back(font);
}
}
void FontFactory::LoadCollection(WritableFontData* wfd, FontArray* output) {
FontBuilderArray builders;
LoadCollectionForBuilding(wfd, &builders);
output->reserve(builders.size());
for (FontBuilderArray::iterator builder = builders.begin(),
builders_end = builders.end();
builder != builders_end; ++builder) {
FontPtr font;
font.Attach((*builder)->Build());
output->push_back(font);
}
}
CALLER_ATTACH
Font::Builder* FontFactory::LoadSingleOTFForBuilding(InputStream* is) {
// UNIMPLEMENTED: SHA-1 hash checking via Java DigestStream
Font::Builder* builder = Font::Builder::GetOTFBuilder(this, is);
// UNIMPLEMENTED: setDigest
return builder;
}
CALLER_ATTACH Font::Builder*
FontFactory::LoadSingleOTFForBuilding(WritableFontData* wfd,
int32_t offset_to_offset_table) {
// UNIMPLEMENTED: SHA-1 hash checking via Java DigestStream
Font::Builder* builder =
Font::Builder::GetOTFBuilder(this, wfd, offset_to_offset_table);
// UNIMPLEMENTED: setDigest
return builder;
}
void FontFactory::LoadCollectionForBuilding(InputStream* is,
FontBuilderArray* builders) {
assert(is);
assert(builders);
WritableFontDataPtr wfd;
wfd.Attach(WritableFontData::CreateWritableFontData(is->Available()));
wfd->CopyFrom(is);
LoadCollectionForBuilding(wfd, builders);
}
void FontFactory::LoadCollectionForBuilding(WritableFontData* wfd,
FontBuilderArray* builders) {
int32_t ttc_tag = wfd->ReadULongAsInt(Offset::kTTCTag);
UNREFERENCED_PARAMETER(ttc_tag);
int32_t version = wfd->ReadFixed(Offset::kVersion);
UNREFERENCED_PARAMETER(version);
int32_t num_fonts = wfd->ReadULongAsInt(Offset::kNumFonts);
builders->reserve(num_fonts);
int32_t offset_table_offset = Offset::kOffsetTable;
for (int32_t font_number = 0;
font_number < num_fonts;
font_number++, offset_table_offset += DataSize::kULONG) {
int32_t offset = wfd->ReadULongAsInt(offset_table_offset);
FontBuilderPtr builder;
builder.Attach(LoadSingleOTFForBuilding(wfd, offset));
builders->push_back(builder);
}
}
bool FontFactory::IsCollection(PushbackInputStream* pbis) {
ByteVector tag(4);
pbis->Read(&tag);
pbis->Unread(&tag);
return Tag::ttcf == GenerateTag(tag[0], tag[1], tag[2], tag[3]);
}
bool FontFactory::IsCollection(ReadableFontData* rfd) {
ByteVector tag(4);
rfd->ReadBytes(0, &(tag[0]), 0, tag.size());
return Tag::ttcf ==
GenerateTag(tag[0], tag[1], tag[2], tag[3]);
}
FontFactory::FontFactory()
: fingerprint_(false) {
}
} // namespace sfntly

View File

@ -0,0 +1,140 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
#define SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
#include <vector>
#include "sfntly/port/refcount.h"
#include "sfntly/port/type.h"
#include "sfntly/font.h"
namespace sfntly {
class FontFactory : public RefCounted<FontFactory> {
public:
virtual ~FontFactory();
// Factory method for the construction of a font factory.
static CALLER_ATTACH FontFactory* GetInstance();
// Toggle whether fonts that are loaded are fingerprinted with a SHA-1 hash.
// If a font is fingerprinted then a SHA-1 hash is generated at load time and
// stored in the font. This is useful for uniquely identifying fonts. By
// default this is turned on.
// @param fingerprint whether fingerprinting should be turned on or off
// TODO(arthurhsu): IMPLEMENT: C++ port currently don't do any SHA-1
void FingerprintFont(bool fingerprint);
bool FingerprintFont();
// Load the font(s) from the input stream. The current settings on the factory
// are used during the loading process. One or more fonts are returned if the
// stream contains valid font data. Some font container formats may have more
// than one font and in this case multiple font objects will be returned. If
// the data in the stream cannot be parsed or is invalid an array of size zero
// will be returned.
void LoadFonts(InputStream* is, FontArray* output);
// ByteArray font loading
// Load the font(s) from the byte array. The current settings on the factory
// are used during the loading process. One or more fonts are returned if the
// stream contains valid font data. Some font container formats may have more
// than one font and in this case multiple font objects will be returned. If
// the data in the stream cannot be parsed or is invalid an array of size zero
// will be returned.
void LoadFonts(ByteVector* b, FontArray* output);
// Load the font(s) from the input stream into font builders. The current
// settings on the factory are used during the loading process. One or more
// font builders are returned if the stream contains valid font data. Some
// font container formats may have more than one font and in this case
// multiple font builder objects will be returned. If the data in the stream
// cannot be parsed or is invalid an array of size zero will be returned.
void LoadFontsForBuilding(InputStream* is, FontBuilderArray* output);
// Load the font(s) from the byte array into font builders. The current
// settings on the factory are used during the loading process. One or more
// font builders are returned if the stream contains valid font data. Some
// font container formats may have more than one font and in this case
// multiple font builder objects will be returned. If the data in the stream
// cannot be parsed or is invalid an array of size zero will be returned.
void LoadFontsForBuilding(ByteVector* b, FontBuilderArray* output);
// Font serialization
// Serialize the font to the output stream.
// NOTE: in this port we attempted not to implement I/O stream because dealing
// with cross-platform I/O stream itself is big enough as a project.
// Byte buffer it is.
void SerializeFont(Font* font, OutputStream* os);
// Set the table ordering to be used in serializing a font. The table ordering
// is an ordered list of table ids and tables will be serialized in the order
// given. Any tables whose id is not listed in the ordering will be placed in
// an unspecified order following those listed.
void SetSerializationTableOrdering(const IntegerList& table_ordering);
// Get an empty font builder for creating a new font from scratch.
CALLER_ATTACH Font::Builder* NewFontBuilder();
private:
// Offsets to specific elements in the underlying data. These offsets are
// relative to the start of the table or the start of sub-blocks within the
// table.
struct Offset {
enum {
// Offsets within the main directory.
kTTCTag = 0,
kVersion = 4,
kNumFonts = 8,
kOffsetTable = 12,
// TTC Version 2.0 extensions.
// Offsets from end of OffsetTable.
kulDsigTag = 0,
kulDsigLength = 4,
kulDsigOffset = 8
};
};
FontFactory();
CALLER_ATTACH Font* LoadSingleOTF(InputStream* is);
CALLER_ATTACH Font* LoadSingleOTF(WritableFontData* wfd);
void LoadCollection(InputStream* is, FontArray* output);
void LoadCollection(WritableFontData* wfd, FontArray* output);
CALLER_ATTACH Font::Builder* LoadSingleOTFForBuilding(InputStream* is);
CALLER_ATTACH Font::Builder*
LoadSingleOTFForBuilding(WritableFontData* wfd,
int32_t offset_to_offset_table);
void LoadCollectionForBuilding(InputStream* is, FontBuilderArray* builders);
void LoadCollectionForBuilding(WritableFontData* ba,
FontBuilderArray* builders);
static bool IsCollection(PushbackInputStream* pbis);
static bool IsCollection(ReadableFontData* wfd);
bool fingerprint_;
IntegerList table_ordering_;
};
typedef Ptr<FontFactory> FontFactoryPtr;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_

View File

@ -0,0 +1,41 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
#define SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
#include "sfntly/port/type.h"
namespace sfntly {
class Fixed1616 {
public:
static inline int32_t Integral(int32_t fixed) {
return (fixed >> 16);
}
static inline int32_t Fractional(int32_t fixed) {
return (fixed & 0xffff);
}
static inline int32_t Fixed(int32_t integral, int32_t fractional) {
return ((integral & 0xffff) << 16) | (fractional & 0xffff);
}
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_

View File

@ -0,0 +1,49 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
#define SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
#include "sfntly/port/type.h"
namespace sfntly {
class FontMath {
public:
static int32_t Log2(int32_t a) {
int r = 0; // r will be lg(a)
while (a != 0) {
a >>= 1;
r++;
}
return r - 1;
}
// Calculates the amount of padding needed. The values provided need to be in
// the same units. So, if the size is given as the number of bytes then the
// alignment size must also be specified as byte size to align to.
// @param size the size of the data that may need padding
// @param alignmentSize the number of units to align to
// @return the number of units needing to be added for alignment
static int32_t PaddingRequired(int32_t size, int32_t alignment_size) {
int32_t padding = alignment_size - (size % alignment_size);
return padding == alignment_size ? 0 : padding;
}
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_

View File

@ -0,0 +1,71 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
#if defined (WIN32)
#include <windows.h>
static inline size_t AtomicIncrement(size_t* address) {
#if defined (_WIN64)
return InterlockedIncrement64(reinterpret_cast<LONGLONG*>(address));
#else
return InterlockedIncrement(reinterpret_cast<LONG*>(address));
#endif
}
static inline size_t AtomicDecrement(size_t* address) {
#if defined (_WIN64)
return InterlockedDecrement64(reinterpret_cast<LONGLONG*>(address));
#else
return InterlockedDecrement(reinterpret_cast<LONG*>(address));
#endif
}
#elif defined (__APPLE__)
#include <libkern/OSAtomic.h>
static inline size_t AtomicIncrement(size_t* address) {
return OSAtomicIncrement32Barrier(reinterpret_cast<int32_t*>(address));
}
static inline size_t AtomicDecrement(size_t* address) {
return OSAtomicDecrement32Barrier(reinterpret_cast<int32_t*>(address));
}
// Originally we check __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4, however, there are
// issues that clang not carring over this definition. Therefore we boldly
// assume it's gcc or gcc-compatible here. Compilation shall still fail since
// the intrinsics used are GCC-specific.
#else
#include <stddef.h>
static inline size_t AtomicIncrement(size_t* address) {
return __sync_add_and_fetch(address, 1);
}
static inline size_t AtomicDecrement(size_t* address) {
return __sync_sub_and_fetch(address, 1);
}
#endif // WIN32
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_

View File

@ -0,0 +1,28 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
#if !defined(SFNTLY_BIG_ENDIAN) && !defined(SFNTLY_LITTLE_ENDIAN)
#if defined (__ppc__) || defined (__ppc64__)
#define SFNTLY_BIG_ENDIAN
#else
#define SFNTLY_LITTLE_ENDIAN
#endif
#endif
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_

View File

@ -0,0 +1,77 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
#include "sfntly/port/config.h"
#include "sfntly/port/type.h"
namespace sfntly {
static inline uint16_t EndianSwap16(uint16_t value) {
return (uint16_t)((value >> 8) | (value << 8));
}
static inline int32_t EndianSwap32(int32_t value) {
return (((value & 0x000000ff) << 24) |
((value & 0x0000ff00) << 8) |
((value & 0x00ff0000) >> 8) |
((value & 0xff000000) >> 24));
}
static inline uint64_t EndianSwap64(uint64_t value) {
return (((value & 0x00000000000000ffLL) << 56) |
((value & 0x000000000000ff00LL) << 40) |
((value & 0x0000000000ff0000LL) << 24) |
((value & 0x00000000ff000000LL) << 8) |
((value & 0x000000ff00000000LL) >> 8) |
((value & 0x0000ff0000000000LL) >> 24) |
((value & 0x00ff000000000000LL) >> 40) |
((value & 0xff00000000000000LL) >> 56));
}
#ifdef SFNTLY_LITTLE_ENDIAN
#define ToBE16(n) EndianSwap16(n)
#define ToBE32(n) EndianSwap32(n)
#define ToBE64(n) EndianSwap64(n)
#define ToLE16(n) (n)
#define ToLE32(n) (n)
#define ToLE64(n) (n)
#define FromBE16(n) EndianSwap16(n)
#define FromBE32(n) EndianSwap32(n)
#define FromBE64(n) EndianSwap64(n)
#define FromLE16(n) (n)
#define FromLE32(n) (n)
#define FromLE64(n) (n)
#else // SFNTLY_BIG_ENDIAN
#define ToBE16(n) (n)
#define ToBE32(n) (n)
#define ToBE64(n) (n)
#define ToLE16(n) EndianSwap16(n)
#define ToLE32(n) EndianSwap32(n)
#define ToLE64(n) EndianSwap64(n)
#define FromBE16(n) (n)
#define FromBE32(n) (n)
#define FromBE64(n) (n)
#define FromLE16(n) EndianSwap16(n)
#define FromLE32(n) EndianSwap32(n)
#define FromLE64(n) EndianSwap64(n)
#endif
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_

View File

@ -0,0 +1,125 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Exceptions used in sfntly
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_EXCEPTION_TYPE_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_EXCEPTION_TYPE_H_
#if !defined (SFNTLY_NO_EXCEPTION)
#include <exception>
#include <string>
#include <sstream>
namespace sfntly {
class Exception : public std::exception {
public:
Exception() : what_("Unknown exception") {}
explicit Exception(const char* message) throw() { SetMessage(message); }
virtual ~Exception() throw() {}
virtual const char* what() const throw() { return what_.c_str(); }
protected:
void SetMessage(const char* message) throw() {
try {
what_ = message;
} catch (...) {}
}
private:
std::string what_;
};
class IndexOutOfBoundException : public Exception {
public:
IndexOutOfBoundException() throw() : Exception("Index out of bound") {}
explicit IndexOutOfBoundException(const char* message) throw()
: Exception(message) {}
IndexOutOfBoundException(const char* message, int32_t index) throw() {
try {
std::ostringstream msg;
msg << message;
msg << ":";
msg << index;
SetMessage(msg.str().c_str());
} catch (...) {}
}
virtual ~IndexOutOfBoundException() throw() {}
};
class IOException : public Exception {
public:
IOException() throw() : Exception("I/O exception") {}
explicit IOException(const char* message) throw() : Exception(message) {}
virtual ~IOException() throw() {}
};
class ArithmeticException : public Exception {
public:
ArithmeticException() throw() : Exception("Arithmetic exception") {}
explicit ArithmeticException(const char* message) throw()
: Exception(message) {}
virtual ~ArithmeticException() throw() {}
};
class UnsupportedOperationException : public Exception {
public:
UnsupportedOperationException() throw() :
Exception("Operation not supported") {}
explicit UnsupportedOperationException(const char* message) throw()
: Exception(message) {}
virtual ~UnsupportedOperationException() throw() {}
};
class RuntimeException : public Exception {
public:
RuntimeException() throw() : Exception("Runtime exception") {}
explicit RuntimeException(const char* message) throw()
: Exception(message) {}
virtual ~RuntimeException() throw() {}
};
class NoSuchElementException : public Exception {
public:
NoSuchElementException() throw() : Exception("No such element") {}
explicit NoSuchElementException(const char* message) throw()
: Exception(message) {}
virtual ~NoSuchElementException() throw() {}
};
class IllegalArgumentException : public Exception {
public:
IllegalArgumentException() throw() : Exception("Illegal argument") {}
explicit IllegalArgumentException(const char* message) throw()
: Exception(message) {}
virtual ~IllegalArgumentException() throw() {}
};
class IllegalStateException : public Exception {
public:
IllegalStateException() throw() : Exception("Illegal state") {}
explicit IllegalStateException(const char* message) throw()
: Exception(message) {}
virtual ~IllegalStateException() throw() {}
};
} // namespace sfntly
#endif // #if !defined (SFNTLY_NO_EXCEPTION)
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_EXCEPTION_TYPE_H_

View File

@ -0,0 +1,169 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined (WIN32)
#include <windows.h>
#endif
#include "sfntly/port/file_input_stream.h"
#include "sfntly/port/exception_type.h"
namespace sfntly {
FileInputStream::FileInputStream()
: file_(NULL),
position_(0),
length_(0) {
}
FileInputStream::~FileInputStream() {
Close();
}
int32_t FileInputStream::Available() {
return length_ - position_;
}
void FileInputStream::Close() {
if (file_) {
fclose(file_);
length_ = 0;
position_ = 0;
file_ = NULL;
}
}
void FileInputStream::Mark(int32_t readlimit) {
// NOP
UNREFERENCED_PARAMETER(readlimit);
}
bool FileInputStream::MarkSupported() {
return false;
}
int32_t FileInputStream::Read() {
if (!file_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no opened file");
#endif
return 0;
}
if (feof(file_)) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("eof reached");
#endif
return 0;
}
byte_t value;
size_t length = fread(&value, 1, 1, file_);
position_ += length;
return value;
}
int32_t FileInputStream::Read(ByteVector* b) {
return Read(b, 0, b->size());
}
int32_t FileInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
assert(b);
if (!file_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no opened file");
#endif
return 0;
}
if (feof(file_)) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("eof reached");
#endif
return 0;
}
size_t read_count = std::min<size_t>(length_ - position_, length);
if (b->size() < (size_t)(offset + read_count)) {
b->resize((size_t)(offset + read_count));
}
int32_t actual_read = fread(&((*b)[offset]), 1, read_count, file_);
position_ += actual_read;
return actual_read;
}
void FileInputStream::Reset() {
// NOP
}
int64_t FileInputStream::Skip(int64_t n) {
if (!file_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no opened file");
#endif
return 0;
}
int64_t skip_count = 0;
if (n < 0) { // move backwards
skip_count = std::max<int64_t>(0 - (int64_t)position_, n);
position_ -= (size_t)(0 - skip_count);
fseek(file_, position_, SEEK_SET);
} else {
skip_count = std::min<size_t>(length_ - position_, (size_t)n);
position_ += (size_t)skip_count;
fseek(file_, (size_t)skip_count, SEEK_CUR);
}
return skip_count;
}
void FileInputStream::Unread(ByteVector* b) {
Unread(b, 0, b->size());
}
void FileInputStream::Unread(ByteVector* b, int32_t offset, int32_t length) {
assert(b);
assert(b->size() >= size_t(offset + length));
if (!file_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no opened file");
#endif
return;
}
size_t unread_count = std::min<size_t>(position_, length);
fseek(file_, position_ - unread_count, SEEK_SET);
position_ -= unread_count;
Read(b, offset, length);
fseek(file_, position_ - unread_count, SEEK_SET);
position_ -= unread_count;
}
bool FileInputStream::Open(const char* file_path) {
assert(file_path);
if (file_) {
Close();
}
#if defined (WIN32)
fopen_s(&file_, file_path, "rb");
#else
file_ = fopen(file_path, "rb");
#endif
if (file_ == NULL) {
return false;
}
fseek(file_, 0, SEEK_END);
length_ = ftell(file_);
fseek(file_, 0, SEEK_SET);
return true;
}
} // namespace sfntly

View File

@ -0,0 +1,57 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_FILE_INPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_FILE_INPUT_STREAM_H_
#include <stdio.h>
#include "sfntly/port/input_stream.h"
namespace sfntly {
class FileInputStream : public PushbackInputStream {
public:
FileInputStream();
virtual ~FileInputStream();
// InputStream methods
virtual int32_t Available();
virtual void Close();
virtual void Mark(int32_t readlimit);
virtual bool MarkSupported();
virtual int32_t Read();
virtual int32_t Read(ByteVector* b);
virtual int32_t Read(ByteVector* b, int32_t offset, int32_t length);
virtual void Reset();
virtual int64_t Skip(int64_t n);
// PushbackInputStream methods
virtual void Unread(ByteVector* b);
virtual void Unread(ByteVector* b, int32_t offset, int32_t length);
// Own methods
virtual bool Open(const char* file_path);
private:
FILE* file_;
size_t position_;
size_t length_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_FILE_INPUT_STREAM_H_

View File

@ -0,0 +1,49 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_INPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_INPUT_STREAM_H_
#include "sfntly/port/type.h"
namespace sfntly {
// C++ equivalent to Java's OutputStream class
class InputStream {
public:
// Make gcc -Wnon-virtual-dtor happy.
virtual ~InputStream() {}
virtual int32_t Available() = 0;
virtual void Close() = 0;
virtual void Mark(int32_t readlimit) = 0;
virtual bool MarkSupported() = 0;
virtual int32_t Read() = 0;
virtual int32_t Read(ByteVector* b) = 0;
virtual int32_t Read(ByteVector* b, int32_t offset, int32_t length) = 0;
virtual void Reset() = 0;
virtual int64_t Skip(int64_t n) = 0;
};
class PushbackInputStream : public InputStream {
public:
virtual void Unread(ByteVector* b) = 0;
virtual void Unread(ByteVector* b, int32_t offset, int32_t length) = 0;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_INPUT_STREAM_H_

View File

@ -0,0 +1,94 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_JAVA_ITERATOR_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_JAVA_ITERATOR_H_
#include "sfntly/port/refcount.h"
// Interface of Java iterator.
// This is a forward read-only iterator that represents java.util.Iterator<E>
namespace sfntly {
template <typename ReturnType, typename ContainerBase>
class Iterator : public virtual RefCount {
public:
virtual ~Iterator() {}
virtual ContainerBase* container_base() = 0;
protected:
Iterator() {}
NO_COPY_AND_ASSIGN(Iterator);
};
template <typename ReturnType, typename Container,
typename ContainerBase = Container>
class PODIterator : public Iterator<ReturnType, ContainerBase>,
public RefCounted< PODIterator<ReturnType, Container> > {
public:
explicit PODIterator(Container* container) : container_(container) {}
virtual ~PODIterator() {}
virtual ContainerBase* container_base() {
return static_cast<ContainerBase*>(container_);
}
virtual bool HasNext() = 0;
virtual ReturnType Next() = 0;
virtual void Remove() {
#if !defined (SFNTLY_NO_EXCEPTION)
// Default to no support.
throw UnsupportedOperationException();
#endif
}
protected:
Container* container() { return container_; }
private:
Container* container_; // Dumb pointer is used to avoid circular ref-counting
};
template <typename ReturnType, typename Container,
typename ContainerBase = Container>
class RefIterator : public Iterator<ReturnType, ContainerBase>,
public RefCounted< RefIterator<ReturnType, Container> > {
public:
explicit RefIterator(Container* container) : container_(container) {}
virtual ~RefIterator() {}
virtual ContainerBase* container_base() {
return static_cast<ContainerBase*>(container_);
}
virtual bool HasNext() = 0;
CALLER_ATTACH virtual ReturnType* Next() = 0;
virtual void Remove() {
#if !defined (SFNTLY_NO_EXCEPTION)
// Default to no support.
throw UnsupportedOperationException();
#endif
}
protected:
Container* container() { return container_; }
private:
Container* container_; // Dumb pointer is used to avoid circular ref-counting
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_JAVA_ITERATOR_H_

View File

@ -0,0 +1,72 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/port/lock.h"
namespace sfntly {
#if defined (WIN32)
Lock::Lock() {
// The second parameter is the spin count, for short-held locks it avoid the
// contending thread from going to sleep which helps performance greatly.
::InitializeCriticalSectionAndSpinCount(&os_lock_, 2000);
}
Lock::~Lock() {
::DeleteCriticalSection(&os_lock_);
}
bool Lock::Try() {
if (::TryEnterCriticalSection(&os_lock_) != FALSE) {
return true;
}
return false;
}
void Lock::Acquire() {
::EnterCriticalSection(&os_lock_);
}
void Lock::Unlock() {
::LeaveCriticalSection(&os_lock_);
}
#else // We assume it's pthread
Lock::Lock() {
pthread_mutex_init(&os_lock_, NULL);
}
Lock::~Lock() {
pthread_mutex_destroy(&os_lock_);
}
bool Lock::Try() {
return (pthread_mutex_trylock(&os_lock_) == 0);
}
void Lock::Acquire() {
pthread_mutex_lock(&os_lock_);
}
void Lock::Unlock() {
pthread_mutex_unlock(&os_lock_);
}
#endif
} // namespace sfntly

View File

@ -0,0 +1,76 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_LOCK_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_LOCK_H_
#if defined (WIN32)
#include <windows.h>
#else // Assume pthread.
#include <pthread.h>
#include <errno.h>
#endif
#include "sfntly/port/type.h"
namespace sfntly {
#if defined (WIN32)
typedef CRITICAL_SECTION OSLockType;
#else // Assume pthread.
typedef pthread_mutex_t OSLockType;
#endif
class Lock {
public:
Lock();
~Lock();
// If the lock is not held, take it and return true. If the lock is already
// held by something else, immediately return false.
bool Try();
// Take the lock, blocking until it is available if necessary.
void Acquire();
// Release the lock. This must only be called by the lock's holder: after
// a successful call to Try, or a call to Lock.
void Unlock();
private:
OSLockType os_lock_;
NO_COPY_AND_ASSIGN(Lock);
};
// A helper class that acquires the given Lock while the AutoLock is in scope.
class AutoLock {
public:
explicit AutoLock(Lock& lock) : lock_(lock) {
lock_.Acquire();
}
~AutoLock() {
lock_.Unlock();
}
private:
Lock& lock_;
NO_COPY_AND_ASSIGN(AutoLock);
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_LOCK_H_

View File

@ -0,0 +1,147 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined (WIN32)
#include <windows.h>
#endif
#include <string.h>
#include "sfntly/port/memory_input_stream.h"
#include "sfntly/port/exception_type.h"
namespace sfntly {
MemoryInputStream::MemoryInputStream()
: buffer_(NULL),
position_(0),
length_(0) {
}
MemoryInputStream::~MemoryInputStream() {
Close();
}
int32_t MemoryInputStream::Available() {
return length_ - position_;
}
void MemoryInputStream::Close() {
}
void MemoryInputStream::Mark(int32_t readlimit) {
// NOP
UNREFERENCED_PARAMETER(readlimit);
}
bool MemoryInputStream::MarkSupported() {
return false;
}
int32_t MemoryInputStream::Read() {
if (!buffer_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no memory attached");
#endif
return 0;
}
if (position_ >= length_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("eof reached");
#endif
return 0;
}
byte_t value = buffer_[position_++];
return value;
}
int32_t MemoryInputStream::Read(ByteVector* b) {
return Read(b, 0, b->size());
}
int32_t MemoryInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
assert(b);
if (!buffer_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no memory attached");
#endif
return 0;
}
if (position_ >= length_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("eof reached");
#endif
return 0;
}
size_t read_count = std::min<size_t>(length_ - position_, length);
if (b->size() < (size_t)(offset + read_count)) {
b->resize((size_t)(offset + read_count));
}
memcpy(&((*b)[offset]), buffer_ + position_, read_count);
position_ += read_count;
return read_count;
}
void MemoryInputStream::Reset() {
// NOP
}
int64_t MemoryInputStream::Skip(int64_t n) {
if (!buffer_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no memory attached");
#endif
return 0;
}
int64_t skip_count = 0;
if (n < 0) { // move backwards
skip_count = std::max<int64_t>(0 - (int64_t)position_, n);
position_ -= (size_t)(0 - skip_count);
} else {
skip_count = std::min<size_t>(length_ - position_, (size_t)n);
position_ += (size_t)skip_count;
}
return skip_count;
}
void MemoryInputStream::Unread(ByteVector* b) {
Unread(b, 0, b->size());
}
void MemoryInputStream::Unread(ByteVector* b, int32_t offset, int32_t length) {
assert(b);
assert(b->size() >= size_t(offset + length));
if (!buffer_) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IOException("no memory attached");
#endif
return;
}
size_t unread_count = std::min<size_t>(position_, length);
position_ -= unread_count;
Read(b, offset, length);
position_ -= unread_count;
}
bool MemoryInputStream::Attach(const byte_t* buffer, size_t length) {
assert(buffer);
assert(length);
buffer_ = buffer;
length_ = length;
return true;
}
} // namespace sfntly

View File

@ -0,0 +1,57 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_INPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_INPUT_STREAM_H_
#include <stdio.h>
#include "sfntly/port/input_stream.h"
namespace sfntly {
class MemoryInputStream : public PushbackInputStream {
public:
MemoryInputStream();
virtual ~MemoryInputStream();
// InputStream methods
virtual int32_t Available();
virtual void Close();
virtual void Mark(int32_t readlimit);
virtual bool MarkSupported();
virtual int32_t Read();
virtual int32_t Read(ByteVector* b);
virtual int32_t Read(ByteVector* b, int32_t offset, int32_t length);
virtual void Reset();
virtual int64_t Skip(int64_t n);
// PushbackInputStream methods
virtual void Unread(ByteVector* b);
virtual void Unread(ByteVector* b, int32_t offset, int32_t length);
// Own methods
virtual bool Attach(const byte_t* buffer, size_t length);
private:
const byte_t* buffer_;
size_t position_;
size_t length_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_INPUT_STREAM_H_

View File

@ -0,0 +1,72 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/port/memory_output_stream.h"
namespace sfntly {
MemoryOutputStream::MemoryOutputStream() {
}
MemoryOutputStream::~MemoryOutputStream() {
}
void MemoryOutputStream::Write(ByteVector* buffer) {
store_.insert(store_.end(), buffer->begin(), buffer->end());
}
void MemoryOutputStream::Write(ByteVector* buffer,
int32_t offset,
int32_t length) {
assert(buffer);
if (offset >= 0 && length > 0) {
store_.insert(store_.end(),
buffer->begin() + offset,
buffer->begin() + offset + length);
} else {
#if !defined(SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundException();
#endif
}
}
void MemoryOutputStream::Write(byte_t* buffer, int32_t offset, int32_t length) {
assert(buffer);
if (offset >= 0 && length > 0) {
store_.insert(store_.end(), buffer + offset, buffer + offset + length);
} else {
#if !defined(SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundException();
#endif
}
}
void MemoryOutputStream::Write(byte_t b) {
store_.push_back(b);
}
byte_t* MemoryOutputStream::Get() {
if (store_.empty()) {
return NULL;
}
return &(store_[0]);
}
size_t MemoryOutputStream::Size() {
return store_.size();
}
} // namespace sfntly

View File

@ -0,0 +1,51 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_
#include <cstddef>
#include <vector>
#include "sfntly/port/type.h"
#include "sfntly/port/output_stream.h"
namespace sfntly {
// OutputStream backed by STL vector
class MemoryOutputStream : public OutputStream {
public:
MemoryOutputStream();
virtual ~MemoryOutputStream();
virtual void Close() {} // no-op
virtual void Flush() {} // no-op
virtual void Write(ByteVector* buffer);
virtual void Write(ByteVector* buffer, int32_t offset, int32_t length);
virtual void Write(byte_t* buffer, int32_t offset, int32_t length);
virtual void Write(byte_t b);
byte_t* Get();
size_t Size();
private:
std::vector<byte_t> store_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_

View File

@ -0,0 +1,46 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_OUTPUT_STREAM_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_OUTPUT_STREAM_H_
#include "sfntly/port/type.h"
namespace sfntly {
// C++ equivalent to Java's OutputStream class
class OutputStream {
public:
// Make gcc -Wnon-virtual-dtor happy.
virtual ~OutputStream() {}
virtual void Close() = 0;
virtual void Flush() = 0;
virtual void Write(ByteVector* buffer) = 0;
virtual void Write(byte_t b) = 0;
// Note: C++ port offered both versions of Write() here. The first one is
// better because it does check bounds. The second one is there for
// performance concerns.
virtual void Write(ByteVector* buffer, int32_t offset, int32_t length) = 0;
// Note: Caller is responsible for the boundary of buffer.
virtual void Write(byte_t* buffer, int32_t offset, int32_t length) = 0;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_OUTPUT_STREAM_H_

View File

@ -0,0 +1,277 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Object reference count and smart pointer implementation.
// Smart pointer usage in sfntly:
//
// sfntly carries a smart pointer implementation like COM. Ref-countable object
// type inherits from RefCounted<>, which have AddRef and Release just like
// IUnknown (but no QueryInterface). Use a Ptr<> based smart pointer to hold
// the object so that the object ref count is handled correctly.
//
// class Foo : public RefCounted<Foo> {
// public:
// static Foo* CreateInstance() {
// Ptr<Foo> obj = new Foo(); // ref count = 1
// return obj.Detach();
// }
// };
// typedef Ptr<Foo> FooPtr; // common short-hand notation
// FooPtr obj;
// obj.Attach(Foo::CreatedInstance()); // ref count = 1
// {
// FooPtr obj2 = obj; // ref count = 2
// } // ref count = 1, obj2 out of scope
// obj.Release(); // ref count = 0, object destroyed
// Notes on usage:
// 1. Virtual inherit from RefCount interface in base class if smart pointers
// are going to be defined.
// 2. All RefCounted objects must be instantiated on the heap. Allocating the
// object on stack will cause crash.
// 3. Be careful when you have complex inheritance. For example,
// class A : public RefCounted<A>;
// class B : public A, public RefCounted<B>;
// In this case the smart pointer is pretty dumb and don't count on it to
// nicely destroy your objects as designed. Try refactor your code like
// class I; // the common interface and implementations
// class A : public I, public RefCounted<A>; // A specific implementation
// class B : public I, public RefCounted<B>; // B specific implementation
// 4. Smart pointers here are very bad candidates for function parameters. Use
// dumb pointers in function parameter list.
// 5. When down_cast is performed on a dangling pointer due to bugs in code,
// VC++ will generate SEH which is not handled well in VC++ debugger. One
// can use WinDBG to run it and get the faulting stack.
// 6. Idioms for heap object as return value
// Foo* createFoo() { FooPtr obj = new Foo(); return obj.Detach(); }
// Foo* passthru() { FooPtr obj = createFoo(), return obj; }
// FooPtr end_scope_pointer;
// end_scope_pointer.Attach(passThrough);
// If you are not passing that object back, you are the end of scope.
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
#if !defined (NDEBUG)
#define ENABLE_OBJECT_COUNTER
// #define REF_COUNT_DEBUGGING
#endif
#if defined (REF_COUNT_DEBUGGING)
#include <stdio.h>
#include <typeinfo>
#endif
#include "sfntly/port/atomic.h"
#include "sfntly/port/type.h"
// Special tag for functions that requires caller to attach instead of using
// assignment operators.
#define CALLER_ATTACH
#if defined (REF_COUNT_DEBUGGING)
#define DEBUG_OUTPUT(a) \
fprintf(stderr, "%s%s:oc=%d,oid=%d,rc=%d\n", a, \
typeid(this).name(), object_counter_, object_id_, ref_count_)
#else
#define DEBUG_OUTPUT(a)
#endif
#if defined (_MSC_VER)
// VC 2008/2010 incorrectly gives this warning for pure virtual functions
// in virtual inheritance. The only way to get around it is to disable it.
#pragma warning(disable:4250)
#endif
namespace sfntly {
class RefCount {
public:
// Make gcc -Wnon-virtual-dtor happy.
virtual ~RefCount() {}
virtual size_t AddRef() const = 0;
virtual size_t Release() const = 0;
};
template <typename T>
class NoAddRefRelease : public T {
public:
NoAddRefRelease();
~NoAddRefRelease();
private:
virtual size_t AddRef() const = 0;
virtual size_t Release() const = 0;
};
template <typename TDerived>
class RefCounted : virtual public RefCount {
public:
RefCounted() : ref_count_(0) {
#if defined (ENABLE_OBJECT_COUNTER)
object_id_ = AtomicIncrement(&next_id_);
AtomicIncrement(&object_counter_);
DEBUG_OUTPUT("C ");
#endif
}
RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
virtual ~RefCounted() {
#if defined (ENABLE_OBJECT_COUNTER)
AtomicDecrement(&object_counter_);
DEBUG_OUTPUT("D ");
#endif
}
RefCounted<TDerived>& operator=(const RefCounted<TDerived>&) {
// Each object maintains own ref count, don't propagate.
return *this;
}
virtual size_t AddRef() const {
size_t new_count = AtomicIncrement(&ref_count_);
DEBUG_OUTPUT("A ");
return new_count;
}
virtual size_t Release() const {
size_t new_ref_count = AtomicDecrement(&ref_count_);
DEBUG_OUTPUT("R ");
if (new_ref_count == 0) {
// A C-style is used to cast away const-ness and to derived.
// lint does not like this but this is how it works.
delete (TDerived*)(this);
}
return new_ref_count;
}
mutable size_t ref_count_; // reference count of current object
#if defined (ENABLE_OBJECT_COUNTER)
static size_t object_counter_;
static size_t next_id_;
mutable size_t object_id_;
#endif
};
#if defined (ENABLE_OBJECT_COUNTER)
template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
#endif
// semi-smart pointer for RefCount derived objects, similar to CComPtr
template <typename T>
class Ptr {
public:
Ptr() : p_(NULL) {
}
// This constructor shall not be explicit.
// lint does not like this but this is how it works.
Ptr(T* pT) : p_(NULL) {
*this = pT;
}
Ptr(const Ptr<T>& p) : p_(NULL) {
*this = p;
}
~Ptr() {
Release();
}
T* operator=(T* pT) {
if (p_ == pT) {
return p_;
}
if (pT) {
RefCount* p = static_cast<RefCount*>(pT);
if (p == NULL) {
return NULL;
}
p->AddRef(); // always AddRef() before Release()
}
Release();
p_ = pT;
return p_;
}
T* operator=(const Ptr<T>& p) {
if (p_ == p.p_) {
return p_;
}
return operator=(p.p_);
}
operator T*&() {
return p_;
}
T& operator*() const {
return *p_; // It can throw!
}
NoAddRefRelease<T>* operator->() const {
return (NoAddRefRelease<T>*)p_; // It can throw!
}
bool operator!() const {
return (p_ == NULL);
}
bool operator<(const Ptr<T>& p) const {
return (p_ < p.p_);
}
bool operator!=(T* pT) const {
return !operator==(pT);
}
bool operator==(T* pT) const {
return (p_ == pT);
}
size_t Release() const {
size_t ref_count = 0;
if (p_) {
RefCount* p = static_cast<RefCount*>(p_);
if (p) {
ref_count = p->Release();
}
p_ = NULL;
}
return ref_count;
}
void Attach(T* pT) {
if (p_ != pT) {
Release();
p_ = pT;
}
}
T* Detach() {
T* pT = p_;
p_ = NULL;
return pT;
}
mutable T* p_;
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_

View File

@ -0,0 +1,102 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_TYPE_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_TYPE_H_
#include <assert.h>
#if defined (_MSC_VER) && (_MSC_VER < 1600)
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned __int16 uint16_t;
typedef signed __int16 int16_t;
typedef unsigned __int32 uint32_t;
typedef signed __int32 int32_t;
typedef unsigned __int64 uint64_t;
typedef signed __int64 int64_t;
// Definitions to avoid ICU redefinition issue
#define U_HAVE_INT8_T 1
#define U_HAVE_UINT8_T 1
#define U_HAVE_INT16_T 1
#define U_HAVE_UINT16_T 1
#define U_HAVE_INT32_T 1
#define U_HAVE_UINT32_T 1
#define U_HAVE_INT64_T 1
#define U_HAVE_UINT64_T 1
#else
#include <stdint.h>
#endif
#include <cstddef>
#include <vector>
#include <set>
namespace sfntly {
typedef uint8_t byte_t;
typedef uint16_t word_t;
typedef uint32_t dword_t;
typedef uint64_t qword_t;
typedef std::vector<byte_t> ByteVector;
typedef std::vector<int32_t> IntegerList;
typedef std::set<int32_t> IntegerSet;
// A macro to disallow the copy constructor and operator= functions.
// This should be used in the private: declarations for a class.
#define NO_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
} // namespace sfntly
// Make google3 happy since it prohibits RTTI.
template<typename To, typename From>
inline To implicit_cast(From const &f) {
return f;
}
template<typename To, typename From> // use like this: down_cast<T*>(foo);
inline To down_cast(From* f) { // so we only accept pointers
// Ensures that To is a sub-type of From *. This test is here only
// for compile-time type checking, and has no overhead in an
// optimized build at run-time, as it will be optimized away
// completely.
#if defined (_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4127) // disable "conditional expression is constant"
#endif
if (false) {
implicit_cast<From*, To>(0);
}
#if defined (_MSC_VER)
#pragma warning(pop)
#endif
// The following code is the only place for RTTI. It is done so to allow
// additional type checking when SFNTLY_TYPE_VERIFICATION is defined.
#if defined (SFNTLY_TYPE_VERIFICATION)
assert(f == NULL || dynamic_cast<To>(f) != NULL);
#endif
return static_cast<To>(f);
}
#if !defined(WIN32)
#define UNREFERENCED_PARAMETER(p) do { (void)p; } while (0)
#endif
#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_TYPE_H_

View File

@ -0,0 +1,171 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/table/bitmap/big_glyph_metrics.h"
namespace sfntly {
/******************************************************************************
* BigGlyphMetrics class
******************************************************************************/
BigGlyphMetrics::BigGlyphMetrics(ReadableFontData* data)
: GlyphMetrics(data) {
}
BigGlyphMetrics::~BigGlyphMetrics() {
}
int32_t BigGlyphMetrics::Height() {
return data_->ReadByte(Offset::kHeight);
}
int32_t BigGlyphMetrics::Width() {
return data_->ReadByte(Offset::kWidth);
}
int32_t BigGlyphMetrics::HoriBearingX() {
return data_->ReadByte(Offset::kHoriBearingX);
}
int32_t BigGlyphMetrics::HoriBearingY() {
return data_->ReadByte(Offset::kHoriBearingY);
}
int32_t BigGlyphMetrics::HoriAdvance() {
return data_->ReadByte(Offset::kHoriAdvance);
}
int32_t BigGlyphMetrics::VertBearingX() {
return data_->ReadByte(Offset::kVertBearingX);
}
int32_t BigGlyphMetrics::VertBearingY() {
return data_->ReadByte(Offset::kVertBearingY);
}
int32_t BigGlyphMetrics::VertAdvance() {
return data_->ReadByte(Offset::kVertAdvance);
}
/******************************************************************************
* BigGlyphMetrics::Builder class
******************************************************************************/
BigGlyphMetrics::Builder::Builder(WritableFontData* data)
: GlyphMetrics::Builder(data) {
}
BigGlyphMetrics::Builder::Builder(ReadableFontData* data)
: GlyphMetrics::Builder(data) {
}
BigGlyphMetrics::Builder::~Builder() {
}
int32_t BigGlyphMetrics::Builder::Height() {
return InternalReadData()->ReadByte(Offset::kHeight);
}
void BigGlyphMetrics::Builder::SetHeight(byte_t height) {
InternalWriteData()->WriteByte(Offset::kHeight, height);
}
int32_t BigGlyphMetrics::Builder::Width() {
return InternalReadData()->ReadByte(Offset::kWidth);
}
void BigGlyphMetrics::Builder::SetWidth(byte_t width) {
InternalWriteData()->WriteByte(Offset::kWidth, width);
}
int32_t BigGlyphMetrics::Builder::HoriBearingX() {
return InternalReadData()->ReadByte(Offset::kHoriBearingX);
}
void BigGlyphMetrics::Builder::SetHoriBearingX(byte_t bearing) {
InternalWriteData()->WriteByte(Offset::kHoriBearingX, bearing);
}
int32_t BigGlyphMetrics::Builder::HoriBearingY() {
return InternalReadData()->ReadByte(Offset::kHoriBearingY);
}
void BigGlyphMetrics::Builder::SetHoriBearingY(byte_t bearing) {
InternalWriteData()->WriteByte(Offset::kHoriBearingY, bearing);
}
int32_t BigGlyphMetrics::Builder::HoriAdvance() {
return InternalReadData()->ReadByte(Offset::kHoriAdvance);
}
void BigGlyphMetrics::Builder::SetHoriAdvance(byte_t advance) {
InternalWriteData()->WriteByte(Offset::kHoriAdvance, advance);
}
int32_t BigGlyphMetrics::Builder::VertBearingX() {
return InternalReadData()->ReadByte(Offset::kVertBearingX);
}
void BigGlyphMetrics::Builder::SetVertBearingX(byte_t bearing) {
InternalWriteData()->WriteByte(Offset::kVertBearingX, bearing);
}
int32_t BigGlyphMetrics::Builder::VertBearingY() {
return InternalReadData()->ReadByte(Offset::kVertBearingY);
}
void BigGlyphMetrics::Builder::SetVertBearingY(byte_t bearing) {
InternalWriteData()->WriteByte(Offset::kVertBearingY, bearing);
}
int32_t BigGlyphMetrics::Builder::VertAdvance() {
return InternalReadData()->ReadByte(Offset::kVertAdvance);
}
void BigGlyphMetrics::Builder::SetVertAdvance(byte_t advance) {
InternalWriteData()->WriteByte(Offset::kVertAdvance, advance);
}
CALLER_ATTACH FontDataTable*
BigGlyphMetrics::Builder::SubBuildTable(ReadableFontData* data) {
BigGlyphMetricsPtr output = new BigGlyphMetrics(data);
return output.Detach();
}
void BigGlyphMetrics::Builder::SubDataSet() {
// NOP.
}
int32_t BigGlyphMetrics::Builder::SubDataSizeToSerialize() {
return 0;
}
bool BigGlyphMetrics::Builder::SubReadyToSerialize() {
return false;
}
int32_t BigGlyphMetrics::Builder::SubSerialize(WritableFontData* new_data) {
return Data()->CopyTo(new_data);
}
// static
CALLER_ATTACH
BigGlyphMetrics::Builder* BigGlyphMetrics::Builder::CreateBuilder() {
WritableFontDataPtr data;
data.Attach(WritableFontData::CreateWritableFontData(Offset::kMetricsLength));
BigGlyphMetricsBuilderPtr output = new BigGlyphMetrics::Builder(data);
return output.Detach();
}
} // namespace sfntly

View File

@ -0,0 +1,96 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_
#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_
#include "sfntly/table/bitmap/glyph_metrics.h"
namespace sfntly {
class BigGlyphMetrics : public GlyphMetrics,
public RefCounted<BigGlyphMetrics> {
public:
struct Offset {
enum {
kMetricsLength = 8,
kHeight = 0,
kWidth = 1,
kHoriBearingX = 2,
kHoriBearingY = 3,
kHoriAdvance = 4,
kVertBearingX = 5,
kVertBearingY = 6,
kVertAdvance = 7,
};
};
class Builder : public GlyphMetrics::Builder,
public RefCounted<Builder> {
public:
// Constructor scope altered to public because C++ does not allow base
// class to instantiate derived class with protected constructors.
explicit Builder(WritableFontData* data);
explicit Builder(ReadableFontData* data);
virtual ~Builder();
int32_t Height();
void SetHeight(byte_t height);
int32_t Width();
void SetWidth(byte_t width);
int32_t HoriBearingX();
void SetHoriBearingX(byte_t bearing);
int32_t HoriBearingY();
void SetHoriBearingY(byte_t bearing);
int32_t HoriAdvance();
void SetHoriAdvance(byte_t advance);
int32_t VertBearingX();
void SetVertBearingX(byte_t bearing);
int32_t VertBearingY();
void SetVertBearingY(byte_t bearing);
int32_t VertAdvance();
void SetVertAdvance(byte_t advance);
virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
virtual void SubDataSet();
virtual int32_t SubDataSizeToSerialize();
virtual bool SubReadyToSerialize();
virtual int32_t SubSerialize(WritableFontData* new_data);
// Static instantiation function.
static CALLER_ATTACH Builder* CreateBuilder();
};
explicit BigGlyphMetrics(ReadableFontData* data);
virtual ~BigGlyphMetrics();
int32_t Height();
int32_t Width();
int32_t HoriBearingX();
int32_t HoriBearingY();
int32_t HoriAdvance();
int32_t VertBearingX();
int32_t VertBearingY();
int32_t VertAdvance();
};
typedef Ptr<BigGlyphMetrics> BigGlyphMetricsPtr;
typedef Ptr<BigGlyphMetrics::Builder> BigGlyphMetricsBuilderPtr;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_

View File

@ -0,0 +1,101 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 = 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/table/bitmap/bitmap_glyph.h"
#include "sfntly/table/bitmap/simple_bitmap_glyph.h"
#include "sfntly/table/bitmap/composite_bitmap_glyph.h"
namespace sfntly {
/******************************************************************************
* BitmapGlyph class
******************************************************************************/
BitmapGlyph::~BitmapGlyph() {
}
CALLER_ATTACH BitmapGlyph* BitmapGlyph::CreateGlyph(ReadableFontData* data,
int32_t format) {
BitmapGlyphPtr glyph;
BitmapGlyphBuilderPtr builder;
builder.Attach(Builder::CreateGlyphBuilder(data, format));
if (builder) {
glyph.Attach(down_cast<BitmapGlyph*>(builder->Build()));
}
return glyph;
}
BitmapGlyph::BitmapGlyph(ReadableFontData* data, int32_t format)
: SubTable(data), format_(format) {
}
/******************************************************************************
* BitmapGlyph::Builder class
******************************************************************************/
BitmapGlyph::Builder::~Builder() {
}
CALLER_ATTACH BitmapGlyph::Builder*
BitmapGlyph::Builder::CreateGlyphBuilder(ReadableFontData* data,
int32_t format) {
BitmapGlyphBuilderPtr builder;
switch (format) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
builder = new SimpleBitmapGlyph::Builder(data, format);
break;
case 8:
case 9:
builder = new CompositeBitmapGlyph::Builder(data, format);
break;
}
return builder.Detach();
}
BitmapGlyph::Builder::Builder(WritableFontData* data, int32_t format)
: SubTable::Builder(data), format_(format) {
}
BitmapGlyph::Builder::Builder(ReadableFontData* data, int32_t format)
: SubTable::Builder(data), format_(format) {
}
CALLER_ATTACH
FontDataTable* BitmapGlyph::Builder::SubBuildTable(ReadableFontData* data) {
UNREFERENCED_PARAMETER(data);
return NULL;
}
void BitmapGlyph::Builder::SubDataSet() {
// NOP
}
int32_t BitmapGlyph::Builder::SubDataSizeToSerialize() {
return InternalReadData()->Length();
}
bool BitmapGlyph::Builder::SubReadyToSerialize() {
return true;
}
int32_t BitmapGlyph::Builder::SubSerialize(WritableFontData* new_data) {
return InternalReadData()->CopyTo(new_data);
}
} // namespace sfntly

View File

@ -0,0 +1,119 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 = 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_
#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_
#include <vector>
#include <map>
#include "sfntly/table/subtable.h"
namespace sfntly {
class BitmapGlyph : public SubTable {
public:
struct Offset {
enum {
// header
kVersion = 0,
kSmallGlyphMetricsLength = 5,
kBigGlyphMetricsLength = 8,
// format 1
kGlyphFormat1_imageData = kSmallGlyphMetricsLength,
// format 2
kGlyphFormat2_imageData = kSmallGlyphMetricsLength,
// format 3
// format 4
// format 5
kGlyphFormat5_imageData = 0,
// format 6
kGlyphFormat6_imageData = kBigGlyphMetricsLength,
// format 7
kGlyphFormat7_imageData = kBigGlyphMetricsLength,
// format 8
kGlyphFormat8_numComponents = kSmallGlyphMetricsLength + 1,
kGlyphFormat8_componentArray = kGlyphFormat8_numComponents +
DataSize::kUSHORT,
// format 9
kGlyphFormat9_numComponents = kBigGlyphMetricsLength,
kGlyphFormat9_componentArray = kGlyphFormat9_numComponents +
DataSize::kUSHORT,
// ebdtComponent
kEbdtComponentLength = DataSize::kUSHORT + 2 * DataSize::kCHAR,
kEbdtComponent_glyphCode = 0,
kEbdtComponent_xOffset = 2,
kEbdtComponent_yOffset = 3,
};
};
// TODO(stuartg): builder is not functional at all
// - need to add subclasses for each type of bitmap glyph
class Builder : public SubTable::Builder {
public:
virtual ~Builder();
virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
virtual void SubDataSet();
virtual int32_t SubDataSizeToSerialize();
virtual bool SubReadyToSerialize();
virtual int32_t SubSerialize(WritableFontData* new_data);
int32_t format() { return format_; }
static CALLER_ATTACH Builder* CreateGlyphBuilder(ReadableFontData* data,
int32_t format);
protected:
Builder(WritableFontData* data, int32_t format);
Builder(ReadableFontData* data, int32_t format);
private:
int32_t format_;
};
virtual ~BitmapGlyph();
static CALLER_ATTACH BitmapGlyph* CreateGlyph(ReadableFontData* data,
int32_t format);
int32_t format() { return format_; }
// UNIMPLEMENTED: toString()
protected:
BitmapGlyph(ReadableFontData* data, int32_t format);
private:
int32_t format_;
};
typedef Ptr<BitmapGlyph> BitmapGlyphPtr;
typedef Ptr<BitmapGlyph::Builder> BitmapGlyphBuilderPtr;
typedef std::map<int32_t, BitmapGlyphBuilderPtr> BitmapGlyphBuilderMap;
typedef std::vector<BitmapGlyphBuilderMap> BitmapGlyphBuilderList;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_

View File

@ -0,0 +1,68 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/table/bitmap/bitmap_glyph_info.h"
namespace sfntly {
BitmapGlyphInfo::BitmapGlyphInfo(int32_t glyph_id,
int32_t block_offset,
int32_t start_offset,
int32_t length,
int32_t format)
: glyph_id_(glyph_id),
relative_(true),
block_offset_(block_offset),
start_offset_(start_offset),
length_(length),
format_(format) {
}
BitmapGlyphInfo::BitmapGlyphInfo(int32_t glyph_id,
int32_t start_offset,
int32_t length,
int32_t format)
: glyph_id_(glyph_id),
relative_(false),
block_offset_(0),
start_offset_(start_offset),
length_(length),
format_(format) {
}
bool BitmapGlyphInfo::operator==(const BitmapGlyphInfo& rhs) const {
return (format_ == rhs.format_ &&
glyph_id_ == rhs.glyph_id_ &&
length_ == rhs.length_ &&
offset() == rhs.offset());
}
bool BitmapGlyphInfo::operator==(BitmapGlyphInfo* rhs) {
if (rhs == NULL) {
return this == NULL;
}
return (format_ == rhs->format() &&
glyph_id_ == rhs->glyph_id() &&
length_ == rhs->length() &&
offset() == rhs->offset());
}
bool StartOffsetComparator::operator()(BitmapGlyphInfo* lhs,
BitmapGlyphInfo* rhs) {
return lhs->start_offset() > rhs->start_offset();
}
} // namespace sfntly

View File

@ -0,0 +1,85 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_
#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_
#include <vector>
#include <map>
#include "sfntly/table/subtable.h"
namespace sfntly {
// An immutable class holding bitmap glyph information.
class BitmapGlyphInfo : public RefCounted<BitmapGlyphInfo> {
public:
// Constructor for a relative located glyph. The glyph's position in the EBDT
// table is a combination of it's block offset and it's own start offset.
// @param glyphId the glyph id
// @param blockOffset the offset of the block to which the glyph belongs
// @param startOffset the offset of the glyph within the block
// @param length the byte length
// @param format the glyph image format
BitmapGlyphInfo(int32_t glyph_id,
int32_t block_offset,
int32_t start_offset,
int32_t length,
int32_t format);
// Constructor for an absolute located glyph. The glyph's position in the EBDT
// table is only given by it's own start offset.
// @param glyphId the glyph id
// @param startOffset the offset of the glyph within the block
// @param length the byte length
// @param format the glyph image format
BitmapGlyphInfo(int32_t glyph_id,
int32_t start_offset,
int32_t length,
int32_t format);
int32_t glyph_id() const { return glyph_id_; }
bool relative() const { return relative_; }
int32_t block_offset() const { return block_offset_; }
int32_t offset() const { return block_offset() + start_offset(); }
int32_t start_offset() const { return start_offset_; }
int32_t length() const { return length_; }
int32_t format() const { return format_; }
// UNIMPLEMENTED: hashCode()
bool operator==(const BitmapGlyphInfo& rhs) const;
bool operator==(BitmapGlyphInfo* rhs);
private:
int32_t glyph_id_;
bool relative_;
int32_t block_offset_;
int32_t start_offset_;
int32_t length_;
int32_t format_;
};
typedef Ptr<BitmapGlyphInfo> BitmapGlyphInfoPtr;
typedef std::map<int32_t, BitmapGlyphInfoPtr> BitmapGlyphInfoMap;
typedef std::vector<BitmapGlyphInfoMap> BitmapLocaList;
class StartOffsetComparator {
public:
bool operator()(BitmapGlyphInfo* lhs, BitmapGlyphInfo* rhs);
};
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_

View File

@ -0,0 +1,604 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 = 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/table/bitmap/bitmap_size_table.h"
#include <stdio.h>
#include <stdlib.h>
#include "sfntly/math/font_math.h"
#include "sfntly/table/bitmap/eblc_table.h"
#include "sfntly/table/bitmap/index_sub_table_format1.h"
#include "sfntly/table/bitmap/index_sub_table_format2.h"
#include "sfntly/table/bitmap/index_sub_table_format3.h"
#include "sfntly/table/bitmap/index_sub_table_format4.h"
#include "sfntly/table/bitmap/index_sub_table_format5.h"
namespace sfntly {
/******************************************************************************
* BitmapSizeTable class
******************************************************************************/
BitmapSizeTable::~BitmapSizeTable() {
}
int32_t BitmapSizeTable::IndexSubTableArrayOffset() {
return data_->ReadULongAsInt(
EblcTable::Offset::kBitmapSizeTable_indexSubTableArrayOffset);
}
int32_t BitmapSizeTable::IndexTableSize() {
return data_->ReadULongAsInt(
EblcTable::Offset::kBitmapSizeTable_indexTableSize);
}
int32_t BitmapSizeTable::NumberOfIndexSubTables() {
return NumberOfIndexSubTables(data_, 0);
}
int32_t BitmapSizeTable::ColorRef() {
return data_->ReadULongAsInt(EblcTable::Offset::kBitmapSizeTable_colorRef);
}
int32_t BitmapSizeTable::StartGlyphIndex() {
return data_->ReadUShort(EblcTable::Offset::kBitmapSizeTable_startGlyphIndex);
}
int32_t BitmapSizeTable::EndGlyphIndex() {
return data_->ReadUShort(EblcTable::Offset::kBitmapSizeTable_endGlyphIndex);
}
int32_t BitmapSizeTable::PpemX() {
return data_->ReadByte(EblcTable::Offset::kBitmapSizeTable_ppemX);
}
int32_t BitmapSizeTable::PpemY() {
return data_->ReadByte(EblcTable::Offset::kBitmapSizeTable_ppemY);
}
int32_t BitmapSizeTable::BitDepth() {
return data_->ReadByte(EblcTable::Offset::kBitmapSizeTable_bitDepth);
}
int32_t BitmapSizeTable::FlagsAsInt() {
return data_->ReadChar(EblcTable::Offset::kBitmapSizeTable_flags);
}
IndexSubTable* BitmapSizeTable::GetIndexSubTable(int32_t index) {
IndexSubTableList* subtable_list = GetIndexSubTableList();
if (index >= 0 && (size_t)index < subtable_list->size()) {
return (*subtable_list)[index];
}
return NULL;
}
int32_t BitmapSizeTable::GlyphOffset(int32_t glyph_id) {
IndexSubTable* subtable = SearchIndexSubTables(glyph_id);
if (subtable == NULL) {
return -1;
}
return subtable->GlyphOffset(glyph_id);
}
int32_t BitmapSizeTable::GlyphLength(int32_t glyph_id) {
IndexSubTable* subtable = SearchIndexSubTables(glyph_id);
if (subtable == NULL) {
return -1;
}
return subtable->GlyphLength(glyph_id);
}
CALLER_ATTACH BitmapGlyphInfo* BitmapSizeTable::GlyphInfo(int32_t glyph_id) {
IndexSubTable* sub_table = SearchIndexSubTables(glyph_id);
if (sub_table == NULL) {
return NULL;
}
return sub_table->GlyphInfo(glyph_id);
}
int32_t BitmapSizeTable::GlyphFormat(int32_t glyph_id) {
IndexSubTable* subtable = SearchIndexSubTables(glyph_id);
if (subtable == NULL) {
return -1;
}
return subtable->image_format();
}
BitmapSizeTable::BitmapSizeTable(ReadableFontData* data,
ReadableFontData* master_data)
: SubTable(data, master_data) {
}
// static
int32_t BitmapSizeTable::NumberOfIndexSubTables(ReadableFontData* data,
int32_t table_offset) {
return data->ReadULongAsInt(table_offset +
EblcTable::Offset::kBitmapSizeTable_numberOfIndexSubTables);
}
IndexSubTable* BitmapSizeTable::SearchIndexSubTables(int32_t glyph_id) {
// would be faster to binary search but too many size tables don't have
// sorted subtables
#if (SFNTLY_BITMAPSIZE_USE_BINARY_SEARCH)
return BinarySearchIndexSubTables(glyph_id);
#else
return LinearSearchIndexSubTables(glyph_id);
#endif
}
IndexSubTable* BitmapSizeTable::LinearSearchIndexSubTables(int32_t glyph_id) {
IndexSubTableList* subtable_list = GetIndexSubTableList();
for (IndexSubTableList::iterator b = subtable_list->begin(),
e = subtable_list->end(); b != e; b++) {
if ((*b)->first_glyph_index() <= glyph_id &&
(*b)->last_glyph_index() >= glyph_id) {
return *b;
}
}
return NULL;
}
IndexSubTable* BitmapSizeTable::BinarySearchIndexSubTables(int32_t glyph_id) {
IndexSubTableList* subtable_list = GetIndexSubTableList();
int32_t index = 0;
int32_t bottom = 0;
int32_t top = subtable_list->size();
while (top != bottom) {
index = (top + bottom) / 2;
IndexSubTable* subtable = (*subtable_list)[index];
if (glyph_id < subtable->first_glyph_index()) {
// Location beow current location
top = index;
} else {
if (glyph_id <= subtable->last_glyph_index()) {
return subtable;
} else {
bottom = index + 1;
}
}
}
return NULL;
}
CALLER_ATTACH
IndexSubTable* BitmapSizeTable::CreateIndexSubTable(int32_t index) {
return IndexSubTable::CreateIndexSubTable(master_read_data(),
IndexSubTableArrayOffset(),
index);
}
IndexSubTableList* BitmapSizeTable::GetIndexSubTableList() {
AutoLock lock(index_subtables_lock_);
if (index_subtables_.empty()) {
for (int32_t i = 0; i < NumberOfIndexSubTables(); ++i) {
IndexSubTablePtr table;
table.Attach(CreateIndexSubTable(i));
index_subtables_.push_back(table);
}
}
return &index_subtables_;
}
/******************************************************************************
* BitmapSizeTable::Builder class
******************************************************************************/
BitmapSizeTable::Builder::~Builder() {
}
CALLER_ATTACH
FontDataTable* BitmapSizeTable::Builder::SubBuildTable(ReadableFontData* data) {
BitmapSizeTablePtr output = new BitmapSizeTable(data, master_read_data());
return output.Detach();
}
void BitmapSizeTable::Builder::SubDataSet() {
Revert();
}
int32_t BitmapSizeTable::Builder::SubDataSizeToSerialize() {
IndexSubTableBuilderList* builders = IndexSubTableBuilders();
if (builders->empty()) {
return 0;
}
int32_t size = EblcTable::Offset::kBitmapSizeTableLength;
bool variable = false;
for (IndexSubTableBuilderList::iterator b = builders->begin(),
e = builders->end(); b != e; b++) {
size += EblcTable::Offset::kIndexSubTableEntryLength;
int32_t sub_table_size = (*b)->SubDataSizeToSerialize();
int32_t padding = FontMath::PaddingRequired(abs(sub_table_size),
DataSize::kULONG);
#if defined (SFNTLY_DEBUG_BITMAP)
fprintf(stderr, "subtable size=%d\n", sub_table_size);
#endif
variable = (sub_table_size > 0) ? variable : true;
size += abs(sub_table_size) + padding;
}
#if defined (SFNTLY_DEBUG_BITMAP)
fprintf(stderr, "bitmap table size=%d\n", variable ? -size : size);
#endif
return variable ? -size : size;
}
bool BitmapSizeTable::Builder::SubReadyToSerialize() {
if (IndexSubTableBuilders()->empty()) {
return false;
}
return true;
}
int32_t BitmapSizeTable::Builder::SubSerialize(WritableFontData* new_data) {
SetNumberOfIndexSubTables(IndexSubTableBuilders()->size());
int32_t size = InternalReadData()->CopyTo(new_data);
return size;
}
CALLER_ATTACH BitmapSizeTable::Builder*
BitmapSizeTable::Builder::CreateBuilder(WritableFontData* data,
ReadableFontData* master_data) {
BitmapSizeTableBuilderPtr output =
new BitmapSizeTable::Builder(data, master_data);
return output.Detach();
}
CALLER_ATTACH BitmapSizeTable::Builder*
BitmapSizeTable::Builder::CreateBuilder(ReadableFontData* data,
ReadableFontData* master_data) {
BitmapSizeTableBuilderPtr output =
new BitmapSizeTable::Builder(data, master_data);
return output.Detach();
}
int32_t BitmapSizeTable::Builder::IndexSubTableArrayOffset() {
return InternalReadData()->ReadULongAsInt(
EblcTable::Offset::kBitmapSizeTable_indexSubTableArrayOffset);
}
void BitmapSizeTable::Builder::SetIndexSubTableArrayOffset(int32_t offset) {
InternalWriteData()->WriteULong(
EblcTable::Offset::kBitmapSizeTable_indexSubTableArrayOffset, offset);
}
int32_t BitmapSizeTable::Builder::IndexTableSize() {
return InternalReadData()->ReadULongAsInt(
EblcTable::Offset::kBitmapSizeTable_indexTableSize);
}
void BitmapSizeTable::Builder::SetIndexTableSize(int32_t size) {
InternalWriteData()->WriteULong(
EblcTable::Offset::kBitmapSizeTable_indexTableSize, size);
}
int32_t BitmapSizeTable::Builder::NumberOfIndexSubTables() {
return GetIndexSubTableBuilders()->size();
}
int32_t BitmapSizeTable::Builder::ColorRef() {
return InternalReadData()->ReadULongAsInt(
EblcTable::Offset::kBitmapSizeTable_colorRef);
}
int32_t BitmapSizeTable::Builder::StartGlyphIndex() {
return InternalReadData()->ReadUShort(
EblcTable::Offset::kBitmapSizeTable_startGlyphIndex);
}
int32_t BitmapSizeTable::Builder::EndGlyphIndex() {
return InternalReadData()->ReadUShort(
EblcTable::Offset::kBitmapSizeTable_endGlyphIndex);
}
int32_t BitmapSizeTable::Builder::PpemX() {
return InternalReadData()->ReadByte(
EblcTable::Offset::kBitmapSizeTable_ppemX);
}
int32_t BitmapSizeTable::Builder::PpemY() {
return InternalReadData()->ReadByte(
EblcTable::Offset::kBitmapSizeTable_ppemY);
}
int32_t BitmapSizeTable::Builder::BitDepth() {
return InternalReadData()->ReadByte(
EblcTable::Offset::kBitmapSizeTable_bitDepth);
}
int32_t BitmapSizeTable::Builder::FlagsAsInt() {
return InternalReadData()->ReadChar(
EblcTable::Offset::kBitmapSizeTable_flags);
}
IndexSubTable::Builder* BitmapSizeTable::Builder::IndexSubTableBuilder(
int32_t index) {
IndexSubTableBuilderList* sub_table_list = GetIndexSubTableBuilders();
return sub_table_list->at(index);
}
CALLER_ATTACH BitmapGlyphInfo* BitmapSizeTable::Builder::GlyphInfo(
int32_t glyph_id) {
IndexSubTable::Builder* sub_table = SearchIndexSubTables(glyph_id);
if (sub_table == NULL) {
return NULL;
}
return sub_table->GlyphInfo(glyph_id);
}
int32_t BitmapSizeTable::Builder::GlyphOffset(int32_t glyph_id) {
IndexSubTable::Builder* subtable = SearchIndexSubTables(glyph_id);
if (subtable == NULL) {
return -1;
}
return subtable->GlyphOffset(glyph_id);
}
int32_t BitmapSizeTable::Builder::GlyphLength(int32_t glyph_id) {
IndexSubTable::Builder* subtable = SearchIndexSubTables(glyph_id);
if (subtable == NULL) {
return -1;
}
return subtable->GlyphLength(glyph_id);
}
int32_t BitmapSizeTable::Builder::GlyphFormat(int32_t glyph_id) {
IndexSubTable::Builder* subtable = SearchIndexSubTables(glyph_id);
if (subtable == NULL) {
return -1;
}
return subtable->image_format();
}
IndexSubTableBuilderList* BitmapSizeTable::Builder::IndexSubTableBuilders() {
return GetIndexSubTableBuilders();
}
CALLER_ATTACH BitmapSizeTable::Builder::BitmapGlyphInfoIterator*
BitmapSizeTable::Builder::GetIterator() {
Ptr<BitmapSizeTable::Builder::BitmapGlyphInfoIterator> output =
new BitmapSizeTable::Builder::BitmapGlyphInfoIterator(this);
return output.Detach();
}
void BitmapSizeTable::Builder::GenerateLocaMap(BitmapGlyphInfoMap* output) {
assert(output);
Ptr<BitmapSizeTable::Builder::BitmapGlyphInfoIterator> it;
it.Attach(GetIterator());
while (it->HasNext()) {
BitmapGlyphInfoPtr info;
info.Attach(it->Next());
(*output)[info->glyph_id()] = info;
}
}
void BitmapSizeTable::Builder::Revert() {
index_sub_tables_.clear();
set_model_changed(false);
}
BitmapSizeTable::Builder::Builder(WritableFontData* data,
ReadableFontData* master_data)
: SubTable::Builder(data, master_data) {
}
BitmapSizeTable::Builder::Builder(ReadableFontData* data,
ReadableFontData* master_data)
: SubTable::Builder(data, master_data) {
}
void BitmapSizeTable::Builder::SetNumberOfIndexSubTables(int32_t count) {
InternalWriteData()->WriteULong(
EblcTable::Offset::kBitmapSizeTable_numberOfIndexSubTables, count);
}
IndexSubTable::Builder* BitmapSizeTable::Builder::SearchIndexSubTables(
int32_t glyph_id) {
// would be faster to binary search but too many size tables don't have
// sorted subtables
#if (SFNTLY_BITMAPSIZE_USE_BINARY_SEARCH)
return BinarySearchIndexSubTables(glyph_id);
#else
return LinearSearchIndexSubTables(glyph_id);
#endif
}
IndexSubTable::Builder* BitmapSizeTable::Builder::LinearSearchIndexSubTables(
int32_t glyph_id) {
IndexSubTableBuilderList* subtable_list = GetIndexSubTableBuilders();
for (IndexSubTableBuilderList::iterator b = subtable_list->begin(),
e = subtable_list->end();
b != e; b++) {
if ((*b)->first_glyph_index() <= glyph_id &&
(*b)->last_glyph_index() >= glyph_id) {
return *b;
}
}
return NULL;
}
IndexSubTable::Builder* BitmapSizeTable::Builder::BinarySearchIndexSubTables(
int32_t glyph_id) {
IndexSubTableBuilderList* subtable_list = GetIndexSubTableBuilders();
int32_t index = 0;
int32_t bottom = 0;
int32_t top = subtable_list->size();
while (top != bottom) {
index = (top + bottom) / 2;
IndexSubTable::Builder* subtable = subtable_list->at(index);
if (glyph_id < subtable->first_glyph_index()) {
// Location beow current location
top = index;
} else {
if (glyph_id <= subtable->last_glyph_index()) {
return subtable;
} else {
bottom = index + 1;
}
}
}
return NULL;
}
IndexSubTableBuilderList* BitmapSizeTable::Builder::GetIndexSubTableBuilders() {
if (index_sub_tables_.empty()) {
Initialize(InternalReadData());
set_model_changed();
}
return &index_sub_tables_;
}
void BitmapSizeTable::Builder::Initialize(ReadableFontData* data) {
index_sub_tables_.clear();
if (data) {
int32_t number_of_index_subtables =
BitmapSizeTable::NumberOfIndexSubTables(data, 0);
index_sub_tables_.resize(number_of_index_subtables);
for (int32_t i = 0; i < number_of_index_subtables; ++i) {
index_sub_tables_[i].Attach(CreateIndexSubTableBuilder(i));
}
}
}
CALLER_ATTACH IndexSubTable::Builder*
BitmapSizeTable::Builder::CreateIndexSubTableBuilder(int32_t index) {
return IndexSubTable::Builder::CreateBuilder(master_read_data(),
IndexSubTableArrayOffset(),
index);
}
/******************************************************************************
* BitmapSizeTable::Builder::BitmapGlyphInfoIterator class
******************************************************************************/
BitmapSizeTable::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
BitmapSizeTable::Builder* container)
: RefIterator<BitmapGlyphInfo, BitmapSizeTable::Builder>(container) {
sub_table_iter_ = container->IndexSubTableBuilders()->begin();
sub_table_glyph_info_iter_.Attach((*sub_table_iter_)->GetIterator());
}
bool BitmapSizeTable::Builder::BitmapGlyphInfoIterator::HasNext() {
if (sub_table_glyph_info_iter_ && HasNext(sub_table_glyph_info_iter_)) {
return true;
}
while (++sub_table_iter_ != container()->IndexSubTableBuilders()->end()) {
sub_table_glyph_info_iter_.Attach((*sub_table_iter_)->GetIterator());
if (HasNext(sub_table_glyph_info_iter_)) {
return true;
}
}
return false;
}
CALLER_ATTACH
BitmapGlyphInfo* BitmapSizeTable::Builder::BitmapGlyphInfoIterator::Next() {
if (!HasNext()) {
// Note: In C++, we do not throw exception when there's no element.
return NULL;
}
return Next(sub_table_glyph_info_iter_);
}
bool BitmapSizeTable::Builder::BitmapGlyphInfoIterator::HasNext(
BitmapGlyphInfoIter* iterator_base) {
if (iterator_base) {
switch (iterator_base->container_base()->index_format()) {
case 1: {
IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->HasNext();
}
case 2: {
IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->HasNext();
}
case 3: {
IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->HasNext();
}
case 4: {
IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->HasNext();
}
case 5: {
IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->HasNext();
}
default:
break;
}
}
return false;
}
CALLER_ATTACH
BitmapGlyphInfo* BitmapSizeTable::Builder::BitmapGlyphInfoIterator::Next(
BitmapGlyphInfoIter* iterator_base) {
if (iterator_base) {
switch (iterator_base->container_base()->index_format()) {
case 1: {
IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->Next();
}
case 2: {
IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->Next();
}
case 3: {
IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->Next();
}
case 4: {
IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->Next();
}
case 5: {
IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* it =
down_cast<IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator*>(
iterator_base);
return it->Next();
}
default:
break;
}
}
return NULL;
}
} // namespace sfntly

View File

@ -0,0 +1,173 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 = 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_
#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_
#include "sfntly/port/lock.h"
#include "sfntly/table/bitmap/bitmap_glyph_info.h"
#include "sfntly/table/bitmap/index_sub_table.h"
namespace sfntly {
// Binary search would be faster but many fonts have index subtables that
// aren't sorted.
// Note: preprocessor define is used to avoid const expression warnings in C++
// code.
#define SFNTLY_BITMAPSIZE_USE_BINARY_SEARCH 0
class BitmapSizeTable : public SubTable,
public RefCounted<BitmapSizeTable> {
public:
class Builder : public SubTable::Builder,
public RefCounted<Builder> {
public:
class BitmapGlyphInfoIterator :
public RefIterator<BitmapGlyphInfo, Builder> {
public:
explicit BitmapGlyphInfoIterator(Builder* container);
virtual ~BitmapGlyphInfoIterator() {}
virtual bool HasNext();
CALLER_ATTACH virtual BitmapGlyphInfo* Next();
private:
bool HasNext(BitmapGlyphInfoIter* iterator_base);
CALLER_ATTACH BitmapGlyphInfo* Next(BitmapGlyphInfoIter* iterator_base);
IndexSubTableBuilderList::iterator sub_table_iter_;
BitmapGlyphInfoIterPtr sub_table_glyph_info_iter_;
};
virtual ~Builder();
virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
virtual void SubDataSet();
virtual int32_t SubDataSizeToSerialize();
virtual bool SubReadyToSerialize();
virtual int32_t SubSerialize(WritableFontData* new_data);
static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
ReadableFontData* master_data);
static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
ReadableFontData* master_data);
// Gets the subtable array offset as set in the original table as read from
// the font file. This value cannot be explicitly set and will be generated
// during table building.
// @return the subtable array offset
int32_t IndexSubTableArrayOffset();
// Sets the subtable array offset. This is used only during the building
// process when the objects are being serialized.
// @param offset the offset to the index subtable array
void SetIndexSubTableArrayOffset(int32_t offset);
// Gets the subtable array size as set in the original table as read from
// the font file. This value cannot be explicitly set and will be generated
// during table building.
// @return the subtable array size
int32_t IndexTableSize();
// Sets the subtable size. This is used only during the building process
// when the objects are being serialized.
// @param size the offset to the index subtable array
void SetIndexTableSize(int32_t size);
int32_t NumberOfIndexSubTables();
int32_t ColorRef();
// TODO(stuartg): SBitLineMetrics hori();
// TODO(stuartg): SBitLineMetrics vert();
int32_t StartGlyphIndex();
int32_t EndGlyphIndex();
int32_t PpemX();
int32_t PpemY();
int32_t BitDepth();
int32_t FlagsAsInt();
IndexSubTable::Builder* IndexSubTableBuilder(int32_t index);
CALLER_ATTACH BitmapGlyphInfo* GlyphInfo(int32_t glyph_id);
int32_t GlyphOffset(int32_t glyph_id);
int32_t GlyphLength(int32_t glyph_id);
int32_t GlyphFormat(int32_t glyph_id);
IndexSubTableBuilderList* IndexSubTableBuilders();
// Note: renamed from iterator(), type is the derived type.
CALLER_ATTACH BitmapGlyphInfoIterator* GetIterator();
void GenerateLocaMap(BitmapGlyphInfoMap* output);
protected:
void Revert();
private:
Builder(WritableFontData* data, ReadableFontData* master_data);
Builder(ReadableFontData* data, ReadableFontData* master_data);
void SetNumberOfIndexSubTables(int32_t count);
IndexSubTable::Builder* SearchIndexSubTables(int32_t glyph_id);
IndexSubTable::Builder* LinearSearchIndexSubTables(int32_t glyph_id);
IndexSubTable::Builder* BinarySearchIndexSubTables(int32_t glyph_id);
IndexSubTableBuilderList* GetIndexSubTableBuilders();
void Initialize(ReadableFontData* data);
CALLER_ATTACH IndexSubTable::Builder* CreateIndexSubTableBuilder(
int32_t index);
IndexSubTableBuilderList index_sub_tables_;
};
virtual ~BitmapSizeTable();
int32_t IndexSubTableArrayOffset();
int32_t IndexTableSize();
int32_t NumberOfIndexSubTables();
int32_t ColorRef();
// TODO(stuartg): SBitLineMetrics hori();
// TODO(stuartg): SBitLineMetrics vert();
int32_t StartGlyphIndex();
int32_t EndGlyphIndex();
int32_t PpemX();
int32_t PpemY();
int32_t BitDepth();
int32_t FlagsAsInt();
// Note: renamed from indexSubTable()
IndexSubTable* GetIndexSubTable(int32_t index);
int32_t GlyphOffset(int32_t glyph_id);
int32_t GlyphLength(int32_t glyph_id);
CALLER_ATTACH BitmapGlyphInfo* GlyphInfo(int32_t glyph_id);
int32_t GlyphFormat(int32_t glyph_id);
protected:
BitmapSizeTable(ReadableFontData* data,
ReadableFontData* master_data);
private:
static int32_t NumberOfIndexSubTables(ReadableFontData* data,
int32_t table_offset);
IndexSubTable* SearchIndexSubTables(int32_t glyph_id);
IndexSubTable* LinearSearchIndexSubTables(int32_t glyph_id);
IndexSubTable* BinarySearchIndexSubTables(int32_t glyph_id);
CALLER_ATTACH IndexSubTable* CreateIndexSubTable(int32_t index);
IndexSubTableList* GetIndexSubTableList();
Lock index_subtables_lock_;
IndexSubTableList index_subtables_;
};
typedef Ptr<BitmapSizeTable> BitmapSizeTablePtr;
typedef std::vector<BitmapSizeTablePtr> BitmapSizeTableList;
typedef Ptr<BitmapSizeTable::Builder> BitmapSizeTableBuilderPtr;
typedef std::vector<BitmapSizeTableBuilderPtr> BitmapSizeTableBuilderList;
} // namespace sfntly
#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_

View File

@ -0,0 +1,109 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 = 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/table/bitmap/composite_bitmap_glyph.h"
namespace sfntly {
/******************************************************************************
* CompositeBitmapGlyph class
******************************************************************************/
CompositeBitmapGlyph::CompositeBitmapGlyph(ReadableFontData* data,
int32_t format)
: BitmapGlyph(data, format) {
Initialize(format);
}
CompositeBitmapGlyph::~CompositeBitmapGlyph() {
}
int32_t CompositeBitmapGlyph::NumComponents() {
return data_->ReadUShort(num_components_offset_);
}
CompositeBitmapGlyph::Component CompositeBitmapGlyph::GetComponent(
int32_t component_num) const {
int32_t component_offset = component_array_offset_ +
component_num * Offset::kEbdtComponentLength;
return CompositeBitmapGlyph::Component(
data_->ReadUShort(component_offset + Offset::kEbdtComponent_glyphCode),
data_->ReadChar(component_offset + Offset::kEbdtComponent_xOffset),
data_->ReadChar(component_offset + Offset::kEbdtComponent_yOffset));
}
void CompositeBitmapGlyph::Initialize(int32_t format) {
if (format == 8) {
num_components_offset_ = Offset::kGlyphFormat8_numComponents;
component_array_offset_ = Offset::kGlyphFormat8_componentArray;
} else if (format == 9) {
num_components_offset_ = Offset::kGlyphFormat9_numComponents;
component_array_offset_ = Offset::kGlyphFormat9_componentArray;
} else {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IllegalStateException("Attempt to create a Composite Bitmap Glyph "
"with a non-composite format.");
#endif
}
}
/******************************************************************************
* CompositeBitmapGlyph::Component class
******************************************************************************/
CompositeBitmapGlyph::Component::Component(const Component& rhs)
: glyph_code_(rhs.glyph_code_),
x_offset_(rhs.x_offset_),
y_offset_(rhs.y_offset_) {
}
bool CompositeBitmapGlyph::Component::operator==(
const CompositeBitmapGlyph::Component& rhs) {
return glyph_code_ == rhs.glyph_code_;
}
CompositeBitmapGlyph::Component& CompositeBitmapGlyph::Component::operator=(
const CompositeBitmapGlyph::Component& rhs) {
glyph_code_ = rhs.glyph_code_;
x_offset_ = rhs.x_offset_;
y_offset_ = rhs.y_offset_;
return *this;
}
CompositeBitmapGlyph::Component::Component(int32_t glyph_code,
int32_t x_offset,
int32_t y_offset)
: glyph_code_(glyph_code), x_offset_(x_offset), y_offset_(y_offset) {
}
/******************************************************************************
* CompositeBitmapGlyph::Builder class
******************************************************************************/
CompositeBitmapGlyph::Builder::Builder(ReadableFontData* data, int32_t format)
: BitmapGlyph::Builder(data, format) {
}
CompositeBitmapGlyph::Builder::Builder(WritableFontData* data, int32_t format)
: BitmapGlyph::Builder(data, format) {
}
CompositeBitmapGlyph::Builder::~Builder() {
}
CALLER_ATTACH FontDataTable*
CompositeBitmapGlyph::Builder::SubBuildTable(ReadableFontData* data) {
Ptr<CompositeBitmapGlyph> glyph = new CompositeBitmapGlyph(data, format());
return glyph.Detach();
}
} // namespace sfntly

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