mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
sync with Kovid's branch
This commit is contained in:
commit
4f1d847a75
12
COPYRIGHT
12
COPYRIGHT
@ -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
|
||||
|
@ -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|?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
26
recipes/delco_times.recipe
Normal file
26
recipes/delco_times.recipe
Normal 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'),
|
||||
]
|
||||
|
@ -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=')
|
@ -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()
|
||||
|
BIN
resources/images/rotate-right.png
Normal file
BIN
resources/images/rotate-right.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
BIN
resources/images/view-image.png
Normal file
BIN
resources/images/view-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -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
|
||||
|
@ -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')):
|
||||
|
@ -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:
|
||||
|
@ -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>
|
||||
|
@ -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)):
|
||||
|
@ -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
93
setup/sfntly.py
Normal 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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'])
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
'''
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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_()
|
||||
|
@ -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
|
||||
|
@ -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', []):
|
||||
|
@ -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)
|
||||
|
@ -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_()
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
119
src/calibre/utils/fonts/metadata.py
Normal file
119
src/calibre/utils/fonts/metadata.py
Normal 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())
|
315
src/calibre/utils/fonts/scanner.py
Normal file
315
src/calibre/utils/fonts/scanner.py
Normal 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()
|
||||
|
||||
|
628
src/calibre/utils/fonts/sfntly.cpp
Normal file
628
src/calibre/utils/fonts/sfntly.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
196
src/calibre/utils/fonts/sfntly.h
Normal file
196
src/calibre/utils/fonts/sfntly.h
Normal 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_;
|
||||
};
|
207
src/calibre/utils/fonts/subset.py
Normal file
207
src/calibre/utils/fonts/subset.py
Normal 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)
|
||||
|
||||
|
@ -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
203
src/sfntly/COPYING.txt
Normal 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.
|
||||
|
199
src/sfntly/src/sfntly/data/byte_array.cc
Normal file
199
src/sfntly/src/sfntly/data/byte_array.cc
Normal 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
|
201
src/sfntly/src/sfntly/data/byte_array.h
Normal file
201
src/sfntly/src/sfntly/data/byte_array.h
Normal 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_
|
82
src/sfntly/src/sfntly/data/font_data.cc
Normal file
82
src/sfntly/src/sfntly/data/font_data.cc
Normal 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
|
135
src/sfntly/src/sfntly/data/font_data.h
Normal file
135
src/sfntly/src/sfntly/data/font_data.h
Normal 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_
|
141
src/sfntly/src/sfntly/data/font_input_stream.cc
Normal file
141
src/sfntly/src/sfntly/data/font_input_stream.cc
Normal 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
|
97
src/sfntly/src/sfntly/data/font_input_stream.h
Normal file
97
src/sfntly/src/sfntly/data/font_input_stream.h
Normal 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_
|
130
src/sfntly/src/sfntly/data/font_output_stream.cc
Normal file
130
src/sfntly/src/sfntly/data/font_output_stream.cc
Normal 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
|
79
src/sfntly/src/sfntly/data/font_output_stream.h
Normal file
79
src/sfntly/src/sfntly/data/font_output_stream.h
Normal 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_
|
82
src/sfntly/src/sfntly/data/growable_memory_byte_array.cc
Normal file
82
src/sfntly/src/sfntly/data/growable_memory_byte_array.cc
Normal 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
|
66
src/sfntly/src/sfntly/data/growable_memory_byte_array.h
Normal file
66
src/sfntly/src/sfntly/data/growable_memory_byte_array.h
Normal 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_
|
93
src/sfntly/src/sfntly/data/memory_byte_array.cc
Normal file
93
src/sfntly/src/sfntly/data/memory_byte_array.cc
Normal 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
|
81
src/sfntly/src/sfntly/data/memory_byte_array.h
Normal file
81
src/sfntly/src/sfntly/data/memory_byte_array.h
Normal 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_
|
336
src/sfntly/src/sfntly/data/readable_font_data.cc
Normal file
336
src/sfntly/src/sfntly/data/readable_font_data.cc
Normal 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
|
308
src/sfntly/src/sfntly/data/readable_font_data.h
Normal file
308
src/sfntly/src/sfntly/data/readable_font_data.h
Normal 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_
|
201
src/sfntly/src/sfntly/data/writable_font_data.cc
Normal file
201
src/sfntly/src/sfntly/data/writable_font_data.cc
Normal 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
|
211
src/sfntly/src/sfntly/data/writable_font_data.h
Normal file
211
src/sfntly/src/sfntly/data/writable_font_data.h
Normal 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_
|
568
src/sfntly/src/sfntly/font.cc
Normal file
568
src/sfntly/src/sfntly/font.cc
Normal 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
|
352
src/sfntly/src/sfntly/font.h
Normal file
352
src/sfntly/src/sfntly/font.h
Normal 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_
|
214
src/sfntly/src/sfntly/font_factory.cc
Normal file
214
src/sfntly/src/sfntly/font_factory.cc
Normal 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
|
140
src/sfntly/src/sfntly/font_factory.h
Normal file
140
src/sfntly/src/sfntly/font_factory.h
Normal 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_
|
41
src/sfntly/src/sfntly/math/fixed1616.h
Normal file
41
src/sfntly/src/sfntly/math/fixed1616.h
Normal 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_
|
49
src/sfntly/src/sfntly/math/font_math.h
Normal file
49
src/sfntly/src/sfntly/math/font_math.h
Normal 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_
|
71
src/sfntly/src/sfntly/port/atomic.h
Normal file
71
src/sfntly/src/sfntly/port/atomic.h
Normal 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_
|
28
src/sfntly/src/sfntly/port/config.h
Normal file
28
src/sfntly/src/sfntly/port/config.h
Normal 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_
|
77
src/sfntly/src/sfntly/port/endian.h
Normal file
77
src/sfntly/src/sfntly/port/endian.h
Normal 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_
|
125
src/sfntly/src/sfntly/port/exception_type.h
Normal file
125
src/sfntly/src/sfntly/port/exception_type.h
Normal 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_
|
169
src/sfntly/src/sfntly/port/file_input_stream.cc
Normal file
169
src/sfntly/src/sfntly/port/file_input_stream.cc
Normal 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
|
57
src/sfntly/src/sfntly/port/file_input_stream.h
Normal file
57
src/sfntly/src/sfntly/port/file_input_stream.h
Normal 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_
|
49
src/sfntly/src/sfntly/port/input_stream.h
Normal file
49
src/sfntly/src/sfntly/port/input_stream.h
Normal 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_
|
94
src/sfntly/src/sfntly/port/java_iterator.h
Normal file
94
src/sfntly/src/sfntly/port/java_iterator.h
Normal 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_
|
72
src/sfntly/src/sfntly/port/lock.cc
Normal file
72
src/sfntly/src/sfntly/port/lock.cc
Normal 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
|
76
src/sfntly/src/sfntly/port/lock.h
Normal file
76
src/sfntly/src/sfntly/port/lock.h
Normal 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_
|
147
src/sfntly/src/sfntly/port/memory_input_stream.cc
Executable file
147
src/sfntly/src/sfntly/port/memory_input_stream.cc
Executable 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
|
57
src/sfntly/src/sfntly/port/memory_input_stream.h
Executable file
57
src/sfntly/src/sfntly/port/memory_input_stream.h
Executable 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_
|
72
src/sfntly/src/sfntly/port/memory_output_stream.cc
Normal file
72
src/sfntly/src/sfntly/port/memory_output_stream.cc
Normal 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
|
51
src/sfntly/src/sfntly/port/memory_output_stream.h
Normal file
51
src/sfntly/src/sfntly/port/memory_output_stream.h
Normal 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_
|
46
src/sfntly/src/sfntly/port/output_stream.h
Normal file
46
src/sfntly/src/sfntly/port/output_stream.h
Normal 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_
|
277
src/sfntly/src/sfntly/port/refcount.h
Normal file
277
src/sfntly/src/sfntly/port/refcount.h
Normal 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_
|
102
src/sfntly/src/sfntly/port/type.h
Normal file
102
src/sfntly/src/sfntly/port/type.h
Normal 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_
|
171
src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.cc
Normal file
171
src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.cc
Normal 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
|
96
src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.h
Normal file
96
src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.h
Normal 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_
|
101
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.cc
Normal file
101
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.cc
Normal 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
|
119
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.h
Normal file
119
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.h
Normal 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_
|
68
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.cc
Normal file
68
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.cc
Normal 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
|
85
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.h
Normal file
85
src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.h
Normal 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_
|
604
src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.cc
Normal file
604
src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.cc
Normal 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
|
173
src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.h
Normal file
173
src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.h
Normal 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_
|
109
src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.cc
Normal file
109
src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.cc
Normal 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
Loading…
x
Reference in New Issue
Block a user