From 6deb1383200e4decf25a5f770a2fb30e4b8911eb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 08:13:27 +0530 Subject: [PATCH 01/40] ... --- src/calibre/devices/mtp/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/mtp/driver.py b/src/calibre/devices/mtp/driver.py index 1584a3bf93..bc55654b1e 100644 --- a/src/calibre/devices/mtp/driver.py +++ b/src/calibre/devices/mtp/driver.py @@ -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: From 488b82b6ff10e01ee8c19a5c466eb3e2c10d0c35 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 10:25:58 +0530 Subject: [PATCH 02/40] Add CSS3 Fonts support to the fontconfig plugin --- src/calibre/utils/fonts/fc.py | 79 +++++++++++++++++++++++++--- src/calibre/utils/fonts/fontconfig.c | 77 ++++++++++++++++++--------- 2 files changed, 123 insertions(+), 33 deletions(-) diff --git a/src/calibre/utils/fonts/fc.py b/src/calibre/utils/fonts/fc.py index 73800d1193..3e6ae844bc 100644 --- a/src/calibre/utils/fonts/fc.py +++ b/src/calibre/utils/fonts/fc.py @@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en' import os, sys -from calibre.constants import plugins, islinux, isbsd, isosx +from calibre.constants import plugins, islinux, isbsd, isosx, preferred_encoding _fc, _fc_err = plugins['fontconfig'] @@ -26,6 +26,33 @@ class FontConfig(Thread): Thread.__init__(self) self.daemon = True self.failed = False + self.css_weight_map = { + _fc.FC_WEIGHT_THIN : u'100', + _fc.FC_WEIGHT_EXTRALIGHT : u'200', _fc.FC_WEIGHT_ULTRALIGHT : u'200', + _fc.FC_WEIGHT_LIGHT : u'300', + _fc.FC_WEIGHT_BOOK : u'normal', _fc.FC_WEIGHT_BOOK : u'normal', _fc.FC_WEIGHT_REGULAR : u'normal', + _fc.FC_WEIGHT_MEDIUM : u'500', + _fc.FC_WEIGHT_DEMIBOLD : u'600', _fc.FC_WEIGHT_SEMIBOLD : u'600', + _fc.FC_WEIGHT_BOLD : u'bold', + _fc.FC_WEIGHT_EXTRABOLD : u'800', _fc.FC_WEIGHT_ULTRABOLD : u'800', + _fc.FC_WEIGHT_HEAVY : u'900', _fc.FC_WEIGHT_BLACK : u'900', _fc.FC_WEIGHT_EXTRABLACK : u'900', _fc.FC_WEIGHT_ULTRABLACK : u'900' + } + self.css_slant_map = { + _fc.FC_SLANT_ROMAN : u'normal', + _fc.FC_SLANT_ITALIC : u'italic', + _fc.FC_SLANT_OBLIQUE : u'oblique' + } + self.css_stretch_map = { + _fc.FC_WIDTH_ULTRACONDENSED: u'ultra-condensed', + _fc.FC_WIDTH_EXTRACONDENSED : u'extra-condensed', + _fc.FC_WIDTH_CONDENSED: u'condensed', + _fc.FC_WIDTH_SEMICONDENSED: u'semi-condensed', + _fc.FC_WIDTH_NORMAL: u'normal', + _fc.FC_WIDTH_SEMIEXPANDED: u'semi-expanded', + _fc.FC_WIDTH_EXPANDED: u'expanded', + _fc.FC_WIDTH_EXTRAEXPANDED: u'extra-expanded', + _fc.FC_WIDTH_ULTRAEXPANDED: u'ultra-expanded', + } def run(self): config = None @@ -83,12 +110,13 @@ class FontConfig(Thread): `('normal', 'bold', 'italic', 'bi', 'light', 'li')` ''' self.wait() - if isinstance(family, unicode): - family = family.encode('utf-8') + if not isinstance(family, unicode): + family = family.decode(preferred_encoding) fonts = {} - ofamily = str(family).decode('utf-8') - for fullname, path, style, nfamily, weight, slant in \ - _fc.files_for_family(str(family)): + for entry in _fc.files_for_family(family): + slant, weight = entry['slant'], entry['weight'] + fullname, path = entry['fullname'], entry['path'] + nfamily = entry['family'] style = (slant, weight) if normalize: italic = slant > 0 @@ -104,7 +132,7 @@ class FontConfig(Thread): except UnicodeDecodeError: continue if style in fonts: - if nfamily.lower().strip() == ofamily.lower().strip() \ + if nfamily.lower().strip() == family.lower().strip() \ and 'Condensed' not in fullname and 'ExtraLight' not in fullname: fonts[style] = (path, fullname) else: @@ -112,6 +140,41 @@ class FontConfig(Thread): return fonts + def faces_for_family(self, family): + ''' + Return all the faces in a font family in a manner suitable for CSS 3. + ''' + self.wait() + if not isinstance(family, unicode): + family = family.decode(preferred_encoding) + seen = set() + for entry in _fc.files_for_family(family): + slant, weight = entry['slant'], entry['weight'] + fullname, path = entry['fullname'], entry['path'] + nfamily, width = entry['family'], entry['width'] + fingerprint = (slant, weight, width, nfamily) + if fingerprint in seen: + # Fontconfig returns the same font multiple times if it is + # present in multiple locations + continue + seen.add(fingerprint) + + try: + nfamily = nfamily.decode('UTF-8') + fullname = fullname.decode('utf-8') + path = path.decode('utf-8') + except UnicodeDecodeError: + continue + + face = { + 'font-weight': self.css_weight_map[weight], + 'font-style': self.css_slant_map[slant], + 'font-stretch': self.css_stretch_map[width], + 'font-family': nfamily, + 'fullname': fullname, 'path':path + } + yield face + def match(self, name, all=False, verbose=False): ''' Find the system font that most closely matches `name`, where `name` is a specification @@ -162,7 +225,7 @@ else: def test(): from pprint import pprint; pprint(fontconfig.find_font_families()) - pprint(fontconfig.files_for_family('liberation serif')) + pprint(tuple(fontconfig.faces_for_family('liberation serif'))) m = 'liberation serif' pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True)) diff --git a/src/calibre/utils/fonts/fontconfig.c b/src/calibre/utils/fonts/fontconfig.c index be1cbdce13..b66aba4b08 100644 --- a/src/calibre/utils/fonts/fontconfig.c +++ b/src/calibre/utils/fonts/fontconfig.c @@ -141,10 +141,10 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) { FcPattern *pat, *tp; FcObjectSet *oset; FcFontSet *fs; - FcValue file, weight, fullname, style, slant, family2; - PyObject *ans, *temp, *t; + FcValue file, weight, fullname, style, slant, family2, width; + PyObject *ans, *temp; - if (!PyArg_ParseTuple(args, "s", &family)) + if (!PyArg_ParseTuple(args, "es", "UTF-8", &family)) return NULL; ans = PyList_New(0); @@ -154,6 +154,7 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) { pat = FcPatternBuild(0, FC_FAMILY, FcTypeString, family, (char *) 0); if (pat == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); } + PyMem_Free(family); family = NULL; oset = FcObjectSetCreate(); if (oset == NULL) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); } @@ -161,8 +162,9 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) { 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_WIDTH)) { 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(); } + if (!FcObjectSetAdd(oset, FC_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(); } @@ -174,30 +176,21 @@ fontconfig_files_for_family(PyObject *self, PyObject *args) { 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_WIDTH, 0, &width) != 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; + if (FcPatternGet(tp, FC_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); + temp = Py_BuildValue("{s:s, s:s, s:s, s:s, s:l, s:l, s:l}", + "fullname", (char*)fullname.u.s, + "path", (char*)file.u.s, + "style", (char*)style.u.s, + "family", (char*)family2.u.s, + "weight", (long)weight.u.i, + "slant", (long)slant.u.i, + "width", (long)width.u.i + ); + if (temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return NULL; } if (PyList_Append(ans, temp) != 0) { fontconfig_cleanup_find(pat, oset, fs); return PyErr_NoMemory(); } } @@ -343,5 +336,39 @@ initfontconfig(void) { "Find fonts." ); if (m == NULL) return; + + PyModule_AddIntMacro(m, FC_WEIGHT_THIN); + PyModule_AddIntMacro(m, FC_WEIGHT_EXTRALIGHT); + PyModule_AddIntMacro(m, FC_WEIGHT_ULTRALIGHT); + PyModule_AddIntMacro(m, FC_WEIGHT_LIGHT); + PyModule_AddIntMacro(m, FC_WEIGHT_BOOK); + PyModule_AddIntMacro(m, FC_WEIGHT_REGULAR); + PyModule_AddIntMacro(m, FC_WEIGHT_NORMAL); + PyModule_AddIntMacro(m, FC_WEIGHT_MEDIUM); + PyModule_AddIntMacro(m, FC_WEIGHT_DEMIBOLD); + PyModule_AddIntMacro(m, FC_WEIGHT_SEMIBOLD); + PyModule_AddIntMacro(m, FC_WEIGHT_BOLD); + PyModule_AddIntMacro(m, FC_WEIGHT_EXTRABOLD); + PyModule_AddIntMacro(m, FC_WEIGHT_ULTRABOLD); + PyModule_AddIntMacro(m, FC_WEIGHT_BLACK); + PyModule_AddIntMacro(m, FC_WEIGHT_HEAVY); + PyModule_AddIntMacro(m, FC_WEIGHT_EXTRABLACK); + PyModule_AddIntMacro(m, FC_WEIGHT_ULTRABLACK); + + PyModule_AddIntMacro(m, FC_SLANT_ROMAN); + PyModule_AddIntMacro(m, FC_SLANT_ITALIC); + PyModule_AddIntMacro(m, FC_SLANT_OBLIQUE); + + PyModule_AddIntMacro(m, FC_WIDTH_ULTRACONDENSED); + PyModule_AddIntMacro(m, FC_WIDTH_EXTRACONDENSED); + PyModule_AddIntMacro(m, FC_WIDTH_CONDENSED); + PyModule_AddIntMacro(m, FC_WIDTH_SEMICONDENSED); + PyModule_AddIntMacro(m, FC_WIDTH_NORMAL); + PyModule_AddIntMacro(m, FC_WIDTH_SEMIEXPANDED); + PyModule_AddIntMacro(m, FC_WIDTH_EXPANDED); + PyModule_AddIntMacro(m, FC_WIDTH_EXTRAEXPANDED); + PyModule_AddIntMacro(m, FC_WIDTH_ULTRAEXPANDED); + +# } From 6d355b82b81575d45639d041f598b593ee8daa01 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 16:11:24 +0530 Subject: [PATCH 03/40] Fix a potential crash in the FreeType bindings, if the string object passed in from python is deleted before the face oject. --- src/calibre/utils/fonts/freetype.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/calibre/utils/fonts/freetype.cpp b/src/calibre/utils/fonts/freetype.cpp index e4e30000a0..9086ae0377 100644 --- a/src/calibre/utils/fonts/freetype.cpp +++ b/src/calibre/utils/fonts/freetype.cpp @@ -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; } From 1af17f0c20388dde0f86a89004706dc7cada947f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 17:24:00 +0530 Subject: [PATCH 04/40] Font embedding: Add support for the CSS 3 Fonts module, which means you can embed font families that have more that the usual four faces, with the full set of font-stretch and font-weight variations. Of course, whether the fonts actually show up on a reader will depend on the readers support for CSS 3. --- src/calibre/__init__.py | 23 -- src/calibre/ebooks/__init__.py | 10 +- src/calibre/ebooks/lrf/__init__.py | 10 +- src/calibre/ebooks/oeb/transforms/flatcss.py | 57 ++-- src/calibre/gui2/__init__.py | 46 +-- src/calibre/gui2/convert/mobi_output.py | 32 +- src/calibre/gui2/font_family_chooser.py | 26 +- src/calibre/gui2/widgets.py | 9 +- .../library/catalogs/epub_mobi_builder.py | 10 +- src/calibre/test_build.py | 9 - src/calibre/utils/fonts/__init__.py | 117 ------- src/calibre/utils/fonts/free_type.py | 6 +- src/calibre/utils/fonts/metadata.py | 114 +++++++ src/calibre/utils/fonts/scanner.py | 294 ++++++++++++++++++ src/calibre/utils/fonts/utils.py | 65 ++-- 15 files changed, 529 insertions(+), 299 deletions(-) create mode 100644 src/calibre/utils/fonts/metadata.py create mode 100644 src/calibre/utils/fonts/scanner.py diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 4b0ba5cf68..b64fd08d0a 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -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) diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index 9cf0e51e7e..a5417be220 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -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 diff --git a/src/calibre/ebooks/lrf/__init__.py b/src/calibre/ebooks/lrf/__init__.py index b12c0d6b34..725338ead8 100644 --- a/src/calibre/ebooks/lrf/__init__.py +++ b/src/calibre/ebooks/lrf/__init__.py @@ -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 diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index 6aae85d8d1..f963f468aa 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -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) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 688b134f0e..c1a088bcac 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1,18 +1,19 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' """ 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,7 @@ class Application(QApplication): qt_app = self self._file_open_paths = [] self._file_open_lock = RLock() - load_builtin_fonts() + self.load_builtin_fonts() self.setup_styles(force_calibre_style) if DEBUG: @@ -792,6 +793,28 @@ class Application(QApplication): self.redirect_notify = True return ret + def load_builtin_fonts(self): + global _rating_font + 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): diff --git a/src/calibre/gui2/convert/mobi_output.py b/src/calibre/gui2/convert/mobi_output.py index ac2bf15164..6a5c4120a0 100644 --- a/src/calibre/gui2/convert/mobi_output.py +++ b/src/calibre/gui2/convert/mobi_output.py @@ -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 - ''' + diff --git a/src/calibre/gui2/font_family_chooser.py b/src/calibre/gui2/font_family_chooser.py index 5110e92d24..6c3aa4594a 100644 --- a/src/calibre/gui2/font_family_chooser.py +++ b/src/calibre/gui2/font_family_chooser.py @@ -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()) @@ -122,19 +119,14 @@ 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 try: - self.families = fontconfig.find_font_families() + self.families = 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() @@ -174,20 +166,6 @@ 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) - class FontFamilyChooser(QWidget): family_changed = pyqtSignal(object) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index a990baaa1e..dfbcbdcbf0 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -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') diff --git a/src/calibre/library/catalogs/epub_mobi_builder.py b/src/calibre/library/catalogs/epub_mobi_builder.py index 48bb634fd6..fb7bda13cf 100644 --- a/src/calibre/library/catalogs/epub_mobi_builder.py +++ b/src/calibre/library/catalogs/epub_mobi_builder.py @@ -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 diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index 8ca0a36528..d6b3c9a400 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -37,14 +37,6 @@ 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_winutil(): from calibre.devices.scanner import win_pnp_drives matches = win_pnp_drives.scanner() @@ -123,7 +115,6 @@ def test(): test_plugins() test_lxml() test_freetype() - test_fontconfig() test_sqlite() test_qt() test_imaging() diff --git a/src/calibre/utils/fonts/__init__.py b/src/calibre/utils/fonts/__init__.py index f3fee56033..3af92bd6a0 100644 --- a/src/calibre/utils/fonts/__init__.py +++ b/src/calibre/utils/fonts/__init__.py @@ -6,120 +6,3 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __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() diff --git a/src/calibre/utils/fonts/free_type.py b/src/calibre/utils/fonts/free_type.py index a2e8eca213..6be782dc79 100644 --- a/src/calibre/utils/fonts/free_type.py +++ b/src/calibre/utils/fonts/free_type.py @@ -83,11 +83,11 @@ def test(): raise RuntimeError('Incorrectly claiming that text is supported') def test_find_font(): - from calibre.utils.fonts import fontconfig + from calibre.utils.fonts.scanner import font_scanner abcd = '诶比西迪' - family = fontconfig.find_font_for_text(abcd)[0] + family = font_scanner.find_font_for_text(abcd)[0] print ('Family for Chinese text:', family) - family = fontconfig.find_font_for_text(abcd)[0] + family = font_scanner.find_font_for_text(abcd)[0] abcd = 'لوحة المفاتيح العربية' print ('Family for Arabic text:', family) diff --git a/src/calibre/utils/fonts/metadata.py b/src/calibre/utils/fonts/metadata.py new file mode 100644 index 0000000000..4907678c21 --- /dev/null +++ b/src/calibre/utils/fonts/metadata.py @@ -0,0 +1,114 @@ +#!/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 ' +__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 + + diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py new file mode 100644 index 0000000000..b26525e690 --- /dev/null +++ b/src/calibre/utils/fonts/scanner.py @@ -0,0 +1,294 @@ +#!/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 ' +__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 +from calibre.utils.fonts.metadata import FontMetadata +from calibre.utils.fonts.utils import panose_to_css_generic_family +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 + + 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.free_type import FreeType, get_printable_characters + ft = FreeType() + found = {} + if not isinstance(text, unicode): + raise TypeError(u'%r is not unicode'%text) + text = get_printable_characters(text) + + def filter_faces(font): + try: + ftface = ft.load_font(self.get_font_data(font)) + return ftface.supports_text(text, has_non_printable_chars=False) + 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') + 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 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(): + 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 read_font_metadata(self, path, fileid): + with lopen(path, 'rb') as f: + fm = FontMetadata(f) + 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() + +if __name__ == '__main__': + font_scanner.dump_fonts() + + diff --git a/src/calibre/utils/fonts/utils.py b/src/calibre/utils/fonts/utils.py index 4fcaa20c44..29b39bbefd 100644 --- a/src/calibre/utils/fonts/utils.py +++ b/src/calibre/utils/fonts/utils.py @@ -36,15 +36,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 +69,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 +148,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 +170,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 @@ -249,11 +278,11 @@ def get_font_for_text(text, candidate_font_data=None): except FreeTypeError: ok = True if not ok: - from calibre.utils.fonts import fontconfig - family, faces = fontconfig.find_font_for_text(text) - if family is not None: - f = faces.get('bold', faces['normal']) - candidate_font_data = f[2] + 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(): From 9c62f0a21c51587f7dae3acf50e8f653a5381802 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 17:37:02 +0530 Subject: [PATCH 05/40] Remove the unused fontconfig bindings --- setup/build_environment.py | 13 - setup/extensions.py | 12 +- setup/installer/osx/app/main.py | 27 +- setup/installer/windows/freeze.py | 2 - setup/installer/windows/notes.rst | 21 -- src/calibre/constants.py | 1 - src/calibre/utils/fonts/fc.py | 233 ----------------- src/calibre/utils/fonts/fontconfig.c | 374 --------------------------- 8 files changed, 5 insertions(+), 678 deletions(-) delete mode 100644 src/calibre/utils/fonts/fc.py delete mode 100644 src/calibre/utils/fonts/fontconfig.c diff --git a/setup/build_environment.py b/setup/build_environment.py index 6601578345..492eca0697 100644 --- a/setup/build_environment.py +++ b/setup/build_environment.py @@ -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')): diff --git a/setup/extensions.py b/setup/extensions.py index 989a9ddbe9..cfbb148873 100644 --- a/setup/extensions.py +++ b/setup/extensions.py @@ -13,7 +13,7 @@ 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, @@ -122,13 +122,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'], @@ -305,9 +298,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 diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py index 14df94f4ba..a101c574b7 100644 --- a/setup/installer/osx/app/main.py +++ b/setup/installer/osx/app/main.py @@ -15,8 +15,6 @@ 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', MAGICK_CONFIGURE_PATH=MAGICK_HOME+'/config', MAGICK_CODER_MODULE_PATH=MAGICK_HOME+'/modules-Q16/coders', MAGICK_CODER_FILTER_PATH=MAGICK_HOME+'/modules-Q16/filter', @@ -180,7 +178,7 @@ class Py2App(object): self.add_poppler() self.add_libjpeg() self.add_libpng() - self.add_fontconfig() + self.add_freetype() self.add_imagemagick() self.add_misc_libraries() @@ -397,28 +395,11 @@ class Py2App(object): @flush - def add_fontconfig(self): - info('\nAdding fontconfig') - for x in ('fontconfig.1', 'freetype.6', 'expat.1'): + def add_freetype(self): + info('\nAdding freetype') + for x in ('freetype.6', 'expat.1'): src = os.path.join(SW, 'lib', 'lib'+x+'.dylib') self.install_dylib(src) - dst = os.path.join(self.resources_dir, 'fonts') - if os.path.exists(dst): - shutil.rmtree(dst) - src = os.path.join(SW, 'etc', 'fonts') - shutil.copytree(src, dst, symlinks=False) - fc = os.path.join(dst, 'fonts.conf') - raw = open(fc, 'rb').read() - raw = raw.replace('/usr/share/fonts', '''\ - /Library/Fonts - /Network/Library/Fonts - /System/Library/Fonts - /usr/X11R6/lib/X11/fonts - /usr/share/fonts - /var/root/Library/Fonts - /usr/share/fonts - ''') - open(fc, 'wb').write(raw) @flush def add_imagemagick(self): diff --git a/setup/installer/windows/freeze.py b/setup/installer/windows/freeze.py index 12443b4898..e578d64607 100644 --- a/setup/installer/windows/freeze.py +++ b/setup/installer/windows/freeze.py @@ -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)): diff --git a/setup/installer/windows/notes.rst b/setup/installer/windows/notes.rst index 9502135f6e..fe0d8380e0 100644 --- a/setup/installer/windows/notes.rst +++ b/setup/installer/windows/notes.rst @@ -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 ------------- diff --git a/src/calibre/constants.py b/src/calibre/constants.py index f098b77f9a..3c89db2d1a 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -83,7 +83,6 @@ class Plugins(collections.Mapping): 'magick', 'podofo', 'cPalmdoc', - 'fontconfig', 'progress_indicator', 'chmlib', 'chm_extra', diff --git a/src/calibre/utils/fonts/fc.py b/src/calibre/utils/fonts/fc.py deleted file mode 100644 index 3e6ae844bc..0000000000 --- a/src/calibre/utils/fonts/fc.py +++ /dev/null @@ -1,233 +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 ' -__docformat__ = 'restructuredtext en' - -import os, sys - -from calibre.constants import plugins, islinux, isbsd, isosx, preferred_encoding - -_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 - self.css_weight_map = { - _fc.FC_WEIGHT_THIN : u'100', - _fc.FC_WEIGHT_EXTRALIGHT : u'200', _fc.FC_WEIGHT_ULTRALIGHT : u'200', - _fc.FC_WEIGHT_LIGHT : u'300', - _fc.FC_WEIGHT_BOOK : u'normal', _fc.FC_WEIGHT_BOOK : u'normal', _fc.FC_WEIGHT_REGULAR : u'normal', - _fc.FC_WEIGHT_MEDIUM : u'500', - _fc.FC_WEIGHT_DEMIBOLD : u'600', _fc.FC_WEIGHT_SEMIBOLD : u'600', - _fc.FC_WEIGHT_BOLD : u'bold', - _fc.FC_WEIGHT_EXTRABOLD : u'800', _fc.FC_WEIGHT_ULTRABOLD : u'800', - _fc.FC_WEIGHT_HEAVY : u'900', _fc.FC_WEIGHT_BLACK : u'900', _fc.FC_WEIGHT_EXTRABLACK : u'900', _fc.FC_WEIGHT_ULTRABLACK : u'900' - } - self.css_slant_map = { - _fc.FC_SLANT_ROMAN : u'normal', - _fc.FC_SLANT_ITALIC : u'italic', - _fc.FC_SLANT_OBLIQUE : u'oblique' - } - self.css_stretch_map = { - _fc.FC_WIDTH_ULTRACONDENSED: u'ultra-condensed', - _fc.FC_WIDTH_EXTRACONDENSED : u'extra-condensed', - _fc.FC_WIDTH_CONDENSED: u'condensed', - _fc.FC_WIDTH_SEMICONDENSED: u'semi-condensed', - _fc.FC_WIDTH_NORMAL: u'normal', - _fc.FC_WIDTH_SEMIEXPANDED: u'semi-expanded', - _fc.FC_WIDTH_EXPANDED: u'expanded', - _fc.FC_WIDTH_EXTRAEXPANDED: u'extra-expanded', - _fc.FC_WIDTH_ULTRAEXPANDED: u'ultra-expanded', - } - - 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 not isinstance(family, unicode): - family = family.decode(preferred_encoding) - fonts = {} - for entry in _fc.files_for_family(family): - slant, weight = entry['slant'], entry['weight'] - fullname, path = entry['fullname'], entry['path'] - nfamily = entry['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() == family.lower().strip() \ - and 'Condensed' not in fullname and 'ExtraLight' not in fullname: - fonts[style] = (path, fullname) - else: - fonts[style] = (path, fullname) - - return fonts - - def faces_for_family(self, family): - ''' - Return all the faces in a font family in a manner suitable for CSS 3. - ''' - self.wait() - if not isinstance(family, unicode): - family = family.decode(preferred_encoding) - seen = set() - for entry in _fc.files_for_family(family): - slant, weight = entry['slant'], entry['weight'] - fullname, path = entry['fullname'], entry['path'] - nfamily, width = entry['family'], entry['width'] - fingerprint = (slant, weight, width, nfamily) - if fingerprint in seen: - # Fontconfig returns the same font multiple times if it is - # present in multiple locations - continue - seen.add(fingerprint) - - try: - nfamily = nfamily.decode('UTF-8') - fullname = fullname.decode('utf-8') - path = path.decode('utf-8') - except UnicodeDecodeError: - continue - - face = { - 'font-weight': self.css_weight_map[weight], - 'font-style': self.css_slant_map[slant], - 'font-stretch': self.css_stretch_map[width], - 'font-family': nfamily, - 'fullname': fullname, 'path':path - } - yield face - - 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-::... - - 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(tuple(fontconfig.faces_for_family('liberation serif'))) - m = 'liberation serif' - pprint(fontconfig.match(m+':slant=italic:weight=bold', verbose=True)) - -if __name__ == '__main__': - test() diff --git a/src/calibre/utils/fonts/fontconfig.c b/src/calibre/utils/fonts/fontconfig.c deleted file mode 100644 index b66aba4b08..0000000000 --- a/src/calibre/utils/fonts/fontconfig.c +++ /dev/null @@ -1,374 +0,0 @@ -/* -:mod:`fontconfig` -- Pythonic interface to fontconfig -===================================================== - -.. module:: fontconfig - :platform: All - :synopsis: Pythonic interface to the fontconfig library - -.. moduleauthor:: Kovid Goyal Copyright 2009 - -*/ - -#define PY_SSIZE_T_CLEAN -#include -#include -#include -#include - -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, width; - PyObject *ans, *temp; - - if (!PyArg_ParseTuple(args, "es", "UTF-8", &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(); } - PyMem_Free(family); family = NULL; - - 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_WIDTH)) { 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, FC_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_WIDTH, 0, &width) != FcResultMatch) continue; - if (FcPatternGet(tp, FC_SLANT, 0, &slant) != FcResultMatch) continue; - if (FcPatternGet(tp, FC_FAMILY, 0, &family2) != FcResultMatch) continue; - if (FcPatternGet(tp, FC_FULLNAME, 0, &fullname) != FcResultMatch) continue; - - temp = Py_BuildValue("{s:s, s:s, s:s, s:s, s:l, s:l, s:l}", - "fullname", (char*)fullname.u.s, - "path", (char*)file.u.s, - "style", (char*)style.u.s, - "family", (char*)family2.u.s, - "weight", (long)weight.u.i, - "slant", (long)slant.u.i, - "width", (long)width.u.i - ); - if (temp == NULL) { fontconfig_cleanup_find(pat, oset, fs); return NULL; } - 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; - - PyModule_AddIntMacro(m, FC_WEIGHT_THIN); - PyModule_AddIntMacro(m, FC_WEIGHT_EXTRALIGHT); - PyModule_AddIntMacro(m, FC_WEIGHT_ULTRALIGHT); - PyModule_AddIntMacro(m, FC_WEIGHT_LIGHT); - PyModule_AddIntMacro(m, FC_WEIGHT_BOOK); - PyModule_AddIntMacro(m, FC_WEIGHT_REGULAR); - PyModule_AddIntMacro(m, FC_WEIGHT_NORMAL); - PyModule_AddIntMacro(m, FC_WEIGHT_MEDIUM); - PyModule_AddIntMacro(m, FC_WEIGHT_DEMIBOLD); - PyModule_AddIntMacro(m, FC_WEIGHT_SEMIBOLD); - PyModule_AddIntMacro(m, FC_WEIGHT_BOLD); - PyModule_AddIntMacro(m, FC_WEIGHT_EXTRABOLD); - PyModule_AddIntMacro(m, FC_WEIGHT_ULTRABOLD); - PyModule_AddIntMacro(m, FC_WEIGHT_BLACK); - PyModule_AddIntMacro(m, FC_WEIGHT_HEAVY); - PyModule_AddIntMacro(m, FC_WEIGHT_EXTRABLACK); - PyModule_AddIntMacro(m, FC_WEIGHT_ULTRABLACK); - - PyModule_AddIntMacro(m, FC_SLANT_ROMAN); - PyModule_AddIntMacro(m, FC_SLANT_ITALIC); - PyModule_AddIntMacro(m, FC_SLANT_OBLIQUE); - - PyModule_AddIntMacro(m, FC_WIDTH_ULTRACONDENSED); - PyModule_AddIntMacro(m, FC_WIDTH_EXTRACONDENSED); - PyModule_AddIntMacro(m, FC_WIDTH_CONDENSED); - PyModule_AddIntMacro(m, FC_WIDTH_SEMICONDENSED); - PyModule_AddIntMacro(m, FC_WIDTH_NORMAL); - PyModule_AddIntMacro(m, FC_WIDTH_SEMIEXPANDED); - PyModule_AddIntMacro(m, FC_WIDTH_EXPANDED); - PyModule_AddIntMacro(m, FC_WIDTH_EXTRAEXPANDED); - PyModule_AddIntMacro(m, FC_WIDTH_ULTRAEXPANDED); - -# -} - From 8d34fbcf2abe751e19033d79cdf3044318684734 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 17:45:20 +0530 Subject: [PATCH 06/40] ... --- src/calibre/utils/fonts/scanner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py index b26525e690..a4dbd60a5f 100644 --- a/src/calibre/utils/fonts/scanner.py +++ b/src/calibre/utils/fonts/scanner.py @@ -25,7 +25,7 @@ def font_dirs(): winutil, err = plugins['winutil'] if err: raise RuntimeError('Failed to load winutil: %s'%err) - return winutil.special_folder_path(winutil.CSIDL_FONTS) + return [winutil.special_folder_path(winutil.CSIDL_FONTS)] if isosx: return [ '/Library/Fonts', From c4b6ab96b202ce8938037dd4b1c604ab21889e7b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 17:46:47 +0530 Subject: [PATCH 07/40] ... --- src/calibre/gui2/font_family_chooser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/font_family_chooser.py b/src/calibre/gui2/font_family_chooser.py index 6c3aa4594a..065ca9f472 100644 --- a/src/calibre/gui2/font_family_chooser.py +++ b/src/calibre/gui2/font_family_chooser.py @@ -121,7 +121,7 @@ class FontFamilyDialog(QDialog): self.setWindowIcon(QIcon(I('font.png'))) from calibre.utils.fonts.scanner import font_scanner try: - self.families = font_scanner.find_font_families() + self.families = list(font_scanner.find_font_families()) except: self.families = [] print ('WARNING: Could not load fonts') From 5b70d13765e62b4f16769161809768b019fe5701 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 18:14:43 +0530 Subject: [PATCH 08/40] ... --- src/calibre/utils/fonts/metadata.py | 7 ++++++- src/calibre/utils/fonts/scanner.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/calibre/utils/fonts/metadata.py b/src/calibre/utils/fonts/metadata.py index 4907678c21..b85a65a9d2 100644 --- a/src/calibre/utils/fonts/metadata.py +++ b/src/calibre/utils/fonts/metadata.py @@ -111,4 +111,9 @@ class FontMetadata(object): 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()) diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py index a4dbd60a5f..c7317ee563 100644 --- a/src/calibre/utils/fonts/scanner.py +++ b/src/calibre/utils/fonts/scanner.py @@ -55,6 +55,7 @@ class Scanner(Thread): self.font_families = () self.allowed_extensions = allowed_extensions + # API {{{ def find_font_families(self): self.join() return self.font_families @@ -143,6 +144,7 @@ class Scanner(Thread): if f in found: return found[f] return None, None + # }}} def reload_cache(self): if not hasattr(self, 'cache'): From 2844f87ba8bf42037cd16510d22b039aab7693b9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 18:19:46 +0530 Subject: [PATCH 09/40] Do not retry unsupported fonts every time the scanner is run --- src/calibre/utils/fonts/scanner.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py index c7317ee563..e90448726d 100644 --- a/src/calibre/utils/fonts/scanner.py +++ b/src/calibre/utils/fonts/scanner.py @@ -13,7 +13,7 @@ from threading import Thread from calibre import walk, prints, as_unicode from calibre.constants import config_dir, iswindows, isosx, plugins, DEBUG -from calibre.utils.fonts.metadata import FontMetadata +from calibre.utils.fonts.metadata import FontMetadata, UnsupportedFont from calibre.utils.fonts.utils import panose_to_css_generic_family from calibre.utils.icu import sort_key @@ -221,6 +221,7 @@ class Scanner(Thread): 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) @@ -266,10 +267,14 @@ class Scanner(Thread): def read_font_metadata(self, path, fileid): with lopen(path, 'rb') as f: - fm = FontMetadata(f) - data = fm.to_dict() - data['path'] = path - self.cached_fonts[fileid] = data + 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() From faea33fc569b8a008204aa74b9bf73811aeb859a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 28 Oct 2012 18:58:13 +0530 Subject: [PATCH 10/40] Font family chooser: Show the faces available for a family when clicking on the family --- src/calibre/gui2/font_family_chooser.py | 70 +++++++++++++++++++++---- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/font_family_chooser.py b/src/calibre/gui2/font_family_chooser.py index 065ca9f472..93682e3966 100644 --- a/src/calibre/gui2/font_family_chooser.py +++ b/src/calibre/gui2/font_family_chooser.py @@ -110,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 = '

'+_('Choose a font family')+'

' + self.setText(self.base_msg) + self.setWordWrap(True) + + def show_family(self, family, faces): + if not family: + self.setText(self.base_msg) + return + msg = ''' +

%s

+
+ {0} +
+ '''%(_('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(''' +
{sf}
+
font-stretch: {width} font-weight: {weight} font-style: + {style}
+ + '''.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): @@ -120,6 +166,7 @@ class FontFamilyDialog(QDialog): self.setWindowTitle(_('Choose font family')) self.setWindowIcon(QIcon(I('font.png'))) from calibre.utils.fonts.scanner import font_scanner + self.font_scanner = font_scanner try: self.families = list(font_scanner.find_font_families()) except: @@ -131,11 +178,9 @@ class FontFamilyDialog(QDialog): 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): @@ -143,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): @@ -166,6 +211,11 @@ class FontFamilyDialog(QDialog): if idx == 0: return None return self.families[idx] + 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): family_changed = pyqtSignal(object) From 2bfc897266f8189a4977f700af73762565fc46fe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Oct 2012 01:52:26 +0530 Subject: [PATCH 11/40] Windows: Workaround for error while changing title/author with calibre library on a network share. Also explicitly close file handles in samefile() instead of relying on garbage collection. --- src/calibre/utils/filenames.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 5c31c92c33..3a327b214c 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -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 not os.path.exists(dest): + 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 not os.path.exists(dest): + raise Exception('This apparently can happen on network shares. Sigh.') return os.link(src, dest) From 7a174b5bc81e7ef8a0155388dc731c8a8524dd85 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Oct 2012 08:58:23 +0530 Subject: [PATCH 12/40] Template editor: Use dummy metadata instead of blank/unknown values --- src/calibre/gui2/dialogs/template_dialog.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index a395ecf6b9..ef95b03fc2 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -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() From 88620ed588de3c6ef7df25bc8a2bd2325f5c152f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Oct 2012 09:01:28 +0530 Subject: [PATCH 13/40] Compare filesizes to verify the hardlinking worked on windows, more robust --- src/calibre/utils/filenames.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 3a327b214c..0960a50c1c 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -307,7 +307,7 @@ class WindowsAtomicFolderMove(object): ' operation was started'%path) try: win32file.CreateHardLink(dest, path) - if not os.path.exists(dest): + if os.path.getsize(dest) != os.path.getsize(path): raise Exception('This apparently can happen on network shares. Sigh.') return except: @@ -348,7 +348,7 @@ def hardlink_file(src, dest): if iswindows: import win32file win32file.CreateHardLink(dest, src) - if not os.path.exists(dest): + if os.path.getsize(dest) != os.path.getsize(src): raise Exception('This apparently can happen on network shares. Sigh.') return os.link(src, dest) From f4485b5e02d1829fa3e0a1b3260a631b57e06826 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Oct 2012 11:13:55 +0530 Subject: [PATCH 14/40] Windows: Do not change title/author if book files are in use, to prevent (a harmless) mismatch between title/author anf on disk path --- src/calibre/gui2/library/models.py | 10 +++- src/calibre/gui2/metadata/basic_widgets.py | 66 ++++++++++++---------- src/calibre/gui2/metadata/single.py | 10 ++-- src/calibre/library/database2.py | 27 ++++++++- 4 files changed, 75 insertions(+), 38 deletions(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 641ac23611..49e5c497fe 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -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() diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 0da9b1bcf4..409088d315 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -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 diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 54067f4c0f..af56e2e657 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -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', []): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index d4cdf8cc1f..5952e11e57 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -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) From 9c9f0e350c93ba0b5d3af11d890da60afd6ab3a8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Oct 2012 19:06:33 +0530 Subject: [PATCH 15/40] Fix Science AAAS --- recipes/science_aas.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/science_aas.recipe b/recipes/science_aas.recipe index f94ab1eb99..2d486e4458 100644 --- a/recipes/science_aas.recipe +++ b/recipes/science_aas.recipe @@ -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() From b859653724be604219b9904ea72e2725ee25573f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Oct 2012 23:25:34 +0530 Subject: [PATCH 16/40] Fix #1072822 (Updated recipe for Financial Times UK edition) --- recipes/financial_times_uk.recipe | 72 ++++++++++++++++--------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/recipes/financial_times_uk.recipe b/recipes/financial_times_uk.recipe index e2b69f4987..6af000d990 100644 --- a/recipes/financial_times_uk.recipe +++ b/recipes/financial_times_uk.recipe @@ -1,5 +1,5 @@ __license__ = 'GPL v3' -__copyright__ = '2010-2011, Darko Miletic ' +__copyright__ = '2010-2012, Darko Miletic ' ''' 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=') \ No newline at end of file From a3e8511e680505c9607e04e09157cc86e2703d05 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 07:54:53 +0530 Subject: [PATCH 17/40] ... --- manual/faq.rst | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/manual/faq.rst b/manual/faq.rst index c8a7b67d27..d7d6367f69 100644 --- a/manual/faq.rst +++ b/manual/faq.rst @@ -674,7 +674,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: ``_. |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|? ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 1156e2f0d3197f66ee93d1db399aebbabce797cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 11:04:06 +0530 Subject: [PATCH 18/40] Import sfntly into the calibre source tree --- src/sfntly/COPYING.txt | 203 +++ src/sfntly/src/sfntly/data/byte_array.cc | 199 +++ src/sfntly/src/sfntly/data/byte_array.h | 201 +++ src/sfntly/src/sfntly/data/font_data.cc | 82 ++ src/sfntly/src/sfntly/data/font_data.h | 135 ++ .../src/sfntly/data/font_input_stream.cc | 141 ++ .../src/sfntly/data/font_input_stream.h | 97 ++ .../src/sfntly/data/font_output_stream.cc | 130 ++ .../src/sfntly/data/font_output_stream.h | 79 + .../sfntly/data/growable_memory_byte_array.cc | 82 ++ .../sfntly/data/growable_memory_byte_array.h | 66 + .../src/sfntly/data/memory_byte_array.cc | 93 ++ .../src/sfntly/data/memory_byte_array.h | 81 ++ .../src/sfntly/data/readable_font_data.cc | 336 +++++ .../src/sfntly/data/readable_font_data.h | 308 ++++ .../src/sfntly/data/writable_font_data.cc | 201 +++ .../src/sfntly/data/writable_font_data.h | 211 +++ src/sfntly/src/sfntly/font.cc | 557 +++++++ src/sfntly/src/sfntly/font.h | 352 +++++ src/sfntly/src/sfntly/font_factory.cc | 214 +++ src/sfntly/src/sfntly/font_factory.h | 140 ++ src/sfntly/src/sfntly/math/fixed1616.h | 41 + src/sfntly/src/sfntly/math/font_math.h | 49 + src/sfntly/src/sfntly/port/atomic.h | 71 + src/sfntly/src/sfntly/port/config.h | 28 + src/sfntly/src/sfntly/port/endian.h | 77 + src/sfntly/src/sfntly/port/exception_type.h | 125 ++ .../src/sfntly/port/file_input_stream.cc | 169 +++ .../src/sfntly/port/file_input_stream.h | 57 + src/sfntly/src/sfntly/port/input_stream.h | 49 + src/sfntly/src/sfntly/port/java_iterator.h | 94 ++ src/sfntly/src/sfntly/port/lock.cc | 72 + src/sfntly/src/sfntly/port/lock.h | 76 + .../src/sfntly/port/memory_input_stream.cc | 147 ++ .../src/sfntly/port/memory_input_stream.h | 57 + .../src/sfntly/port/memory_output_stream.cc | 72 + .../src/sfntly/port/memory_output_stream.h | 51 + src/sfntly/src/sfntly/port/output_stream.h | 46 + src/sfntly/src/sfntly/port/refcount.h | 277 ++++ src/sfntly/src/sfntly/port/type.h | 102 ++ .../sfntly/table/bitmap/big_glyph_metrics.cc | 171 +++ .../sfntly/table/bitmap/big_glyph_metrics.h | 96 ++ .../src/sfntly/table/bitmap/bitmap_glyph.cc | 101 ++ .../src/sfntly/table/bitmap/bitmap_glyph.h | 119 ++ .../sfntly/table/bitmap/bitmap_glyph_info.cc | 68 + .../sfntly/table/bitmap/bitmap_glyph_info.h | 85 ++ .../sfntly/table/bitmap/bitmap_size_table.cc | 604 ++++++++ .../sfntly/table/bitmap/bitmap_size_table.h | 173 +++ .../table/bitmap/composite_bitmap_glyph.cc | 109 ++ .../table/bitmap/composite_bitmap_glyph.h | 75 + .../src/sfntly/table/bitmap/ebdt_table.cc | 236 +++ .../src/sfntly/table/bitmap/ebdt_table.h | 108 ++ .../src/sfntly/table/bitmap/eblc_table.cc | 313 ++++ .../src/sfntly/table/bitmap/eblc_table.h | 194 +++ .../src/sfntly/table/bitmap/ebsc_table.cc | 107 ++ .../src/sfntly/table/bitmap/ebsc_table.h | 101 ++ .../src/sfntly/table/bitmap/glyph_metrics.cc | 39 + .../src/sfntly/table/bitmap/glyph_metrics.h | 43 + .../sfntly/table/bitmap/index_sub_table.cc | 278 ++++ .../src/sfntly/table/bitmap/index_sub_table.h | 178 +++ .../table/bitmap/index_sub_table_format1.cc | 299 ++++ .../table/bitmap/index_sub_table_format1.h | 116 ++ .../table/bitmap/index_sub_table_format2.cc | 272 ++++ .../table/bitmap/index_sub_table_format2.h | 106 ++ .../table/bitmap/index_sub_table_format3.cc | 295 ++++ .../table/bitmap/index_sub_table_format3.h | 113 ++ .../table/bitmap/index_sub_table_format4.cc | 381 +++++ .../table/bitmap/index_sub_table_format4.h | 135 ++ .../table/bitmap/index_sub_table_format5.cc | 344 +++++ .../table/bitmap/index_sub_table_format5.h | 118 ++ .../table/bitmap/simple_bitmap_glyph.cc | 45 + .../sfntly/table/bitmap/simple_bitmap_glyph.h | 44 + .../table/bitmap/small_glyph_metrics.cc | 126 ++ .../sfntly/table/bitmap/small_glyph_metrics.h | 79 + .../sfntly/table/byte_array_table_builder.cc | 70 + .../sfntly/table/byte_array_table_builder.h | 53 + .../src/sfntly/table/core/cmap_table.cc | 1288 +++++++++++++++++ src/sfntly/src/sfntly/table/core/cmap_table.h | 711 +++++++++ .../sfntly/table/core/font_header_table.cc | 265 ++++ .../src/sfntly/table/core/font_header_table.h | 168 +++ .../core/horizontal_device_metrics_table.cc | 124 ++ .../core/horizontal_device_metrics_table.h | 82 ++ .../table/core/horizontal_header_table.cc | 213 +++ .../table/core/horizontal_header_table.h | 111 ++ .../table/core/horizontal_metrics_table.cc | 138 ++ .../table/core/horizontal_metrics_table.h | 87 ++ .../table/core/maximum_profile_table.cc | 240 +++ .../sfntly/table/core/maximum_profile_table.h | 120 ++ .../src/sfntly/table/core/name_table.cc | 721 +++++++++ src/sfntly/src/sfntly/table/core/name_table.h | 744 ++++++++++ src/sfntly/src/sfntly/table/core/os2_table.cc | 608 ++++++++ src/sfntly/src/sfntly/table/core/os2_table.h | 508 +++++++ .../src/sfntly/table/font_data_table.cc | 193 +++ src/sfntly/src/sfntly/table/font_data_table.h | 123 ++ .../src/sfntly/table/generic_table_builder.cc | 49 + .../src/sfntly/table/generic_table_builder.h | 42 + src/sfntly/src/sfntly/table/header.cc | 66 + src/sfntly/src/sfntly/table/header.h | 114 ++ src/sfntly/src/sfntly/table/subtable.cc | 64 + src/sfntly/src/sfntly/table/subtable.h | 73 + .../sfntly/table/subtable_container_table.h | 48 + src/sfntly/src/sfntly/table/table.cc | 162 +++ src/sfntly/src/sfntly/table/table.h | 119 ++ .../sfntly/table/table_based_table_builder.cc | 69 + .../sfntly/table/table_based_table_builder.h | 48 + .../src/sfntly/table/truetype/glyph_table.cc | 679 +++++++++ .../src/sfntly/table/truetype/glyph_table.h | 335 +++++ .../src/sfntly/table/truetype/loca_table.cc | 246 ++++ .../src/sfntly/table/truetype/loca_table.h | 183 +++ src/sfntly/src/sfntly/tag.cc | 110 ++ src/sfntly/src/sfntly/tag.h | 123 ++ .../tools/subsetter/glyph_table_subsetter.cc | 90 ++ .../tools/subsetter/glyph_table_subsetter.h | 37 + .../src/sfntly/tools/subsetter/subsetter.cc | 102 ++ .../src/sfntly/tools/subsetter/subsetter.h | 72 + .../sfntly/tools/subsetter/table_subsetter.h | 39 + .../tools/subsetter/table_subsetter_impl.cc | 38 + .../tools/subsetter/table_subsetter_impl.h | 37 + 118 files changed, 20751 insertions(+) create mode 100644 src/sfntly/COPYING.txt create mode 100644 src/sfntly/src/sfntly/data/byte_array.cc create mode 100644 src/sfntly/src/sfntly/data/byte_array.h create mode 100644 src/sfntly/src/sfntly/data/font_data.cc create mode 100644 src/sfntly/src/sfntly/data/font_data.h create mode 100644 src/sfntly/src/sfntly/data/font_input_stream.cc create mode 100644 src/sfntly/src/sfntly/data/font_input_stream.h create mode 100644 src/sfntly/src/sfntly/data/font_output_stream.cc create mode 100644 src/sfntly/src/sfntly/data/font_output_stream.h create mode 100644 src/sfntly/src/sfntly/data/growable_memory_byte_array.cc create mode 100644 src/sfntly/src/sfntly/data/growable_memory_byte_array.h create mode 100644 src/sfntly/src/sfntly/data/memory_byte_array.cc create mode 100644 src/sfntly/src/sfntly/data/memory_byte_array.h create mode 100644 src/sfntly/src/sfntly/data/readable_font_data.cc create mode 100644 src/sfntly/src/sfntly/data/readable_font_data.h create mode 100644 src/sfntly/src/sfntly/data/writable_font_data.cc create mode 100644 src/sfntly/src/sfntly/data/writable_font_data.h create mode 100644 src/sfntly/src/sfntly/font.cc create mode 100644 src/sfntly/src/sfntly/font.h create mode 100644 src/sfntly/src/sfntly/font_factory.cc create mode 100644 src/sfntly/src/sfntly/font_factory.h create mode 100644 src/sfntly/src/sfntly/math/fixed1616.h create mode 100644 src/sfntly/src/sfntly/math/font_math.h create mode 100644 src/sfntly/src/sfntly/port/atomic.h create mode 100644 src/sfntly/src/sfntly/port/config.h create mode 100644 src/sfntly/src/sfntly/port/endian.h create mode 100644 src/sfntly/src/sfntly/port/exception_type.h create mode 100644 src/sfntly/src/sfntly/port/file_input_stream.cc create mode 100644 src/sfntly/src/sfntly/port/file_input_stream.h create mode 100644 src/sfntly/src/sfntly/port/input_stream.h create mode 100644 src/sfntly/src/sfntly/port/java_iterator.h create mode 100644 src/sfntly/src/sfntly/port/lock.cc create mode 100644 src/sfntly/src/sfntly/port/lock.h create mode 100755 src/sfntly/src/sfntly/port/memory_input_stream.cc create mode 100755 src/sfntly/src/sfntly/port/memory_input_stream.h create mode 100644 src/sfntly/src/sfntly/port/memory_output_stream.cc create mode 100644 src/sfntly/src/sfntly/port/memory_output_stream.h create mode 100644 src/sfntly/src/sfntly/port/output_stream.h create mode 100644 src/sfntly/src/sfntly/port/refcount.h create mode 100644 src/sfntly/src/sfntly/port/type.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/ebdt_table.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/ebdt_table.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/eblc_table.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/eblc_table.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/ebsc_table.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/ebsc_table.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/glyph_metrics.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/glyph_metrics.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.h create mode 100644 src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.cc create mode 100644 src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.h create mode 100644 src/sfntly/src/sfntly/table/byte_array_table_builder.cc create mode 100644 src/sfntly/src/sfntly/table/byte_array_table_builder.h create mode 100644 src/sfntly/src/sfntly/table/core/cmap_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/cmap_table.h create mode 100644 src/sfntly/src/sfntly/table/core/font_header_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/font_header_table.h create mode 100644 src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.h create mode 100644 src/sfntly/src/sfntly/table/core/horizontal_header_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/horizontal_header_table.h create mode 100644 src/sfntly/src/sfntly/table/core/horizontal_metrics_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/horizontal_metrics_table.h create mode 100644 src/sfntly/src/sfntly/table/core/maximum_profile_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/maximum_profile_table.h create mode 100644 src/sfntly/src/sfntly/table/core/name_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/name_table.h create mode 100644 src/sfntly/src/sfntly/table/core/os2_table.cc create mode 100644 src/sfntly/src/sfntly/table/core/os2_table.h create mode 100644 src/sfntly/src/sfntly/table/font_data_table.cc create mode 100644 src/sfntly/src/sfntly/table/font_data_table.h create mode 100644 src/sfntly/src/sfntly/table/generic_table_builder.cc create mode 100644 src/sfntly/src/sfntly/table/generic_table_builder.h create mode 100644 src/sfntly/src/sfntly/table/header.cc create mode 100644 src/sfntly/src/sfntly/table/header.h create mode 100644 src/sfntly/src/sfntly/table/subtable.cc create mode 100644 src/sfntly/src/sfntly/table/subtable.h create mode 100644 src/sfntly/src/sfntly/table/subtable_container_table.h create mode 100644 src/sfntly/src/sfntly/table/table.cc create mode 100644 src/sfntly/src/sfntly/table/table.h create mode 100644 src/sfntly/src/sfntly/table/table_based_table_builder.cc create mode 100644 src/sfntly/src/sfntly/table/table_based_table_builder.h create mode 100644 src/sfntly/src/sfntly/table/truetype/glyph_table.cc create mode 100644 src/sfntly/src/sfntly/table/truetype/glyph_table.h create mode 100644 src/sfntly/src/sfntly/table/truetype/loca_table.cc create mode 100644 src/sfntly/src/sfntly/table/truetype/loca_table.h create mode 100644 src/sfntly/src/sfntly/tag.cc create mode 100644 src/sfntly/src/sfntly/tag.h create mode 100644 src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.cc create mode 100644 src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.h create mode 100644 src/sfntly/src/sfntly/tools/subsetter/subsetter.cc create mode 100644 src/sfntly/src/sfntly/tools/subsetter/subsetter.h create mode 100644 src/sfntly/src/sfntly/tools/subsetter/table_subsetter.h create mode 100644 src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.cc create mode 100644 src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.h diff --git a/src/sfntly/COPYING.txt b/src/sfntly/COPYING.txt new file mode 100644 index 0000000000..c61423cfed --- /dev/null +++ b/src/sfntly/COPYING.txt @@ -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. + diff --git a/src/sfntly/src/sfntly/data/byte_array.cc b/src/sfntly/src/sfntly/data/byte_array.cc new file mode 100644 index 0000000000..915a40c035 --- /dev/null +++ b/src/sfntly/src/sfntly/data/byte_array.cc @@ -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 + +#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(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(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(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(length, Size() - index); + int32_t bytes_written = InternalPut(index, b, offset, actual_length); + filled_length_ = std::max(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(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(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(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(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(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(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 diff --git a/src/sfntly/src/sfntly/data/byte_array.h b/src/sfntly/src/sfntly/data/byte_array.h new file mode 100644 index 0000000000..70dc92f51a --- /dev/null +++ b/src/sfntly/src/sfntly/data/byte_array.h @@ -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 ByteArrayPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_ diff --git a/src/sfntly/src/sfntly/data/font_data.cc b/src/sfntly/src/sfntly/data/font_data.cc new file mode 100644 index 0000000000..d2b95eac1b --- /dev/null +++ b/src/sfntly/src/sfntly/data/font_data.cc @@ -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 +#include +#include + +#include "sfntly/data/font_data.h" + +namespace sfntly { + +int32_t FontData::Size() const { + return std::min(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(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(length, bound_length_ - offset); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/data/font_data.h b/src/sfntly/src/sfntly/data/font_data.h new file mode 100644 index 0000000000..d02e8b75db --- /dev/null +++ b/src/sfntly/src/sfntly/data/font_data.h @@ -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 + +#include + +#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 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) = 0; + + // 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) = 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 FontDataPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_ diff --git a/src/sfntly/src/sfntly/data/font_input_stream.cc b/src/sfntly/src/sfntly/data/font_input_stream.cc new file mode 100644 index 0000000000..dcf8be35f9 --- /dev/null +++ b/src/sfntly/src/sfntly/data/font_input_stream.cc @@ -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 + +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(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 diff --git a/src/sfntly/src/sfntly/data/font_input_stream.h b/src/sfntly/src/sfntly/data/font_input_stream.h new file mode 100644 index 0000000000..9992b0753f --- /dev/null +++ b/src/sfntly/src/sfntly/data/font_input_stream.h @@ -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_ diff --git a/src/sfntly/src/sfntly/data/font_output_stream.cc b/src/sfntly/src/sfntly/data/font_output_stream.cc new file mode 100644 index 0000000000..3422a22827 --- /dev/null +++ b/src/sfntly/src/sfntly/data/font_output_stream.cc @@ -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 + +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(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 diff --git a/src/sfntly/src/sfntly/data/font_output_stream.h b/src/sfntly/src/sfntly/data/font_output_stream.h new file mode 100644 index 0000000000..fcd48e8aaa --- /dev/null +++ b/src/sfntly/src/sfntly/data/font_output_stream.h @@ -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_ diff --git a/src/sfntly/src/sfntly/data/growable_memory_byte_array.cc b/src/sfntly/src/sfntly/data/growable_memory_byte_array.cc new file mode 100644 index 0000000000..c335614d4d --- /dev/null +++ b/src/sfntly/src/sfntly/data/growable_memory_byte_array.cc @@ -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 +#include + +#include + +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 diff --git a/src/sfntly/src/sfntly/data/growable_memory_byte_array.h b/src/sfntly/src/sfntly/data/growable_memory_byte_array.h new file mode 100644 index 0000000000..8583a0d645 --- /dev/null +++ b/src/sfntly/src/sfntly/data/growable_memory_byte_array.h @@ -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 { + 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_ diff --git a/src/sfntly/src/sfntly/data/memory_byte_array.cc b/src/sfntly/src/sfntly/data/memory_byte_array.cc new file mode 100644 index 0000000000..d6c9c4828d --- /dev/null +++ b/src/sfntly/src/sfntly/data/memory_byte_array.cc @@ -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 + +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 diff --git a/src/sfntly/src/sfntly/data/memory_byte_array.h b/src/sfntly/src/sfntly/data/memory_byte_array.h new file mode 100644 index 0000000000..838fd1aca5 --- /dev/null +++ b/src/sfntly/src/sfntly/data/memory_byte_array.h @@ -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 { + 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_ diff --git a/src/sfntly/src/sfntly/data/readable_font_data.cc b/src/sfntly/src/sfntly/data/readable_font_data.cc new file mode 100644 index 0000000000..06d783f2e3 --- /dev/null +++ b/src/sfntly/src/sfntly/data/readable_font_data.cc @@ -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 + +#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(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 diff --git a/src/sfntly/src/sfntly/data/readable_font_data.h b/src/sfntly/src/sfntly/data/readable_font_data.h new file mode 100644 index 0000000000..b43c626041 --- /dev/null +++ b/src/sfntly/src/sfntly/data/readable_font_data.h @@ -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 { + 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 ReadableFontDataPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_ diff --git a/src/sfntly/src/sfntly/data/writable_font_data.cc b/src/sfntly/src/sfntly/data/writable_font_data.cc new file mode 100644 index 0000000000..7f6f72f533 --- /dev/null +++ b/src/sfntly/src/sfntly/data/writable_font_data.cc @@ -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(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 diff --git a/src/sfntly/src/sfntly/data/writable_font_data.h b/src/sfntly/src/sfntly/data/writable_font_data.h new file mode 100644 index 0000000000..d2a049eb55 --- /dev/null +++ b/src/sfntly/src/sfntly/data/writable_font_data.h @@ -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 WritableFontDataPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_ diff --git a/src/sfntly/src/sfntly/font.cc b/src/sfntly/src/sfntly/font.cc new file mode 100644 index 0000000000..347e0c13e9 --- /dev/null +++ b/src/sfntly/src/sfntly/font.cc @@ -0,0 +1,557 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#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); +} + +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(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 Int2Bool; + typedef std::pair 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(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(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(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(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(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(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(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(sliced_data.p_); + table_data->insert(DataBlockEntry(*table_header, data)); + } +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/font.h b/src/sfntly/src/sfntly/font.h new file mode 100644 index 0000000000..975e8cc52c --- /dev/null +++ b/src/sfntly/src/sfntly/font.h @@ -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 + +#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 { + 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 { + 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() { return 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() { return &digest_; } + + // Get the checksum for this font. + int64_t checksum() { return checksum_; } + + // Get the number of tables in this font. + int32_t num_tables() { return (int32_t)tables_.size(); } + + // Whether the font has a particular table. + bool HasTable(int32_t tag); + + // UNIMPLEMENTED: public Iterator 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 FontPtr; +typedef std::vector FontArray; +typedef Ptr FontBuilderPtr; +typedef std::vector FontBuilderArray; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_FONT_H_ diff --git a/src/sfntly/src/sfntly/font_factory.cc b/src/sfntly/src/sfntly/font_factory.cc new file mode 100644 index 0000000000..c162a77af3 --- /dev/null +++ b/src/sfntly/src/sfntly/font_factory.cc @@ -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 + +#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(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(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 diff --git a/src/sfntly/src/sfntly/font_factory.h b/src/sfntly/src/sfntly/font_factory.h new file mode 100644 index 0000000000..63deff4abf --- /dev/null +++ b/src/sfntly/src/sfntly/font_factory.h @@ -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 + +#include "sfntly/port/refcount.h" +#include "sfntly/port/type.h" +#include "sfntly/font.h" + +namespace sfntly { + +class FontFactory : public RefCounted { + 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 FontFactoryPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_ diff --git a/src/sfntly/src/sfntly/math/fixed1616.h b/src/sfntly/src/sfntly/math/fixed1616.h new file mode 100644 index 0000000000..4abbe18098 --- /dev/null +++ b/src/sfntly/src/sfntly/math/fixed1616.h @@ -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_ diff --git a/src/sfntly/src/sfntly/math/font_math.h b/src/sfntly/src/sfntly/math/font_math.h new file mode 100644 index 0000000000..f1cd2d2a6f --- /dev/null +++ b/src/sfntly/src/sfntly/math/font_math.h @@ -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_ diff --git a/src/sfntly/src/sfntly/port/atomic.h b/src/sfntly/src/sfntly/port/atomic.h new file mode 100644 index 0000000000..b381a52af7 --- /dev/null +++ b/src/sfntly/src/sfntly/port/atomic.h @@ -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 + +static inline size_t AtomicIncrement(size_t* address) { +#if defined (_WIN64) + return InterlockedIncrement64(reinterpret_cast(address)); +#else + return InterlockedIncrement(reinterpret_cast(address)); +#endif +} + +static inline size_t AtomicDecrement(size_t* address) { +#if defined (_WIN64) + return InterlockedDecrement64(reinterpret_cast(address)); +#else + return InterlockedDecrement(reinterpret_cast(address)); +#endif +} + +#elif defined (__APPLE__) + +#include + +static inline size_t AtomicIncrement(size_t* address) { + return OSAtomicIncrement32Barrier(reinterpret_cast(address)); +} + +static inline size_t AtomicDecrement(size_t* address) { + return OSAtomicDecrement32Barrier(reinterpret_cast(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 + +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_ diff --git a/src/sfntly/src/sfntly/port/config.h b/src/sfntly/src/sfntly/port/config.h new file mode 100644 index 0000000000..0fcdffe724 --- /dev/null +++ b/src/sfntly/src/sfntly/port/config.h @@ -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_ diff --git a/src/sfntly/src/sfntly/port/endian.h b/src/sfntly/src/sfntly/port/endian.h new file mode 100644 index 0000000000..db58f0a307 --- /dev/null +++ b/src/sfntly/src/sfntly/port/endian.h @@ -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_ diff --git a/src/sfntly/src/sfntly/port/exception_type.h b/src/sfntly/src/sfntly/port/exception_type.h new file mode 100644 index 0000000000..b96efcb6c5 --- /dev/null +++ b/src/sfntly/src/sfntly/port/exception_type.h @@ -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 +#include +#include + +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_ diff --git a/src/sfntly/src/sfntly/port/file_input_stream.cc b/src/sfntly/src/sfntly/port/file_input_stream.cc new file mode 100644 index 0000000000..5bcb434af0 --- /dev/null +++ b/src/sfntly/src/sfntly/port/file_input_stream.cc @@ -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 +#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(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(0 - (int64_t)position_, n); + position_ -= (size_t)(0 - skip_count); + fseek(file_, position_, SEEK_SET); + } else { + skip_count = std::min(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(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 diff --git a/src/sfntly/src/sfntly/port/file_input_stream.h b/src/sfntly/src/sfntly/port/file_input_stream.h new file mode 100644 index 0000000000..cbca25f7e4 --- /dev/null +++ b/src/sfntly/src/sfntly/port/file_input_stream.h @@ -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 + +#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_ diff --git a/src/sfntly/src/sfntly/port/input_stream.h b/src/sfntly/src/sfntly/port/input_stream.h new file mode 100644 index 0000000000..5d24036ea7 --- /dev/null +++ b/src/sfntly/src/sfntly/port/input_stream.h @@ -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_ diff --git a/src/sfntly/src/sfntly/port/java_iterator.h b/src/sfntly/src/sfntly/port/java_iterator.h new file mode 100644 index 0000000000..0a99bca1d0 --- /dev/null +++ b/src/sfntly/src/sfntly/port/java_iterator.h @@ -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 + +namespace sfntly { + +template +class Iterator : public virtual RefCount { + public: + virtual ~Iterator() {} + virtual ContainerBase* container_base() = 0; + + protected: + Iterator() {} + NO_COPY_AND_ASSIGN(Iterator); +}; + +template +class PODIterator : public Iterator, + public RefCounted< PODIterator > { + public: + explicit PODIterator(Container* container) : container_(container) {} + virtual ~PODIterator() {} + virtual ContainerBase* container_base() { + return static_cast(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 +class RefIterator : public Iterator, + public RefCounted< RefIterator > { + public: + explicit RefIterator(Container* container) : container_(container) {} + virtual ~RefIterator() {} + virtual ContainerBase* container_base() { + return static_cast(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_ diff --git a/src/sfntly/src/sfntly/port/lock.cc b/src/sfntly/src/sfntly/port/lock.cc new file mode 100644 index 0000000000..6c0c309a94 --- /dev/null +++ b/src/sfntly/src/sfntly/port/lock.cc @@ -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 diff --git a/src/sfntly/src/sfntly/port/lock.h b/src/sfntly/src/sfntly/port/lock.h new file mode 100644 index 0000000000..b2e29bf64f --- /dev/null +++ b/src/sfntly/src/sfntly/port/lock.h @@ -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 +#else // Assume pthread. +#include +#include +#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_ diff --git a/src/sfntly/src/sfntly/port/memory_input_stream.cc b/src/sfntly/src/sfntly/port/memory_input_stream.cc new file mode 100755 index 0000000000..56ee81e5dd --- /dev/null +++ b/src/sfntly/src/sfntly/port/memory_input_stream.cc @@ -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 +#endif + +#include + +#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(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(0 - (int64_t)position_, n); + position_ -= (size_t)(0 - skip_count); + } else { + skip_count = std::min(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(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 diff --git a/src/sfntly/src/sfntly/port/memory_input_stream.h b/src/sfntly/src/sfntly/port/memory_input_stream.h new file mode 100755 index 0000000000..bc861c3f13 --- /dev/null +++ b/src/sfntly/src/sfntly/port/memory_input_stream.h @@ -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 + +#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_ diff --git a/src/sfntly/src/sfntly/port/memory_output_stream.cc b/src/sfntly/src/sfntly/port/memory_output_stream.cc new file mode 100644 index 0000000000..f2ff2e302e --- /dev/null +++ b/src/sfntly/src/sfntly/port/memory_output_stream.cc @@ -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 diff --git a/src/sfntly/src/sfntly/port/memory_output_stream.h b/src/sfntly/src/sfntly/port/memory_output_stream.h new file mode 100644 index 0000000000..d1eda7faf8 --- /dev/null +++ b/src/sfntly/src/sfntly/port/memory_output_stream.h @@ -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 +#include + +#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 store_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_ diff --git a/src/sfntly/src/sfntly/port/output_stream.h b/src/sfntly/src/sfntly/port/output_stream.h new file mode 100644 index 0000000000..64a602471d --- /dev/null +++ b/src/sfntly/src/sfntly/port/output_stream.h @@ -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_ diff --git a/src/sfntly/src/sfntly/port/refcount.h b/src/sfntly/src/sfntly/port/refcount.h new file mode 100644 index 0000000000..eed51622d7 --- /dev/null +++ b/src/sfntly/src/sfntly/port/refcount.h @@ -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 { +// public: +// static Foo* CreateInstance() { +// Ptr obj = new Foo(); // ref count = 1 +// return obj.Detach(); +// } +// }; +// typedef Ptr 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; +// class B : public A, public RefCounted; +// 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 specific implementation +// class B : public I, public RefCounted; // 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 + #include +#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 +class NoAddRefRelease : public T { + public: + NoAddRefRelease(); + ~NoAddRefRelease(); + + private: + virtual size_t AddRef() const = 0; + virtual size_t Release() const = 0; +}; + +template +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&) : ref_count_(0) {} + virtual ~RefCounted() { +#if defined (ENABLE_OBJECT_COUNTER) + AtomicDecrement(&object_counter_); + DEBUG_OUTPUT("D "); +#endif + } + + RefCounted& operator=(const RefCounted&) { + // 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 size_t RefCounted::object_counter_ = 0; +template size_t RefCounted::next_id_ = 0; +#endif + +// semi-smart pointer for RefCount derived objects, similar to CComPtr +template +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& p) : p_(NULL) { + *this = p; + } + + ~Ptr() { + Release(); + } + + T* operator=(T* pT) { + if (p_ == pT) { + return p_; + } + if (pT) { + RefCount* p = static_cast(pT); + if (p == NULL) { + return NULL; + } + p->AddRef(); // always AddRef() before Release() + } + Release(); + p_ = pT; + return p_; + } + + T* operator=(const Ptr& p) { + if (p_ == p.p_) { + return p_; + } + return operator=(p.p_); + } + + operator T*&() { + return p_; + } + + T& operator*() const { + return *p_; // It can throw! + } + + NoAddRefRelease* operator->() const { + return (NoAddRefRelease*)p_; // It can throw! + } + + bool operator!() const { + return (p_ == NULL); + } + + bool operator<(const Ptr& 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(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_ diff --git a/src/sfntly/src/sfntly/port/type.h b/src/sfntly/src/sfntly/port/type.h new file mode 100644 index 0000000000..20a5ba8a89 --- /dev/null +++ b/src/sfntly/src/sfntly/port/type.h @@ -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 + +#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 +#endif + +#include +#include +#include + +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 ByteVector; +typedef std::vector IntegerList; +typedef std::set 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 +inline To implicit_cast(From const &f) { + return f; +} + +template // use like this: down_cast(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(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(f) != NULL); +#endif + return static_cast(f); +} + +#if !defined(WIN32) + #define UNREFERENCED_PARAMETER(p) do { (void)p; } while (0) +#endif + +#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_TYPE_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.cc b/src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.cc new file mode 100644 index 0000000000..d853212bbe --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.cc @@ -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 diff --git a/src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.h b/src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.h new file mode 100644 index 0000000000..a91601c211 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/big_glyph_metrics.h @@ -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 { + 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 { + 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 BigGlyphMetricsPtr; +typedef Ptr BigGlyphMetricsBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.cc b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.cc new file mode 100644 index 0000000000..334a0c07fb --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.cc @@ -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(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 diff --git a/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.h b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.h new file mode 100644 index 0000000000..2dd4c3a130 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph.h @@ -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 +#include + +#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 BitmapGlyphPtr; +typedef Ptr BitmapGlyphBuilderPtr; +typedef std::map BitmapGlyphBuilderMap; +typedef std::vector BitmapGlyphBuilderList; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.cc b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.cc new file mode 100644 index 0000000000..ab9953bc77 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.cc @@ -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 diff --git a/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.h b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.h new file mode 100644 index 0000000000..9921d0d526 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/bitmap_glyph_info.h @@ -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 +#include + +#include "sfntly/table/subtable.h" + +namespace sfntly { + +// An immutable class holding bitmap glyph information. +class BitmapGlyphInfo : public RefCounted { + 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 BitmapGlyphInfoPtr; +typedef std::map BitmapGlyphInfoMap; +typedef std::vector BitmapLocaList; + +class StartOffsetComparator { + public: + bool operator()(BitmapGlyphInfo* lhs, BitmapGlyphInfo* rhs); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.cc b/src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.cc new file mode 100644 index 0000000000..6c7d7315a4 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.cc @@ -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 +#include + +#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 output = + new BitmapSizeTable::Builder::BitmapGlyphInfoIterator(this); + return output.Detach(); +} + +void BitmapSizeTable::Builder::GenerateLocaMap(BitmapGlyphInfoMap* output) { + assert(output); + Ptr 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(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( + iterator_base); + return it->HasNext(); + } + + case 2: { + IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->HasNext(); + } + + case 3: { + IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->HasNext(); + } + + case 4: { + IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->HasNext(); + } + + case 5: { + IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* it = + down_cast( + 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( + iterator_base); + return it->Next(); + } + + case 2: { + IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->Next(); + } + + case 3: { + IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->Next(); + } + + case 4: { + IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->Next(); + } + + case 5: { + IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* it = + down_cast( + iterator_base); + return it->Next(); + } + + default: + break; + } + } + return NULL; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.h b/src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.h new file mode 100644 index 0000000000..6733e20304 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/bitmap_size_table.h @@ -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 { + public: + class Builder : public SubTable::Builder, + public RefCounted { + public: + class BitmapGlyphInfoIterator : + public RefIterator { + 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 BitmapSizeTablePtr; +typedef std::vector BitmapSizeTableList; +typedef Ptr BitmapSizeTableBuilderPtr; +typedef std::vector BitmapSizeTableBuilderList; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.cc b/src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.cc new file mode 100644 index 0000000000..ae7dc5a731 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.cc @@ -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 glyph = new CompositeBitmapGlyph(data, format()); + return glyph.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.h b/src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.h new file mode 100644 index 0000000000..897db7e22a --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/composite_bitmap_glyph.h @@ -0,0 +1,75 @@ +/* + * 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_COMPOSITE_BITMAP_GLYPH_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_COMPOSITE_BITMAP_GLYPH_H_ + +#include "sfntly/table/bitmap/bitmap_glyph.h" + +namespace sfntly { + +class CompositeBitmapGlyph : public BitmapGlyph, + public RefCounted { + public: + class Component { + public: + Component(const Component& rhs); + + int32_t glyph_code() { return glyph_code_; } + int32_t x_offset() { return x_offset_; } + int32_t y_offset() { return y_offset_; } + + // UNIMPLEMENTED: int hashCode() + bool operator==(const Component& rhs); + Component& operator=(const Component& rhs); + + protected: + Component(int32_t glyph_code, int32_t x_offset, int32_t y_offset); + + private: + int32_t glyph_code_; + int32_t x_offset_; + int32_t y_offset_; + + friend class CompositeBitmapGlyph; + }; + + class Builder : public BitmapGlyph::Builder, + public RefCounted { + public: + Builder(WritableFontData* data, int32_t format); + Builder(ReadableFontData* data, int32_t format); + virtual ~Builder(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + }; + + CompositeBitmapGlyph(ReadableFontData* data, int32_t format); + virtual ~CompositeBitmapGlyph(); + int32_t NumComponents(); + // Note: returned immutable object over stack. + Component GetComponent(int32_t component_num) const; + + private: + void Initialize(int32_t format); + + int32_t num_components_offset_; + int32_t component_array_offset_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_COMPOSITE_BITMAP_GLYPH_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/ebdt_table.cc b/src/sfntly/src/sfntly/table/bitmap/ebdt_table.cc new file mode 100644 index 0000000000..eeb1fa06b3 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/ebdt_table.cc @@ -0,0 +1,236 @@ +/* + * 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/ebdt_table.h" + +#include + +#include "sfntly/table/bitmap/composite_bitmap_glyph.h" +#include "sfntly/table/bitmap/simple_bitmap_glyph.h" + +namespace sfntly { +/****************************************************************************** + * EbdtTable class + ******************************************************************************/ +EbdtTable::~EbdtTable() { +} + +int32_t EbdtTable::Version() { + return data_->ReadFixed(Offset::kVersion); +} + +CALLER_ATTACH +BitmapGlyph* EbdtTable::Glyph(int32_t offset, int32_t length, int32_t format) { + ReadableFontDataPtr glyph_data; + glyph_data.Attach(down_cast(data_->Slice(offset, length))); + return BitmapGlyph::CreateGlyph(glyph_data, format); +} + +EbdtTable::EbdtTable(Header* header, ReadableFontData* data) + : SubTableContainerTable(header, data) { +} + +/****************************************************************************** + * EbdtTable::Builder class + ******************************************************************************/ +EbdtTable::Builder::Builder(Header* header, WritableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +EbdtTable::Builder::Builder(Header* header, ReadableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +EbdtTable::Builder::~Builder() { +} + +CALLER_ATTACH FontDataTable* + EbdtTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new EbdtTable(header(), data); + return table.Detach(); +} + +void EbdtTable::Builder::SubDataSet() { + Revert(); +} + +int32_t EbdtTable::Builder::SubDataSizeToSerialize() { + if (glyph_builders_.empty()) { + return 0; + } + bool fixed = true; + int32_t size = Offset::kHeaderLength; + for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(), + builder_end = glyph_builders_.end(); + builder_map != builder_end; + builder_map++) { + for (BitmapGlyphBuilderMap::iterator glyph_entry = builder_map->begin(), + glyph_entry_end = builder_map->end(); + glyph_entry != glyph_entry_end; + glyph_entry++) { + int32_t glyph_size = glyph_entry->second->SubDataSizeToSerialize(); + size += abs(glyph_size); + fixed = (glyph_size <= 0) ? false : fixed; + } + } + return (fixed ? 1 : -1) * size; +} + +bool EbdtTable::Builder::SubReadyToSerialize() { + if (glyph_builders_.empty()) { + return false; + } + return true; +} + +int32_t EbdtTable::Builder::SubSerialize(WritableFontData* new_data) { + int32_t size = 0; + size += new_data->WriteFixed(Offset::kVersion, kVersion); + for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(), + builder_end = glyph_builders_.end(); + builder_map != builder_end; + builder_map++) { + for (BitmapGlyphBuilderMap::iterator glyph_entry = builder_map->begin(), + glyph_entry_end = builder_map->end(); + glyph_entry != glyph_entry_end; + glyph_entry++) { + WritableFontDataPtr slice; + slice.Attach(down_cast(new_data->Slice(size))); + size += glyph_entry->second->SubSerialize(slice); + } + } + return size; +} + +void EbdtTable::Builder::SetLoca(BitmapLocaList* loca_list) { + assert(loca_list); + Revert(); + glyph_loca_.resize(loca_list->size()); + std::copy(loca_list->begin(), loca_list->end(), glyph_loca_.begin()); +} + +void EbdtTable::Builder::GenerateLocaList(BitmapLocaList* output) { + assert(output); + output->clear(); + + if (glyph_builders_.empty()) { + if (glyph_loca_.empty()) { + return; + } + } + + int start_offset = Offset::kHeaderLength; + for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(), + builder_end = glyph_builders_.end(); + builder_map != builder_end; + builder_map++) { + BitmapGlyphInfoMap new_loca_map; + int32_t glyph_offset = 0; + for (BitmapGlyphBuilderMap::iterator glyph_entry = builder_map->begin(), + glyph_end = builder_map->end(); + glyph_entry != glyph_end; + glyph_entry++) { + BitmapGlyphBuilderPtr builder = glyph_entry->second; + int32_t size = builder->SubDataSizeToSerialize(); + BitmapGlyphInfoPtr info = new BitmapGlyphInfo(glyph_entry->first, + start_offset + glyph_offset, size, builder->format()); + new_loca_map[glyph_entry->first] = info; + glyph_offset += size; + } + start_offset += glyph_offset; + output->push_back(new_loca_map); + } +} + +BitmapGlyphBuilderList* EbdtTable::Builder::GlyphBuilders() { + return GetGlyphBuilders(); +} + +void EbdtTable::Builder::SetGlyphBuilders( + BitmapGlyphBuilderList* glyph_builders) { + glyph_builders_.clear(); + std::copy(glyph_builders->begin(), glyph_builders->end(), + glyph_builders_.begin()); + set_model_changed(); +} + +void EbdtTable::Builder::Revert() { + glyph_loca_.clear(); + glyph_builders_.clear(); + set_model_changed(false); +} + +CALLER_ATTACH +EbdtTable::Builder* EbdtTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new Builder(header, data); + return builder.Detach(); +} + +CALLER_ATTACH +EbdtTable::Builder* EbdtTable::Builder::CreateBuilder(Header* header, + ReadableFontData* data) { + Ptr builder; + builder = new Builder(header, data); + return builder.Detach(); +} + +BitmapGlyphBuilderList* EbdtTable::Builder::GetGlyphBuilders() { + if (glyph_builders_.empty()) { + if (glyph_loca_.empty()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalStateException( + "Loca values not set - unable to parse glyph data."); +#endif + return NULL; + } + Initialize(InternalReadData(), &glyph_loca_, &glyph_builders_); + set_model_changed(); + } + return &glyph_builders_; +} + +void EbdtTable::Builder::Initialize(ReadableFontData* data, + BitmapLocaList* loca_list, + BitmapGlyphBuilderList* output) { + assert(loca_list); + assert(output); + + output->clear(); + if (data) { + for (BitmapLocaList::iterator loca_map = loca_list->begin(), + loca_end = loca_list->end(); + loca_map != loca_end; loca_map++) { + BitmapGlyphBuilderMap glyph_builder_map; + for (BitmapGlyphInfoMap::iterator entry = loca_map->begin(), + entry_end = loca_map->end(); + entry != entry_end; entry++) { + BitmapGlyphInfoPtr info = entry->second; + ReadableFontDataPtr slice; + slice.Attach(down_cast(data->Slice( + info->offset(), info->length()))); + BitmapGlyphBuilderPtr glyph_builder; + glyph_builder.Attach(BitmapGlyph::Builder::CreateGlyphBuilder( + slice, info->format())); + glyph_builder_map[entry->first] = glyph_builder; + } + output->push_back(glyph_builder_map); + } + } +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/ebdt_table.h b/src/sfntly/src/sfntly/table/bitmap/ebdt_table.h new file mode 100644 index 0000000000..d138c14ca5 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/ebdt_table.h @@ -0,0 +1,108 @@ +/* + * 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_EBDT_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBDT_TABLE_H_ + +#include "sfntly/table/bitmap/bitmap_glyph.h" +#include "sfntly/table/bitmap/bitmap_glyph_info.h" +#include "sfntly/table/subtable_container_table.h" + +namespace sfntly { + +class EbdtTable : public SubTableContainerTable, + public RefCounted { + public: + struct Offset { + enum { + kVersion = 0, + kHeaderLength = DataSize::kFixed, + }; + }; + + class Builder : public SubTableContainerTable::Builder, + public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + virtual int32_t SubSerialize(WritableFontData* new_data); + virtual bool SubReadyToSerialize(); + virtual int32_t SubDataSizeToSerialize(); + virtual void SubDataSet(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + void SetLoca(BitmapLocaList* loca_list); + void GenerateLocaList(BitmapLocaList* output); + + // Gets the List of glyph builders for the glyph table builder. These may be + // manipulated in any way by the caller and the changes will be reflected in + // the final glyph table produced. + // If there is no current data for the glyph builder or the glyph builders + // have not been previously set then this will return an empty glyph builder + // List. If there is current data (i.e. data read from an existing font) and + // the loca list has not been set or is null, empty, or invalid, then an + // empty glyph builder List will be returned. + // @return the list of glyph builders + BitmapGlyphBuilderList* GlyphBuilders(); + + // Replace the internal glyph builders with the one provided. The provided + // list and all contained objects belong to this builder. + // This call is only required if the entire set of glyphs in the glyph + // table builder are being replaced. If the glyph builder list provided from + // the {@link EbdtTable.Builder#glyphBuilders()} is being used and modified + // then those changes will already be reflected in the glyph table builder. + // @param glyphBuilders the new glyph builders + void SetGlyphBuilders(BitmapGlyphBuilderList* glyph_builders); + + void Revert(); + + // Create a new builder using the header information and data provided. + // @param header the header information + // @param data the data holding the table + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + ReadableFontData* data); + + private: + BitmapGlyphBuilderList* GetGlyphBuilders(); + static void Initialize(ReadableFontData* data, + BitmapLocaList* loca_list, + BitmapGlyphBuilderList* output); + + static const int32_t kVersion = 0x00020000; // TODO(stuartg): const/enum + BitmapLocaList glyph_loca_; + BitmapGlyphBuilderList glyph_builders_; + }; + + virtual ~EbdtTable(); + int32_t Version(); + CALLER_ATTACH BitmapGlyph* Glyph(int32_t offset, + int32_t length, + int32_t format); + protected: + EbdtTable(Header* header, ReadableFontData* data); +}; +typedef Ptr EbdtTablePtr; +typedef Ptr EbdtTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBDT_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/eblc_table.cc b/src/sfntly/src/sfntly/table/bitmap/eblc_table.cc new file mode 100644 index 0000000000..0ad2764bf6 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/eblc_table.cc @@ -0,0 +1,313 @@ +/* + * 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/eblc_table.h" + +#include +#include + +#include "sfntly/math/font_math.h" + +namespace sfntly { +/****************************************************************************** + * EblcTable class + ******************************************************************************/ +int32_t EblcTable::Version() { + return data_->ReadFixed(Offset::kVersion); +} + +int32_t EblcTable::NumSizes() { + return data_->ReadULongAsInt(Offset::kNumSizes); +} + +BitmapSizeTable* EblcTable::GetBitmapSizeTable(int32_t index) { + if (index < 0 || index > NumSizes()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException( + "Size table index is outside the range of tables."); +#endif + return NULL; + } + BitmapSizeTableList* bitmap_size_table_list = GetBitmapSizeTableList(); + if (bitmap_size_table_list) { + return (*bitmap_size_table_list)[index]; + } + return NULL; +} + +EblcTable::EblcTable(Header* header, ReadableFontData* data) + : SubTableContainerTable(header, data) { +} + +BitmapSizeTableList* EblcTable::GetBitmapSizeTableList() { + AutoLock lock(bitmap_size_table_lock_); + if (bitmap_size_table_.empty()) { + CreateBitmapSizeTable(data_, NumSizes(), &bitmap_size_table_); + } + return &bitmap_size_table_; +} + +// static +void EblcTable::CreateBitmapSizeTable(ReadableFontData* data, + int32_t num_sizes, + BitmapSizeTableList* output) { + assert(data); + assert(output); + for (int32_t i = 0; i < num_sizes; ++i) { + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(Offset::kBitmapSizeTableArrayStart + + i * Offset::kBitmapSizeTableLength, + Offset::kBitmapSizeTableLength))); + BitmapSizeTableBuilderPtr size_builder; + size_builder.Attach( + BitmapSizeTable::Builder::CreateBuilder(new_data, data)); + BitmapSizeTablePtr size; + size.Attach(down_cast(size_builder->Build())); + output->push_back(size); + } +} + +/****************************************************************************** + * EblcTable::Builder class + ******************************************************************************/ +EblcTable::Builder::Builder(Header* header, WritableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +EblcTable::Builder::Builder(Header* header, ReadableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +EblcTable::Builder::~Builder() { +} + +int32_t EblcTable::Builder::SubSerialize(WritableFontData* new_data) { + // header + int32_t size = new_data->WriteFixed(0, kVersion); + size += new_data->WriteULong(size, size_table_builders_.size()); + + // calculate the offsets + // offset to the start of the size table array + int32_t size_table_start_offset = size; + // walking offset in the size table array + int32_t size_table_offset = size_table_start_offset; + // offset to the start of the whole index subtable block + int32_t sub_table_block_start_offset = size_table_offset + + size_table_builders_.size() * Offset::kBitmapSizeTableLength; + // walking offset in the index subtable + // points to the start of the current subtable block + int32_t current_sub_table_block_start_offset = sub_table_block_start_offset; + +#if defined (SFNTLY_DEBUG_BITMAP) + int32_t size_index = 0; +#endif + for (BitmapSizeTableBuilderList::iterator + size_builder = size_table_builders_.begin(), + size_builder_end = size_table_builders_.end(); + size_builder != size_builder_end; size_builder++) { + (*size_builder)->SetIndexSubTableArrayOffset( + current_sub_table_block_start_offset); + IndexSubTableBuilderList* index_sub_table_builder_list = + (*size_builder)->IndexSubTableBuilders(); + + // walking offset within the current subTable array + int32_t index_sub_table_array_offset = current_sub_table_block_start_offset; + // walking offset within the subTable entries + int32_t index_sub_table_offset = index_sub_table_array_offset + + index_sub_table_builder_list->size() * Offset::kIndexSubHeaderLength; + +#if defined (SFNTLY_DEBUG_BITMAP) + fprintf(stderr, "size %d: sizeTable=%x, current subTable Block=%x, ", + size_index, size_table_offset, + current_sub_table_block_start_offset); + fprintf(stderr, "index subTableStart=%x\n", index_sub_table_offset); + size_index++; + int32_t sub_table_index = 0; +#endif + for (IndexSubTableBuilderList::iterator + index_sub_table_builder = index_sub_table_builder_list->begin(), + index_sub_table_builder_end = index_sub_table_builder_list->end(); + index_sub_table_builder != index_sub_table_builder_end; + index_sub_table_builder++) { +#if defined (SFNTLY_DEBUG_BITMAP) + fprintf(stderr, "\tsubTableIndex %d: format=%x, ", sub_table_index, + (*index_sub_table_builder)->index_format()); + fprintf(stderr, "indexSubTableArrayOffset=%x, indexSubTableOffset=%x\n", + index_sub_table_array_offset, index_sub_table_offset); + sub_table_index++; +#endif + // array entry + index_sub_table_array_offset += new_data->WriteUShort( + index_sub_table_array_offset, + (*index_sub_table_builder)->first_glyph_index()); + index_sub_table_array_offset += new_data->WriteUShort( + index_sub_table_array_offset, + (*index_sub_table_builder)->last_glyph_index()); + index_sub_table_array_offset += new_data->WriteULong( + index_sub_table_array_offset, + index_sub_table_offset - current_sub_table_block_start_offset); + + // index sub table + WritableFontDataPtr slice_index_sub_table; + slice_index_sub_table.Attach(down_cast( + new_data->Slice(index_sub_table_offset))); + int32_t current_sub_table_size = + (*index_sub_table_builder)->SubSerialize(slice_index_sub_table); + int32_t padding = FontMath::PaddingRequired(current_sub_table_size, + DataSize::kULONG); +#if defined (SFNTLY_DEBUG_BITMAP) + fprintf(stderr, "\t\tsubTableSize = %x, padding = %x\n", + current_sub_table_size, padding); +#endif + index_sub_table_offset += current_sub_table_size; + index_sub_table_offset += + new_data->WritePadding(index_sub_table_offset, padding); + } + + // serialize size table + (*size_builder)->SetIndexTableSize( + index_sub_table_offset - current_sub_table_block_start_offset); + WritableFontDataPtr slice_size_table; + slice_size_table.Attach(down_cast( + new_data->Slice(size_table_offset))); + size_table_offset += (*size_builder)->SubSerialize(slice_size_table); + + current_sub_table_block_start_offset = index_sub_table_offset; + } + return size + current_sub_table_block_start_offset; +} + +bool EblcTable::Builder::SubReadyToSerialize() { + if (size_table_builders_.empty()) { + return false; + } + for (BitmapSizeTableBuilderList::iterator b = size_table_builders_.begin(), + e = size_table_builders_.end(); + b != e; b++) { + if (!(*b)->SubReadyToSerialize()) { + return false; + } + } + return true; +} + +int32_t EblcTable::Builder::SubDataSizeToSerialize() { + if (size_table_builders_.empty()) { + return 0; + } + int32_t size = Offset::kHeaderLength; + bool variable = false; +#if defined (SFNTLY_DEBUG_BITMAP) + size_t size_index = 0; +#endif + for (BitmapSizeTableBuilderList::iterator b = size_table_builders_.begin(), + e = size_table_builders_.end(); + b != e; b++) { + int32_t size_builder_size = (*b)->SubDataSizeToSerialize(); +#if defined (SFNTLY_DEBUG_BITMAP) + fprintf(stderr, "sizeIndex = %d, sizeBuilderSize=0x%x (%d)\n", + size_index++, size_builder_size, size_builder_size); +#endif + variable = size_builder_size > 0 ? variable : true; + size += abs(size_builder_size); + } +#if defined (SFNTLY_DEBUG_BITMAP) + fprintf(stderr, "eblc size=%d\n", size); +#endif + return variable ? -size : size; +} + +void EblcTable::Builder::SubDataSet() { + Revert(); +} + +BitmapSizeTableBuilderList* EblcTable::Builder::BitmapSizeBuilders() { + return GetSizeList(); +} + +void EblcTable::Builder::Revert() { + size_table_builders_.clear(); + set_model_changed(false); +} + +void EblcTable::Builder::GenerateLocaList(BitmapLocaList* output) { + assert(output); + BitmapSizeTableBuilderList* size_builder_list = GetSizeList(); + output->clear(); +#if defined (SFNTLY_DEBUG_BITMAP) + int32_t size_index = 0; +#endif + for (BitmapSizeTableBuilderList::iterator b = size_builder_list->begin(), + e = size_builder_list->end(); + b != e; b++) { +#if defined (SFNTLY_DEBUG_BITMAP) + fprintf(stderr, "size table = %d\n", size_index++); +#endif + BitmapGlyphInfoMap loca_map; + (*b)->GenerateLocaMap(&loca_map); + output->push_back(loca_map); + } +} + +CALLER_ATTACH +FontDataTable* EblcTable::Builder::SubBuildTable(ReadableFontData* data) { + Ptr new_table = new EblcTable(header(), data); + return new_table.Detach(); +} + +// static +CALLER_ATTACH EblcTable::Builder* + EblcTable::Builder::CreateBuilder(Header* header, WritableFontData* data) { + Ptr new_builder = new EblcTable::Builder(header, data); + return new_builder.Detach(); +} + +// static +CALLER_ATTACH EblcTable::Builder* + EblcTable::Builder::CreateBuilder(Header* header, ReadableFontData* data) { + Ptr new_builder = new EblcTable::Builder(header, data); + return new_builder.Detach(); +} + +BitmapSizeTableBuilderList* EblcTable::Builder::GetSizeList() { + if (size_table_builders_.empty()) { + Initialize(InternalReadData(), &size_table_builders_); + set_model_changed(); + } + return &size_table_builders_; +} + +void EblcTable::Builder::Initialize(ReadableFontData* data, + BitmapSizeTableBuilderList* output) { + assert(output); + if (data) { + int32_t num_sizes = data->ReadULongAsInt(Offset::kNumSizes); + for (int32_t i = 0; i < num_sizes; ++i) { + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(Offset::kBitmapSizeTableArrayStart + + i * Offset::kBitmapSizeTableLength, + Offset::kBitmapSizeTableLength))); + BitmapSizeTableBuilderPtr size_builder; + size_builder.Attach(BitmapSizeTable::Builder::CreateBuilder( + new_data, data)); + output->push_back(size_builder); + } + } +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/eblc_table.h b/src/sfntly/src/sfntly/table/bitmap/eblc_table.h new file mode 100644 index 0000000000..b04338a93b --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/eblc_table.h @@ -0,0 +1,194 @@ +/* + * 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_EBLC_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBLC_TABLE_H_ + +#include "sfntly/port/lock.h" +#include "sfntly/table/bitmap/big_glyph_metrics.h" +#include "sfntly/table/bitmap/bitmap_glyph.h" +#include "sfntly/table/bitmap/bitmap_size_table.h" +#include "sfntly/table/subtable_container_table.h" + +namespace sfntly { + +class EblcTable : public SubTableContainerTable, + public RefCounted { + public: + struct Offset { + enum { + // header + kVersion = 0, + kNumSizes = 4, + kHeaderLength = kNumSizes + DataSize::kULONG, + + // bitmapSizeTable + kBitmapSizeTableArrayStart = kHeaderLength, + kBitmapSizeTableLength = 48, + kBitmapSizeTable_indexSubTableArrayOffset = 0, + kBitmapSizeTable_indexTableSize = 4, + kBitmapSizeTable_numberOfIndexSubTables = 8, + kBitmapSizeTable_colorRef = 12, + kBitmapSizeTable_hori = 16, + kBitmapSizeTable_vert = 28, + kBitmapSizeTable_startGlyphIndex = 40, + kBitmapSizeTable_endGlyphIndex = 42, + kBitmapSizeTable_ppemX = 44, + kBitmapSizeTable_ppemY = 45, + kBitmapSizeTable_bitDepth = 46, + kBitmapSizeTable_flags = 47, + + // sbitLineMetrics + kSbitLineMetricsLength = 12, + kSbitLineMetrics_ascender = 0, + kSbitLineMetrics_descender = 1, + kSbitLineMetrics_widthMax = 2, + kSbitLineMetrics_caretSlopeNumerator = 3, + kSbitLineMetrics_caretSlopeDenominator = 4, + kSbitLineMetrics_caretOffset = 5, + kSbitLineMetrics_minOriginSB = 6, + kSbitLineMetrics_minAdvanceSB = 7, + kSbitLineMetrics_maxBeforeBL = 8, + kSbitLineMetrics_minAfterBL = 9, + kSbitLineMetrics_pad1 = 10, + kSbitLineMetrics_pad2 = 11, + + // indexSubTable + kIndexSubTableEntryLength = 8, + kIndexSubTableEntry_firstGlyphIndex = 0, + kIndexSubTableEntry_lastGlyphIndex = 2, + kIndexSubTableEntry_additionalOffsetToIndexSubTable = 4, + + // indexSubHeader + kIndexSubHeaderLength = 8, + kIndexSubHeader_indexFormat = 0, + kIndexSubHeader_imageFormat = 2, + kIndexSubHeader_imageDataOffset = 4, + + // indexSubTable - all offset relative to the subtable start + + // indexSubTable1 + kIndexSubTable1_offsetArray = kIndexSubHeaderLength, + kIndexSubTable1_builderDataSize = kIndexSubHeaderLength, + + // kIndexSubTable2 + kIndexSubTable2Length = kIndexSubHeaderLength + + DataSize::kULONG + + BitmapGlyph::Offset::kBigGlyphMetricsLength, + kIndexSubTable2_imageSize = kIndexSubHeaderLength, + kIndexSubTable2_bigGlyphMetrics = kIndexSubTable2_imageSize + + DataSize::kULONG, + kIndexSubTable2_builderDataSize = kIndexSubTable2_bigGlyphMetrics + + BigGlyphMetrics::Offset::kMetricsLength, + + // kIndexSubTable3 + kIndexSubTable3_offsetArray = kIndexSubHeaderLength, + kIndexSubTable3_builderDataSize = kIndexSubTable3_offsetArray, + + // kIndexSubTable4 + kIndexSubTable4_numGlyphs = kIndexSubHeaderLength, + kIndexSubTable4_glyphArray = kIndexSubTable4_numGlyphs + + DataSize::kULONG, + kIndexSubTable4_codeOffsetPairLength = 2 * DataSize::kUSHORT, + kIndexSubTable4_codeOffsetPair_glyphCode = 0, + kIndexSubTable4_codeOffsetPair_offset = DataSize::kUSHORT, + kIndexSubTable4_builderDataSize = kIndexSubTable4_glyphArray, + + // kIndexSubTable5 + kIndexSubTable5_imageSize = kIndexSubHeaderLength, + kIndexSubTable5_bigGlyphMetrics = kIndexSubTable5_imageSize + + DataSize::kULONG, + kIndexSubTable5_numGlyphs = kIndexSubTable5_bigGlyphMetrics + + BitmapGlyph::Offset::kBigGlyphMetricsLength, + kIndexSubTable5_glyphArray = kIndexSubTable5_numGlyphs + + DataSize::kULONG, + kIndexSubTable5_builderDataSize = kIndexSubTable5_glyphArray, + + // codeOffsetPair + kCodeOffsetPairLength = 2 * DataSize::kUSHORT, + kCodeOffsetPair_glyphCode = 0, + kCodeOffsetPair_offset = DataSize::kUSHORT, + }; + }; + + class Builder : public SubTableContainerTable::Builder, + public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + virtual int32_t SubSerialize(WritableFontData* new_data); + virtual bool SubReadyToSerialize(); + virtual int32_t SubDataSizeToSerialize(); + virtual void SubDataSet(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + BitmapSizeTableBuilderList* BitmapSizeBuilders(); + void Revert(); + + // Generates the loca list for the EBDT table. The list is intended to be + // used by the EBDT to allow it to parse the glyph data and generate glyph + // objects. After returning from this method the list belongs to the caller. + // The list entries are in the same order as the size table builders are at + // the time of this call. + // @return the list of loca maps with one for each size table builder + void GenerateLocaList(BitmapLocaList* output); + + // Create a new builder using the header information and data provided. + // @param header the header information + // @param data the data holding the table + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + ReadableFontData* data); + + private: + BitmapSizeTableBuilderList* GetSizeList(); + void Initialize(ReadableFontData* data, BitmapSizeTableBuilderList* output); + + static const int32_t kVersion = 0x00020000; + BitmapSizeTableBuilderList size_table_builders_; + }; + + int32_t Version(); + int32_t NumSizes(); + // UNIMPLEMENTED: toString() + + BitmapSizeTable* GetBitmapSizeTable(int32_t index); + + static const int32_t NOTDEF = -1; + + protected: + EblcTable(Header* header, ReadableFontData* data); + + private: + BitmapSizeTableList* GetBitmapSizeTableList(); + + static void CreateBitmapSizeTable(ReadableFontData* data, + int32_t num_sizes, + BitmapSizeTableList* output); + + Lock bitmap_size_table_lock_; + BitmapSizeTableList bitmap_size_table_; +}; +typedef Ptr EblcTablePtr; +typedef Ptr EblcTableBuilderPtr; +} + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBLC_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/ebsc_table.cc b/src/sfntly/src/sfntly/table/bitmap/ebsc_table.cc new file mode 100644 index 0000000000..458c2d49e8 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/ebsc_table.cc @@ -0,0 +1,107 @@ +/* + * 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/ebsc_table.h" + +namespace sfntly { +/****************************************************************************** + * EbscTable class + ******************************************************************************/ +EbscTable::~EbscTable() { +} + +int32_t EbscTable::Version() { + return data_->ReadFixed(Offset::kVersion); +} + +int32_t EbscTable::NumSizes() { + return data_->ReadULongAsInt(Offset::kNumSizes); +} + +EbscTable::EbscTable(Header* header, ReadableFontData* data) + : Table(header, data) { +} + +/****************************************************************************** + * EbscTable::BitmapScaleTable class + ******************************************************************************/ +EbscTable::BitmapScaleTable::~BitmapScaleTable() { +} + +EbscTable::BitmapScaleTable::BitmapScaleTable(ReadableFontData* data) + : SubTable(data) { +} + +int32_t EbscTable::BitmapScaleTable::PpemX() { + return data_->ReadByte(Offset::kBitmapScaleTable_ppemX); +} + +int32_t EbscTable::BitmapScaleTable::PpemY() { + return data_->ReadByte(Offset::kBitmapScaleTable_ppemY); +} + +int32_t EbscTable::BitmapScaleTable::SubstitutePpemX() { + return data_->ReadByte(Offset::kBitmapScaleTable_substitutePpemX); +} + +int32_t EbscTable::BitmapScaleTable::SubstitutePpemY() { + return data_->ReadByte(Offset::kBitmapScaleTable_substitutePpemY); +} + +/****************************************************************************** + * EbscTable::Builder class + ******************************************************************************/ +EbscTable::Builder::~Builder() { +} + +CALLER_ATTACH EbscTable::Builder* EbscTable::Builder::CreateBuilder( + Header* header, WritableFontData* data) { + EbscTableBuilderPtr builder = new EbscTable::Builder(header, data); + return builder.Detach(); +} + +EbscTable::Builder::Builder(Header* header, WritableFontData* data) + : Table::Builder(header, data) { +} + +EbscTable::Builder::Builder(Header* header, ReadableFontData* data) + : Table::Builder(header, data) { +} + +CALLER_ATTACH +FontDataTable* EbscTable::Builder::SubBuildTable(ReadableFontData* data) { + EbscTablePtr output = new EbscTable(header(), data); + return output.Detach(); +} + +void EbscTable::Builder::SubDataSet() { + // NOP +} + +int32_t EbscTable::Builder::SubDataSizeToSerialize() { + return 0; +} + +bool EbscTable::Builder::SubReadyToSerialize() { + return false; +} + +int32_t EbscTable::Builder::SubSerialize(WritableFontData* new_data) { + UNREFERENCED_PARAMETER(new_data); + return 0; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/ebsc_table.h b/src/sfntly/src/sfntly/table/bitmap/ebsc_table.h new file mode 100644 index 0000000000..b79df380df --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/ebsc_table.h @@ -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. + */ + +#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBSC_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBSC_TABLE_H_ + +#include "sfntly/table/bitmap/eblc_table.h" + +namespace sfntly { + +class EbscTable : public Table, + public RefCounted { + public: + struct Offset { + enum { + // header + kVersion = 0, + kNumSizes = DataSize::kFixed, + kHeaderLength = kNumSizes + DataSize::kULONG, + kBitmapScaleTableStart = kHeaderLength, + + // bitmapScaleTable + kBitmapScaleTable_hori = 0, + kBitmapScaleTable_vert = EblcTable::Offset::kSbitLineMetricsLength, + kBitmapScaleTable_ppemX = kBitmapScaleTable_vert + + EblcTable::Offset::kSbitLineMetricsLength, + kBitmapScaleTable_ppemY = kBitmapScaleTable_ppemX + DataSize::kBYTE, + kBitmapScaleTable_substitutePpemX = kBitmapScaleTable_ppemY + + DataSize::kBYTE, + kBitmapScaleTable_substitutePpemY = kBitmapScaleTable_substitutePpemX + + DataSize::kBYTE, + kBitmapScaleTableLength = kBitmapScaleTable_substitutePpemY + + DataSize::kBYTE, + }; + }; + + class BitmapScaleTable : public SubTable, + public RefCounted { + public: + virtual ~BitmapScaleTable(); + int32_t PpemX(); + int32_t PpemY(); + int32_t SubstitutePpemX(); + int32_t SubstitutePpemY(); + + protected: + // Note: caller to do data->Slice(offset, Offset::kBitmapScaleTableLength) + explicit BitmapScaleTable(ReadableFontData* data); + }; + + // TODO(stuartg): currently the builder just builds from initial data + // - need to make fully working but few if any examples to test with + class Builder : public Table::Builder, + public RefCounted { + public: + virtual ~Builder(); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + protected: + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + }; + + virtual ~EbscTable(); + + int32_t Version(); + int32_t NumSizes(); + // Note: renamed from bitmapScaleTable + CALLER_ATTACH BitmapScaleTable* GetBitmapScaleTable(int32_t index); + + private: + EbscTable(Header* header, ReadableFontData* data); + friend class Builder; +}; +typedef Ptr EbscTablePtr; +typedef Ptr EbscTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBSC_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/glyph_metrics.cc b/src/sfntly/src/sfntly/table/bitmap/glyph_metrics.cc new file mode 100644 index 0000000000..e91eb9921e --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/glyph_metrics.cc @@ -0,0 +1,39 @@ +/* + * 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/glyph_metrics.h" + +namespace sfntly { + +GlyphMetrics::~GlyphMetrics() { +} + +GlyphMetrics::GlyphMetrics(ReadableFontData* data) + : SubTable(data) { +} + +GlyphMetrics::Builder::~Builder() { +} + +GlyphMetrics::Builder::Builder(WritableFontData* data) + : SubTable::Builder(data) { +} + +GlyphMetrics::Builder::Builder(ReadableFontData* data) + : SubTable::Builder(data) { +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/glyph_metrics.h b/src/sfntly/src/sfntly/table/bitmap/glyph_metrics.h new file mode 100644 index 0000000000..5f16aaa661 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/glyph_metrics.h @@ -0,0 +1,43 @@ +/* + * 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_METRICS_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_METRICS_H_ + +#include "sfntly/table/subtable.h" + +namespace sfntly { + +class GlyphMetrics : public SubTable { + public: + virtual ~GlyphMetrics(); + + protected: + class Builder : public SubTable::Builder { + public: + virtual ~Builder(); + + protected: + explicit Builder(WritableFontData* data); + explicit Builder(ReadableFontData* data); + }; + + explicit GlyphMetrics(ReadableFontData* data); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_METRICS_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table.cc b/src/sfntly/src/sfntly/table/bitmap/index_sub_table.cc new file mode 100644 index 0000000000..5e29784504 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table.cc @@ -0,0 +1,278 @@ +/* + * 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/index_sub_table.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 { +/****************************************************************************** + * IndexSubTable class + ******************************************************************************/ +CALLER_ATTACH BitmapGlyphInfo* IndexSubTable::GlyphInfo(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return NULL; + } + if (GlyphStartOffset(glyph_id) == -1) { + return NULL; + } + BitmapGlyphInfoPtr output = new BitmapGlyphInfo(glyph_id, + image_data_offset(), + GlyphStartOffset(glyph_id), + GlyphLength(glyph_id), + image_format()); + return output.Detach(); +} + +int32_t IndexSubTable::GlyphOffset(int32_t glyph_id) { + int32_t glyph_start_offset = GlyphStartOffset(glyph_id); + if (glyph_start_offset == -1) { + return -1; + } + return image_data_offset() + glyph_start_offset; +} + +// static +CALLER_ATTACH IndexSubTable* + IndexSubTable::CreateIndexSubTable(ReadableFontData* data, + int32_t offset_to_index_sub_table_array, + int32_t array_index) { + IndexSubTableBuilderPtr builder; + builder.Attach(IndexSubTable::Builder::CreateBuilder( + data, offset_to_index_sub_table_array, array_index)); + return down_cast(builder->Build()); +} + +IndexSubTable::IndexSubTable(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : SubTable(data), + first_glyph_index_(first_glyph_index), + last_glyph_index_(last_glyph_index) { + index_format_ = + data_->ReadUShort(EblcTable::Offset::kIndexSubHeader_indexFormat); + image_format_ = + data_->ReadUShort(EblcTable::Offset::kIndexSubHeader_imageFormat); + image_data_offset_ = + data_->ReadULongAsInt(EblcTable::Offset::kIndexSubHeader_imageDataOffset); +} + +int32_t IndexSubTable::CheckGlyphRange(int32_t glyph_id) { + return CheckGlyphRange(glyph_id, first_glyph_index(), last_glyph_index()); +} + +// static +int32_t IndexSubTable::CheckGlyphRange(int32_t glyph_id, + int32_t first_glyph_id, + int32_t last_glyph_id) { + if (glyph_id < first_glyph_id || glyph_id > last_glyph_id) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException("Glyph ID is outside of the allowed range."); +#endif + return -1; + } + return glyph_id - first_glyph_id; +} + +/****************************************************************************** + * IndexSubTable::Builder class + ******************************************************************************/ +IndexSubTable::Builder::~Builder() { +} + +void IndexSubTable::Builder::Revert() { + set_model_changed(false); + Initialize(InternalReadData()); +} + +CALLER_ATTACH BitmapGlyphInfo* IndexSubTable::Builder::GlyphInfo( + int32_t glyph_id) { + BitmapGlyphInfoPtr glyph_info = + new BitmapGlyphInfo(glyph_id, + image_data_offset(), + GlyphStartOffset(glyph_id), + GlyphLength(glyph_id), + image_format()); + return glyph_info.Detach(); +} + +int32_t IndexSubTable::Builder::GlyphOffset(int32_t glyph_id) { + return image_data_offset() + GlyphStartOffset(glyph_id); +} + +// static +CALLER_ATTACH IndexSubTable::Builder* +IndexSubTable::Builder::CreateBuilder(int32_t index_format) { + switch (index_format) { + case Format::FORMAT_1: + return IndexSubTableFormat1::Builder::CreateBuilder(); + case Format::FORMAT_2: + return IndexSubTableFormat2::Builder::CreateBuilder(); + case Format::FORMAT_3: + return IndexSubTableFormat3::Builder::CreateBuilder(); + case Format::FORMAT_4: + return IndexSubTableFormat4::Builder::CreateBuilder(); + case Format::FORMAT_5: + return IndexSubTableFormat5::Builder::CreateBuilder(); + default: +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalArgumentException("Invalid index subtable format"); +#endif + return NULL; + } +} + +// static +CALLER_ATTACH IndexSubTable::Builder* +IndexSubTable::Builder::CreateBuilder(ReadableFontData* data, + int32_t offset_to_index_sub_table_array, int32_t array_index) { + int32_t index_sub_table_entry_offset = + offset_to_index_sub_table_array + + array_index * EblcTable::Offset::kIndexSubTableEntryLength; + int32_t first_glyph_index = + data->ReadUShort(index_sub_table_entry_offset + + EblcTable::Offset::kIndexSubTableEntry_firstGlyphIndex); + int32_t last_glyph_index = + data->ReadUShort(index_sub_table_entry_offset + + EblcTable::Offset::kIndexSubTableEntry_lastGlyphIndex); + int32_t additional_offset_to_index_subtable = data->ReadULongAsInt( + index_sub_table_entry_offset + + EblcTable::Offset::kIndexSubTableEntry_additionalOffsetToIndexSubTable); + int32_t index_sub_table_offset = offset_to_index_sub_table_array + + additional_offset_to_index_subtable; + int32_t index_format = data->ReadUShort(index_sub_table_offset); + switch (index_format) { + case 1: + return IndexSubTableFormat1::Builder::CreateBuilder( + data, index_sub_table_offset, first_glyph_index, last_glyph_index); + case 2: + return IndexSubTableFormat2::Builder::CreateBuilder( + data, index_sub_table_offset, first_glyph_index, last_glyph_index); + case 3: + return IndexSubTableFormat3::Builder::CreateBuilder( + data, index_sub_table_offset, first_glyph_index, last_glyph_index); + case 4: + return IndexSubTableFormat4::Builder::CreateBuilder( + data, index_sub_table_offset, first_glyph_index, last_glyph_index); + case 5: + return IndexSubTableFormat5::Builder::CreateBuilder( + data, index_sub_table_offset, first_glyph_index, last_glyph_index); + default: + // Unknown format and unable to process. +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalArgumentException("Invalid Index Subtable Format"); +#endif + break; + } + return NULL; +} + +CALLER_ATTACH +FontDataTable* IndexSubTable::Builder::SubBuildTable(ReadableFontData* data) { + UNREFERENCED_PARAMETER(data); + return NULL; +} + +void IndexSubTable::Builder::SubDataSet() { + // NOP +} + +int32_t IndexSubTable::Builder::SubDataSizeToSerialize() { + return 0; +} + +bool IndexSubTable::Builder::SubReadyToSerialize() { + return false; +} + +int32_t IndexSubTable::Builder::SubSerialize(WritableFontData* new_data) { + UNREFERENCED_PARAMETER(new_data); + return 0; +} + +IndexSubTable::Builder::Builder(int32_t data_size, int32_t index_format) + : SubTable::Builder(data_size), + first_glyph_index_(0), + last_glyph_index_(0), + index_format_(index_format), + image_format_(0), + image_data_offset_(0) { +} + +IndexSubTable::Builder::Builder(int32_t index_format, + int32_t image_format, + int32_t image_data_offset, + int32_t data_size) + : SubTable::Builder(data_size), + first_glyph_index_(0), + last_glyph_index_(0), + index_format_(index_format), + image_format_(image_format), + image_data_offset_(image_data_offset) { +} + +IndexSubTable::Builder::Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : SubTable::Builder(data), + first_glyph_index_(first_glyph_index), + last_glyph_index_(last_glyph_index) { + Initialize(data); +} + +IndexSubTable::Builder::Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : SubTable::Builder(data), + first_glyph_index_(first_glyph_index), + last_glyph_index_(last_glyph_index) { + Initialize(data); +} + +int32_t IndexSubTable::Builder::CheckGlyphRange(int32_t glyph_id) { + return IndexSubTable::CheckGlyphRange(glyph_id, + first_glyph_index(), + last_glyph_index()); +} + +int32_t IndexSubTable::Builder::SerializeIndexSubHeader( + WritableFontData* data) { + int32_t size = + data->WriteUShort(EblcTable::Offset::kIndexSubHeader_indexFormat, + index_format()); + size += data->WriteUShort(EblcTable::Offset::kIndexSubHeader_imageFormat, + image_format()); + size += data->WriteULong(EblcTable::Offset::kIndexSubHeader_imageDataOffset, + image_data_offset()); + return size; +} + +void IndexSubTable::Builder::Initialize(ReadableFontData* data) { + index_format_ = + data->ReadUShort(EblcTable::Offset::kIndexSubHeader_indexFormat); + image_format_ = + data->ReadUShort(EblcTable::Offset::kIndexSubHeader_imageFormat); + image_data_offset_ = + data->ReadULongAsInt(EblcTable::Offset::kIndexSubHeader_imageDataOffset); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table.h b/src/sfntly/src/sfntly/table/bitmap/index_sub_table.h new file mode 100644 index 0000000000..6d27129ccd --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table.h @@ -0,0 +1,178 @@ +/* + * 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_INDEX_SUBTABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_H_ + +#include + +#include "sfntly/port/java_iterator.h" +#include "sfntly/table/subtable.h" +#include "sfntly/table/bitmap/bitmap_glyph_info.h" + +namespace sfntly { + +class IndexSubTable : public SubTable { + public: + struct Format { + enum { + FORMAT_1 = 1, + FORMAT_2 = 2, + FORMAT_3 = 3, + FORMAT_4 = 4, + FORMAT_5 = 5, + }; + }; + + class Builder : public SubTable::Builder { + public: + virtual ~Builder(); + + void Revert(); + + int32_t index_format() { return index_format_; } + int32_t first_glyph_index() { return first_glyph_index_; } + void set_first_glyph_index(int32_t v) { first_glyph_index_ = v; } + int32_t last_glyph_index() { return last_glyph_index_; } + void set_last_glyph_index(int32_t v) { last_glyph_index_ = v; } + int32_t image_format() { return image_format_; } + void set_image_format(int32_t v) { image_format_ = v; } + int32_t image_data_offset() { return image_data_offset_; } + void set_image_data_offset(int32_t v) { image_data_offset_ = v; } + + virtual int32_t NumGlyphs() = 0; + + // Gets the glyph info for the specified glyph id. + // @param glyphId the glyph id to look up + // @return the glyph info + CALLER_ATTACH virtual BitmapGlyphInfo* GlyphInfo(int32_t glyph_id); + + // Gets the full offset of the glyph within the EBDT table. + // @param glyphId the glyph id + // @return the glyph offset + virtual int32_t GlyphOffset(int32_t glyph_id); + + // Gets the offset of the glyph relative to the block for this index + // subtable. + // @param glyphId the glyph id + // @return the glyph offset + virtual int32_t GlyphStartOffset(int32_t glyph_id) = 0; + + // Gets the length of the glyph within the EBDT table. + // @param glyphId the glyph id + // @return the glyph offset + virtual int32_t GlyphLength(int32_t glyph_id) = 0; + + // Note: renamed from java iterator() + CALLER_ATTACH virtual Iterator* + GetIterator() = 0; + + // Static instantiation function. + static CALLER_ATTACH Builder* CreateBuilder(int32_t index_format); + static CALLER_ATTACH Builder* + CreateBuilder(ReadableFontData* data, + int32_t offset_to_index_sub_table_array, + int32_t array_index); + + // The following methods will never be called but they need to be here to + // allow the BitmapSizeTable to see these methods through an abstract + // reference. + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + protected: + Builder(int32_t data_size, int32_t index_format); + Builder(int32_t index_format, + int32_t image_format, + int32_t image_data_offset, + int32_t data_size); + Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + + // Checks that the glyph id is within the correct range. If it returns the + // offset of the glyph id from the start of the range. + // @param glyphId + // @return the offset of the glyphId from the start of the glyph range + // @throws IndexOutOfBoundsException if the glyph id is not within the + // correct range + int32_t CheckGlyphRange(int32_t glyph_id); + int32_t SerializeIndexSubHeader(WritableFontData* data); + + private: + void Initialize(ReadableFontData* data); + + int32_t first_glyph_index_; + int32_t last_glyph_index_; + int32_t index_format_; + int32_t image_format_; + int32_t image_data_offset_; + }; + + int32_t index_format() { return index_format_; } + int32_t first_glyph_index() { return first_glyph_index_; } + int32_t last_glyph_index() { return last_glyph_index_; } + int32_t image_format() { return image_format_; } + int32_t image_data_offset() { return image_data_offset_; } + + CALLER_ATTACH BitmapGlyphInfo* GlyphInfo(int32_t glyph_id); + virtual int32_t GlyphOffset(int32_t glyph_id); + virtual int32_t GlyphStartOffset(int32_t glyph_id) = 0; + virtual int32_t GlyphLength(int32_t glyph_id) = 0; + virtual int32_t NumGlyphs() = 0; + + static CALLER_ATTACH IndexSubTable* + CreateIndexSubTable(ReadableFontData* data, + int32_t offset_to_index_sub_table_array, + int32_t array_index); + + protected: + // Note: the constructor does not implement offset/length form provided in + // Java to avoid heavy lifting in constructors. Callers to call + // GetDataLength() static method of the derived class to get proper + // length and slice ahead. + IndexSubTable(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + + int32_t CheckGlyphRange(int32_t glyph_id); + static int32_t CheckGlyphRange(int32_t glyph_id, + int32_t first_glyph_id, + int32_t last_glyph_id); + + private: + int32_t first_glyph_index_; + int32_t last_glyph_index_; + int32_t index_format_; + int32_t image_format_; + int32_t image_data_offset_; +}; +typedef Ptr IndexSubTablePtr; +typedef std::vector IndexSubTableList; +typedef Ptr IndexSubTableBuilderPtr; +typedef std::vector IndexSubTableBuilderList; +typedef Iterator BitmapGlyphInfoIter; +typedef Ptr BitmapGlyphInfoIterPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.cc b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.cc new file mode 100644 index 0000000000..db73723916 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.cc @@ -0,0 +1,299 @@ +/* + * 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/index_sub_table_format1.h" + +#include "sfntly/table/bitmap/eblc_table.h" + +namespace sfntly { +/****************************************************************************** + * IndexSubTableFormat1 class + ******************************************************************************/ +// static +int32_t IndexSubTableFormat1::GetDataLength(ReadableFontData* data, + int32_t offset, + int32_t first, + int32_t last) { + UNREFERENCED_PARAMETER(data); + UNREFERENCED_PARAMETER(offset); + return (last - first + 1 + 1) * DataSize::kULONG; +} + +IndexSubTableFormat1::~IndexSubTableFormat1() { +} + +int32_t IndexSubTableFormat1::NumGlyphs() { + return last_glyph_index() - first_glyph_index() + 1; +} + +int32_t IndexSubTableFormat1::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + return Loca(loca); +} + +int32_t IndexSubTableFormat1::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + return Loca(loca + 1) - Loca(loca); +} + +IndexSubTableFormat1::IndexSubTableFormat1(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable(data, first_glyph_index, last_glyph_index) { +} + +int32_t IndexSubTableFormat1::Loca(int32_t loca) { + return image_data_offset() + + data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable1_offsetArray + + loca * DataSize::kULONG); +} + +/****************************************************************************** + * IndexSubTableFormat1::Builder class + ******************************************************************************/ +IndexSubTableFormat1::Builder::~Builder() { +} + +int32_t IndexSubTableFormat1::Builder::NumGlyphs() { + return GetOffsetArray()->size() - 1; +} + +int32_t IndexSubTableFormat1::Builder::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return 0; + } + IntegerList* offset_array = GetOffsetArray(); + return offset_array->at(loca + 1) - offset_array->at(loca); +} + +int32_t IndexSubTableFormat1::Builder::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + return GetOffsetArray()->at(loca); +} + +CALLER_ATTACH IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat1::Builder::GetIterator() { + Ptr it = + new IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator(this); + return it.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat1::Builder* +IndexSubTableFormat1::Builder::CreateBuilder() { + IndexSubTableFormat1BuilderPtr output = new IndexSubTableFormat1::Builder(); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat1::Builder* +IndexSubTableFormat1::Builder::CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat1BuilderPtr output = + new IndexSubTableFormat1::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + + +// static +CALLER_ATTACH IndexSubTableFormat1::Builder* +IndexSubTableFormat1::Builder::CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + WritableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat1BuilderPtr output = + new IndexSubTableFormat1::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +CALLER_ATTACH FontDataTable* IndexSubTableFormat1::Builder::SubBuildTable( + ReadableFontData* data) { + IndexSubTableFormat1Ptr output = new IndexSubTableFormat1( + data, first_glyph_index(), last_glyph_index()); + return output.Detach(); +} + +void IndexSubTableFormat1::Builder::SubDataSet() { + Revert(); +} + +int32_t IndexSubTableFormat1::Builder::SubDataSizeToSerialize() { + if (offset_array_.empty()) { + return InternalReadData()->Length(); + } + return EblcTable::Offset::kIndexSubHeaderLength + + offset_array_.size() * DataSize::kULONG; +} + +bool IndexSubTableFormat1::Builder::SubReadyToSerialize() { + if (!offset_array_.empty()) { + return true; + } + return false; +} + +int32_t IndexSubTableFormat1::Builder::SubSerialize( + WritableFontData* new_data) { + int32_t size = SerializeIndexSubHeader(new_data); + if (!model_changed()) { + if (InternalReadData() == NULL) { + return size; + } + ReadableFontDataPtr source; + WritableFontDataPtr target; + source.Attach(down_cast(InternalReadData()->Slice( + EblcTable::Offset::kIndexSubTable1_offsetArray))); + target.Attach(down_cast(new_data->Slice( + EblcTable::Offset::kIndexSubTable1_offsetArray))); + size += source->CopyTo(target); + } else { + for (IntegerList::iterator b = GetOffsetArray()->begin(), + e = GetOffsetArray()->end(); b != e; b++) { + size += new_data->WriteLong(size, *b); + } + } + return size; +} + +IntegerList* IndexSubTableFormat1::Builder::OffsetArray() { + return GetOffsetArray(); +} + +void IndexSubTableFormat1::Builder::SetOffsetArray( + const IntegerList& offset_array) { + offset_array_.clear(); + offset_array_ = offset_array; + set_model_changed(); +} + +void IndexSubTableFormat1::Builder::Revert() { + offset_array_.clear(); + IndexSubTable::Builder::Revert(); +} + +IndexSubTableFormat1::Builder::Builder() + : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable1_builderDataSize, + IndexSubTable::Format::FORMAT_1) { +} + +IndexSubTableFormat1::Builder::Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IndexSubTableFormat1::Builder::Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IntegerList* IndexSubTableFormat1::Builder::GetOffsetArray() { + if (offset_array_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &offset_array_; +} + +void IndexSubTableFormat1::Builder::Initialize(ReadableFontData* data) { + offset_array_.clear(); + if (data) { + int32_t num_offsets = (last_glyph_index() - first_glyph_index() + 1) + 1; + for (int32_t i = 0; i < num_offsets; ++i) { + offset_array_.push_back(data->ReadULongAsInt( + EblcTable::Offset::kIndexSubTable1_offsetArray + + i * DataSize::kULONG)); + } + } +} + +// static +int32_t IndexSubTableFormat1::Builder::DataLength( + ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + UNREFERENCED_PARAMETER(data); + UNREFERENCED_PARAMETER(index_sub_table_offset); + return EblcTable::Offset::kIndexSubHeaderLength + + (last_glyph_index - first_glyph_index + 1 + 1) * DataSize::kULONG; +} + +/****************************************************************************** + * IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator class + ******************************************************************************/ +IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator( + IndexSubTableFormat1::Builder* container) + : RefIterator(container) { + glyph_id_ = container->first_glyph_index(); +} + +bool IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator::HasNext() { + if (glyph_id_ <= container()->last_glyph_index()) { + return true; + } + return false; +} + +CALLER_ATTACH BitmapGlyphInfo* +IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator::Next() { + BitmapGlyphInfoPtr output; + if (!HasNext()) { + // Note: In C++, we do not throw exception when there's no element. + return NULL; + } + output = new BitmapGlyphInfo(glyph_id_, + container()->image_data_offset(), + container()->GlyphStartOffset(glyph_id_), + container()->GlyphLength(glyph_id_), + container()->image_format()); + glyph_id_++; + return output.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.h b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.h new file mode 100644 index 0000000000..33171c1561 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format1.h @@ -0,0 +1,116 @@ +/* + * 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_INDEX_SUBTABLE_FORMAT1_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_ + +#include "sfntly/port/java_iterator.h" +#include "sfntly/table/bitmap/index_sub_table.h" + +namespace sfntly { +// Format 1 Index Subtable Entry. +class IndexSubTableFormat1 : public IndexSubTable, + public RefCounted { + public: + class Builder : public IndexSubTable::Builder, + public RefCounted { + public: + class BitmapGlyphInfoIterator + : public RefIterator { + public: + explicit BitmapGlyphInfoIterator(Builder* container); + virtual ~BitmapGlyphInfoIterator() {} + + virtual bool HasNext(); + CALLER_ATTACH virtual BitmapGlyphInfo* Next(); + + private: + int32_t glyph_id_; + }; + + virtual ~Builder(); + virtual int32_t NumGlyphs(); + virtual int32_t GlyphLength(int32_t glyph_id); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + IntegerList* OffsetArray(); + void SetOffsetArray(const IntegerList& offset_array); + CALLER_ATTACH BitmapGlyphInfoIter* Iterator(); + + static CALLER_ATTACH Builder* CreateBuilder(); + static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + protected: + void Revert(); + + private: + Builder(); + Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + IntegerList* GetOffsetArray(); + void Initialize(ReadableFontData* data); + + static int32_t DataLength(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + IntegerList offset_array_; + }; + + virtual ~IndexSubTableFormat1(); + + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + + static int32_t GetDataLength(ReadableFontData* data, + int32_t offset, + int32_t first, + int32_t last); + + private: + IndexSubTableFormat1(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + int32_t Loca(int32_t loca_index); + + friend class Builder; +}; +typedef Ptr IndexSubTableFormat1Ptr; +typedef Ptr IndexSubTableFormat1BuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.cc b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.cc new file mode 100644 index 0000000000..b3bffdad91 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.cc @@ -0,0 +1,272 @@ +/* + * 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/index_sub_table_format2.h" + +#include "sfntly/table/bitmap/eblc_table.h" + +namespace sfntly { +/****************************************************************************** + * IndexSubTableFormat2 class + ******************************************************************************/ +IndexSubTableFormat2::~IndexSubTableFormat2() { +} + +int32_t IndexSubTableFormat2::ImageSize() { + return data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable2_imageSize); +} + +CALLER_ATTACH BigGlyphMetrics* IndexSubTableFormat2::BigMetrics() { + ReadableFontDataPtr slice; + slice.Attach(down_cast( + data_->Slice(EblcTable::Offset::kIndexSubTable2_bigGlyphMetrics, + BigGlyphMetrics::Offset::kMetricsLength))); + BigGlyphMetricsPtr output = new BigGlyphMetrics(slice); + return output.Detach(); +} + +int32_t IndexSubTableFormat2::NumGlyphs() { + return last_glyph_index() - first_glyph_index() + 1; +} + +int32_t IndexSubTableFormat2::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + return loca * image_size_; +} + +int32_t IndexSubTableFormat2::GlyphLength(int32_t glyph_id) { + if (CheckGlyphRange(glyph_id) == -1) { + return 0; + } + return image_size_; +} + +IndexSubTableFormat2::IndexSubTableFormat2(ReadableFontData* data, + int32_t first, + int32_t last) + : IndexSubTable(data, first, last) { + image_size_ = + data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable2_imageSize); +} + +/****************************************************************************** + * IndexSubTableFormat2::Builder class + ******************************************************************************/ +IndexSubTableFormat2::Builder::~Builder() { +} + +int32_t IndexSubTableFormat2::Builder::NumGlyphs() { + return last_glyph_index() - first_glyph_index() + 1; +} + +int32_t IndexSubTableFormat2::Builder::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + return loca * ImageSize(); +} + +int32_t IndexSubTableFormat2::Builder::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return 0; + } + return ImageSize(); +} + +CALLER_ATTACH IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat2::Builder::GetIterator() { + Ptr it = + new IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator(this); + return it.Detach(); +} + +int32_t IndexSubTableFormat2::Builder::ImageSize() { + return InternalReadData()->ReadULongAsInt( + EblcTable::Offset::kIndexSubTable2_imageSize); +} + +void IndexSubTableFormat2::Builder::SetImageSize(int32_t image_size) { + InternalWriteData()->WriteULong(EblcTable::Offset::kIndexSubTable2_imageSize, + image_size); +} + +BigGlyphMetrics::Builder* IndexSubTableFormat2::Builder::BigMetrics() { + if (metrics_ == NULL) { + WritableFontDataPtr data; + data.Attach(down_cast(InternalWriteData()->Slice( + EblcTable::Offset::kIndexSubTable2_bigGlyphMetrics, + BigGlyphMetrics::Offset::kMetricsLength))); + metrics_ = new BigGlyphMetrics::Builder(data); + } + return metrics_; +} + +// static +CALLER_ATTACH IndexSubTableFormat2::Builder* +IndexSubTableFormat2::Builder::CreateBuilder() { + IndexSubTableFormat2BuilderPtr output = new IndexSubTableFormat2::Builder(); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat2::Builder* +IndexSubTableFormat2::Builder::CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat2BuilderPtr output = + new IndexSubTableFormat2::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat2::Builder* +IndexSubTableFormat2::Builder::CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + WritableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat2BuilderPtr output = + new IndexSubTableFormat2::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +CALLER_ATTACH FontDataTable* IndexSubTableFormat2::Builder::SubBuildTable( + ReadableFontData* data) { + IndexSubTableFormat2Ptr output = new IndexSubTableFormat2( + data, first_glyph_index(), last_glyph_index()); + return output.Detach(); +} + +void IndexSubTableFormat2::Builder::SubDataSet() { + Revert(); +} + +int32_t IndexSubTableFormat2::Builder::SubDataSizeToSerialize() { + return EblcTable::Offset::kIndexSubTable2Length; +} + +bool IndexSubTableFormat2::Builder::SubReadyToSerialize() { + return true; +} + +int32_t IndexSubTableFormat2::Builder::SubSerialize( + WritableFontData* new_data) { + int32_t size = SerializeIndexSubHeader(new_data); + if (metrics_ == NULL) { + ReadableFontDataPtr source; + WritableFontDataPtr target; + source.Attach(down_cast( + InternalReadData()->Slice(size))); + target.Attach(down_cast(new_data->Slice(size))); + size += source->CopyTo(target); + } else { + WritableFontDataPtr slice; + size += new_data->WriteLong(EblcTable::Offset::kIndexSubTable2_imageSize, + ImageSize()); + slice.Attach(down_cast(new_data->Slice(size))); + size += metrics_->SubSerialize(slice); + } + return size; +} + +IndexSubTableFormat2::Builder::Builder() + : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable3_builderDataSize, + IndexSubTable::Format::FORMAT_2) { + metrics_.Attach(BigGlyphMetrics::Builder::CreateBuilder()); +} + +IndexSubTableFormat2::Builder::Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IndexSubTableFormat2::Builder::Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +// static +int32_t IndexSubTableFormat2::Builder::DataLength( + ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + UNREFERENCED_PARAMETER(data); + UNREFERENCED_PARAMETER(index_sub_table_offset); + UNREFERENCED_PARAMETER(first_glyph_index); + UNREFERENCED_PARAMETER(last_glyph_index); + return EblcTable::Offset::kIndexSubTable2Length; +} + +/****************************************************************************** + * IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator class + ******************************************************************************/ +IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator( + IndexSubTableFormat2::Builder* container) + : RefIterator(container) { + glyph_id_ = container->first_glyph_index(); +} + +bool IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator::HasNext() { + if (glyph_id_ <= container()->last_glyph_index()) { + return true; + } + return false; +} + +CALLER_ATTACH BitmapGlyphInfo* +IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator::Next() { + BitmapGlyphInfoPtr output; + if (!HasNext()) { + // Note: In C++, we do not throw exception when there's no element. + return NULL; + } + output = new BitmapGlyphInfo(glyph_id_, + container()->image_data_offset(), + container()->GlyphStartOffset(glyph_id_), + container()->GlyphLength(glyph_id_), + container()->image_format()); + glyph_id_++; + return output.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.h b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.h new file mode 100644 index 0000000000..784e8a39fe --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format2.h @@ -0,0 +1,106 @@ +/* + * 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_INDEX_SUBTABLE_FORMAT2_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT2_H_ + +#include "sfntly/table/bitmap/index_sub_table.h" +#include "sfntly/table/bitmap/big_glyph_metrics.h" + +namespace sfntly { +// Format 2 Index Subtable Entry. +class IndexSubTableFormat2 : public IndexSubTable, + public RefCounted { + public: + class Builder : public IndexSubTable::Builder, + public RefCounted { + public: + class BitmapGlyphInfoIterator + : public RefIterator { + public: + explicit BitmapGlyphInfoIterator(Builder* container); + virtual ~BitmapGlyphInfoIterator() {} + + virtual bool HasNext(); + CALLER_ATTACH virtual BitmapGlyphInfo* Next(); + + private: + int32_t glyph_id_; + }; + + virtual ~Builder(); + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); + + 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 ImageSize(); + void SetImageSize(int32_t image_size); + BigGlyphMetrics::Builder* BigMetrics(); + + static CALLER_ATTACH Builder* CreateBuilder(); + static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + private: + Builder(); + Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + + static int32_t DataLength(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + BigGlyphMetricsBuilderPtr metrics_; + }; + + virtual ~IndexSubTableFormat2(); + + int32_t ImageSize(); + CALLER_ATTACH BigGlyphMetrics* BigMetrics(); + + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + + private: + IndexSubTableFormat2(ReadableFontData* data, int32_t first, int32_t last); + + int32_t image_size_; + friend class Builder; +}; +typedef Ptr IndexSubTableFormat2Ptr; +typedef Ptr IndexSubTableFormat2BuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.cc b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.cc new file mode 100644 index 0000000000..b3e418ff54 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.cc @@ -0,0 +1,295 @@ +/* + * 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/index_sub_table_format3.h" + +#include "sfntly/table/bitmap/eblc_table.h" + +namespace sfntly { +/****************************************************************************** + * IndexSubTableFormat3 class + ******************************************************************************/ +IndexSubTableFormat3::~IndexSubTableFormat3() { +} + +int32_t IndexSubTableFormat3::NumGlyphs() { + return last_glyph_index() - first_glyph_index() + 1; +} + +int32_t IndexSubTableFormat3::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca != -1) { + return Loca(loca); + } + return -1; +} + +int32_t IndexSubTableFormat3::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca != -1) { + return Loca(glyph_id + 1) - Loca(glyph_id); + } + return 0; +} + +// static +int32_t IndexSubTableFormat3::GetDataLength(ReadableFontData* data, + int32_t offset, + int32_t first, + int32_t last) { + UNREFERENCED_PARAMETER(data); + UNREFERENCED_PARAMETER(offset); + return (last - first + 1 + 1) * DataSize::kUSHORT; +} + +IndexSubTableFormat3::IndexSubTableFormat3(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable(data, first_glyph_index, last_glyph_index) { +} + +int32_t IndexSubTableFormat3::Loca(int32_t loca) { + int32_t read_offset = + data_->ReadUShort(EblcTable::Offset::kIndexSubTable3_offsetArray + + loca * DataSize::kUSHORT); + return read_offset; +} + +/****************************************************************************** + * IndexSubTableFormat3::Builder class + ******************************************************************************/ +IndexSubTableFormat3::Builder::~Builder() { +} + +int32_t IndexSubTableFormat3::Builder::NumGlyphs() { + return GetOffsetArray()->size() - 1; +} + +int32_t IndexSubTableFormat3::Builder::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + return GetOffsetArray()->at(loca); +} + +int32_t IndexSubTableFormat3::Builder::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return 0; + } + IntegerList* offset_array = GetOffsetArray(); + return offset_array->at(loca + 1) - offset_array->at(loca); +} + +CALLER_ATTACH IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat3::Builder::GetIterator() { + Ptr it = + new IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator(this); + return it.Detach(); +} + +void IndexSubTableFormat3::Builder::Revert() { + offset_array_.clear(); + IndexSubTable::Builder::Revert(); +} + +void IndexSubTableFormat3::Builder::SetOffsetArray( + const IntegerList& offset_array) { + offset_array_.clear(); + offset_array_ = offset_array; + set_model_changed(); +} + +// static +CALLER_ATTACH IndexSubTableFormat3::Builder* +IndexSubTableFormat3::Builder::CreateBuilder() { + IndexSubTableFormat3BuilderPtr output = new IndexSubTableFormat3::Builder(); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat3::Builder* +IndexSubTableFormat3::Builder::CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat3BuilderPtr output = + new IndexSubTableFormat3::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat3::Builder* +IndexSubTableFormat3::Builder::CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + WritableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat3BuilderPtr output = + new IndexSubTableFormat3::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +CALLER_ATTACH FontDataTable* IndexSubTableFormat3::Builder::SubBuildTable( + ReadableFontData* data) { + IndexSubTableFormat3Ptr output = new IndexSubTableFormat3( + data, first_glyph_index(), last_glyph_index()); + return output.Detach(); +} + +void IndexSubTableFormat3::Builder::SubDataSet() { + Revert(); +} + +int32_t IndexSubTableFormat3::Builder::SubDataSizeToSerialize() { + if (offset_array_.empty()) { + return InternalReadData()->Length(); + } + return EblcTable::Offset::kIndexSubHeaderLength + + offset_array_.size() * DataSize::kULONG; +} + +bool IndexSubTableFormat3::Builder::SubReadyToSerialize() { + if (!offset_array_.empty()) { + return true; + } + return false; +} + +int32_t IndexSubTableFormat3::Builder::SubSerialize( + WritableFontData* new_data) { + int32_t size = SerializeIndexSubHeader(new_data); + if (!model_changed()) { + if (InternalReadData() == NULL) { + return size; + } + ReadableFontDataPtr source; + WritableFontDataPtr target; + source.Attach(down_cast(InternalReadData()->Slice( + EblcTable::Offset::kIndexSubTable3_offsetArray))); + target.Attach(down_cast(new_data->Slice( + EblcTable::Offset::kIndexSubTable3_offsetArray))); + size += source->CopyTo(target); + } else { + for (IntegerList::iterator b = GetOffsetArray()->begin(), + e = GetOffsetArray()->end(); b != e; b++) { + size += new_data->WriteUShort(size, *b); + } + } + return size; +} + +IndexSubTableFormat3::Builder::Builder() + : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable3_builderDataSize, + IndexSubTable::Format::FORMAT_3) { +} + +IndexSubTableFormat3::Builder::Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IndexSubTableFormat3::Builder::Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IntegerList* IndexSubTableFormat3::Builder::GetOffsetArray() { + if (offset_array_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &offset_array_; +} + +void IndexSubTableFormat3::Builder::Initialize(ReadableFontData* data) { + offset_array_.clear(); + if (data) { + int32_t num_offsets = (last_glyph_index() - first_glyph_index() + 1) + 1; + for (int32_t i = 0; i < num_offsets; ++i) { + offset_array_.push_back(data->ReadUShort( + EblcTable::Offset::kIndexSubTable3_offsetArray + + i * DataSize::kUSHORT)); + } + } +} + +// static +int32_t IndexSubTableFormat3::Builder::DataLength( + ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + UNREFERENCED_PARAMETER(data); + UNREFERENCED_PARAMETER(index_sub_table_offset); + return EblcTable::Offset::kIndexSubHeaderLength + + (last_glyph_index - first_glyph_index + 1 + 1) * DataSize::kUSHORT; +} + +/****************************************************************************** + * IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator class + ******************************************************************************/ +IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator( + IndexSubTableFormat3::Builder* container) + : RefIterator(container) { + glyph_id_ = container->first_glyph_index(); +} + +bool IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator::HasNext() { + if (glyph_id_ <= container()->last_glyph_index()) { + return true; + } + return false; +} + +CALLER_ATTACH BitmapGlyphInfo* +IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator::Next() { + BitmapGlyphInfoPtr output; + if (!HasNext()) { + // Note: In C++, we do not throw exception when there's no element. + return NULL; + } + output = new BitmapGlyphInfo(glyph_id_, + container()->image_data_offset(), + container()->GlyphStartOffset(glyph_id_), + container()->GlyphLength(glyph_id_), + container()->image_format()); + glyph_id_++; + return output.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.h b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.h new file mode 100644 index 0000000000..d71f8573cc --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format3.h @@ -0,0 +1,113 @@ +/* + * 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_INDEX_SUBTABLE_FORMAT3_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT3_H_ + +#include "sfntly/table/bitmap/index_sub_table.h" + +namespace sfntly { +// Format 3 Index Subtable Entry. +class IndexSubTableFormat3 : public IndexSubTable, + public RefCounted { + public: + class Builder : public IndexSubTable::Builder, + public RefCounted { + public: + class BitmapGlyphInfoIterator + : public RefIterator { + public: + explicit BitmapGlyphInfoIterator(Builder* container); + virtual ~BitmapGlyphInfoIterator() {} + + virtual bool HasNext(); + CALLER_ATTACH virtual BitmapGlyphInfo* Next(); + + private: + int32_t glyph_id_; + }; + + virtual ~Builder(); + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + void SetOffsetArray(const IntegerList& offset_array); + + static CALLER_ATTACH Builder* CreateBuilder(); + static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + protected: + void Revert(); + + private: + Builder(); + Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + IntegerList* GetOffsetArray(); + void Initialize(ReadableFontData* data); + + static int32_t DataLength(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + IntegerList offset_array_; + }; + + virtual ~IndexSubTableFormat3(); + + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + + static int32_t GetDataLength(ReadableFontData* data, + int32_t offset, + int32_t first, + int32_t last); + + private: + IndexSubTableFormat3(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + int32_t Loca(int32_t loca_index); + + friend class Builder; +}; +typedef Ptr IndexSubTableFormat3Ptr; +typedef Ptr IndexSubTableFormat3BuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT3_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.cc b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.cc new file mode 100644 index 0000000000..23f3e47406 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.cc @@ -0,0 +1,381 @@ +/* + * 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/index_sub_table_format4.h" + +#include "sfntly/table/bitmap/eblc_table.h" + +namespace sfntly { +/****************************************************************************** + * IndexSubTableFormat4 class + ******************************************************************************/ +IndexSubTableFormat4::~IndexSubTableFormat4() { +} + +int32_t IndexSubTableFormat4::NumGlyphs() { + return IndexSubTableFormat4::NumGlyphs(data_, 0); +} + +int32_t IndexSubTableFormat4::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + int32_t pair_index = FindCodeOffsetPair(glyph_id); + if (pair_index < 0) { + return -1; + } + return data_->ReadUShort(EblcTable::Offset::kIndexSubTable4_glyphArray + + pair_index * + EblcTable::Offset::kCodeOffsetPairLength + + EblcTable::Offset::kCodeOffsetPair_offset); +} + +int32_t IndexSubTableFormat4::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + + int32_t pair_index = FindCodeOffsetPair(glyph_id); + if (pair_index < 0) { + return -1; + } + return data_->ReadUShort( + EblcTable::Offset::kIndexSubTable4_glyphArray + + (pair_index + 1) * EblcTable::Offset::kCodeOffsetPairLength + + EblcTable::Offset::kCodeOffsetPair_offset) - + data_->ReadUShort( + EblcTable::Offset::kIndexSubTable4_glyphArray + + (pair_index) * EblcTable::Offset::kCodeOffsetPairLength + + EblcTable::Offset::kCodeOffsetPair_offset); +} + +IndexSubTableFormat4::IndexSubTableFormat4(ReadableFontData* data, + int32_t first, + int32_t last) + : IndexSubTable(data, first, last) { +} + +int32_t IndexSubTableFormat4::FindCodeOffsetPair(int32_t glyph_id) { + return data_->SearchUShort(EblcTable::Offset::kIndexSubTable4_glyphArray, + EblcTable::Offset::kCodeOffsetPairLength, + NumGlyphs(), + glyph_id); +} + +int32_t IndexSubTableFormat4::NumGlyphs(ReadableFontData* data, + int32_t table_offset) { + int32_t num_glyphs = data->ReadULongAsInt(table_offset + + EblcTable::Offset::kIndexSubTable4_numGlyphs); + return num_glyphs; +} + +/****************************************************************************** + * IndexSubTableFormat4::CodeOffsetPair related class + ******************************************************************************/ +IndexSubTableFormat4::CodeOffsetPair::CodeOffsetPair(int32_t glyph_code, + int32_t offset) + : glyph_code_(glyph_code), offset_(offset) { +} + +IndexSubTableFormat4::CodeOffsetPairBuilder::CodeOffsetPairBuilder() + : CodeOffsetPair(0, 0) { +} + +IndexSubTableFormat4::CodeOffsetPairBuilder::CodeOffsetPairBuilder( + int32_t glyph_code, int32_t offset) + : CodeOffsetPair(glyph_code, offset) { +} + +bool IndexSubTableFormat4::CodeOffsetPairGlyphCodeComparator::operator()( + const CodeOffsetPair& lhs, const CodeOffsetPair& rhs) { + return lhs.glyph_code() < rhs.glyph_code(); +} + +/****************************************************************************** + * IndexSubTableFormat4::Builder class + ******************************************************************************/ +IndexSubTableFormat4::Builder::~Builder() { +} + +int32_t IndexSubTableFormat4::Builder::NumGlyphs() { + return GetOffsetArray()->size() - 1; +} + +int32_t IndexSubTableFormat4::Builder::GlyphLength(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return 0; + } + int32_t pair_index = FindCodeOffsetPair(glyph_id); + if (pair_index == -1) { + return 0; + } + return GetOffsetArray()->at(pair_index + 1).offset() - + GetOffsetArray()->at(pair_index).offset(); +} + +int32_t IndexSubTableFormat4::Builder::GlyphStartOffset(int32_t glyph_id) { + int32_t loca = CheckGlyphRange(glyph_id); + if (loca == -1) { + return -1; + } + int32_t pair_index = FindCodeOffsetPair(glyph_id); + if (pair_index == -1) { + return -1; + } + return GetOffsetArray()->at(pair_index).offset(); +} + +CALLER_ATTACH IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat4::Builder::GetIterator() { + Ptr it = + new IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator(this); + return it.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat4::Builder* +IndexSubTableFormat4::Builder::CreateBuilder() { + IndexSubTableFormat4BuilderPtr output = new IndexSubTableFormat4::Builder(); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat4::Builder* +IndexSubTableFormat4::Builder::CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat4BuilderPtr output = + new IndexSubTableFormat4::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat4::Builder* +IndexSubTableFormat4::Builder::CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + WritableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat4BuilderPtr output = + new IndexSubTableFormat4::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +CALLER_ATTACH FontDataTable* IndexSubTableFormat4::Builder::SubBuildTable( + ReadableFontData* data) { + IndexSubTableFormat4Ptr output = new IndexSubTableFormat4( + data, first_glyph_index(), last_glyph_index()); + return output.Detach(); +} + +void IndexSubTableFormat4::Builder::SubDataSet() { + Revert(); +} + +int32_t IndexSubTableFormat4::Builder::SubDataSizeToSerialize() { + if (offset_pair_array_.empty()) { + return InternalReadData()->Length(); + } + return EblcTable::Offset::kIndexSubHeaderLength + DataSize::kULONG + + GetOffsetArray()->size() * + EblcTable::Offset::kIndexSubTable4_codeOffsetPairLength; +} + +bool IndexSubTableFormat4::Builder::SubReadyToSerialize() { + if (!offset_pair_array_.empty()) { + return true; + } + return false; +} + +int32_t IndexSubTableFormat4::Builder::SubSerialize( + WritableFontData* new_data) { + int32_t size = SerializeIndexSubHeader(new_data); + if (!model_changed()) { + if (InternalReadData() == NULL) { + return size; + } + ReadableFontDataPtr source; + WritableFontDataPtr target; + source.Attach(down_cast(InternalReadData()->Slice( + EblcTable::Offset::kIndexSubTable4_glyphArray))); + target.Attach(down_cast(new_data->Slice( + EblcTable::Offset::kIndexSubTable4_glyphArray))); + size += source->CopyTo(target); + } else { + size += new_data->WriteLong(size, offset_pair_array_.size() - 1); + for (std::vector::iterator + b = GetOffsetArray()->begin(), e = GetOffsetArray()->end(); + b != e; b++) { + size += new_data->WriteUShort(size, b->glyph_code()); + size += new_data->WriteUShort(size, b->offset()); + } + } + return size; +} + +void IndexSubTableFormat4::Builder::Revert() { + offset_pair_array_.clear(); + IndexSubTable::Builder::Revert(); +} + +void IndexSubTableFormat4::Builder::SetOffsetArray( + const std::vector& pair_array) { + offset_pair_array_.clear(); + offset_pair_array_ = pair_array; + set_model_changed(); +} + +IndexSubTableFormat4::Builder::Builder() + : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable4_builderDataSize, + Format::FORMAT_4) { +} + +IndexSubTableFormat4::Builder::Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IndexSubTableFormat4::Builder::Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +std::vector* +IndexSubTableFormat4::Builder::GetOffsetArray() { + if (offset_pair_array_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &offset_pair_array_; +} + +void IndexSubTableFormat4::Builder::Initialize(ReadableFontData* data) { + offset_pair_array_.clear(); + if (data) { + int32_t num_pairs = IndexSubTableFormat4::NumGlyphs(data, 0) + 1; + int32_t offset = EblcTable::Offset::kIndexSubTable4_glyphArray; + for (int32_t i = 0; i < num_pairs; ++i) { + int32_t glyph_code = data->ReadUShort(offset + + EblcTable::Offset::kIndexSubTable4_codeOffsetPair_glyphCode); + int32_t glyph_offset = data->ReadUShort(offset + + EblcTable::Offset::kIndexSubTable4_codeOffsetPair_offset); + offset += EblcTable::Offset::kIndexSubTable4_codeOffsetPairLength; + CodeOffsetPairBuilder pair_builder(glyph_code, glyph_offset); + offset_pair_array_.push_back(pair_builder); + } + } +} + +int32_t IndexSubTableFormat4::Builder::FindCodeOffsetPair(int32_t glyph_id) { + std::vector* pair_list = GetOffsetArray(); + int32_t location = 0; + int32_t bottom = 0; + int32_t top = pair_list->size(); + while (top != bottom) { + location = (top + bottom) / 2; + CodeOffsetPairBuilder* pair = &(pair_list->at(location)); + if (glyph_id < pair->glyph_code()) { + // location is below current location + top = location; + } else if (glyph_id > pair->glyph_code()) { + // location is above current location + bottom = location + 1; + } else { + return location; + } + } + return -1; +} + +// static +int32_t IndexSubTableFormat4::Builder::DataLength( + ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t num_glyphs = IndexSubTableFormat4::NumGlyphs(data, + index_sub_table_offset); + UNREFERENCED_PARAMETER(first_glyph_index); + UNREFERENCED_PARAMETER(last_glyph_index); + return EblcTable::Offset::kIndexSubTable4_glyphArray + + num_glyphs * EblcTable::Offset::kIndexSubTable4_codeOffsetPair_offset; +} + + +/****************************************************************************** + * IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator class + ******************************************************************************/ +IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator( + IndexSubTableFormat4::Builder* container) + : RefIterator(container), + code_offset_pair_index_(0) { +} + +bool IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator::HasNext() { + if (code_offset_pair_index_ < + (int32_t)(container()->GetOffsetArray()->size() - 1)) { + return true; + } + return false; +} + +CALLER_ATTACH BitmapGlyphInfo* +IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator::Next() { + BitmapGlyphInfoPtr output; + if (!HasNext()) { + // Note: In C++, we do not throw exception when there's no element. + return NULL; + } + std::vector* offset_array = + container()->GetOffsetArray(); + int32_t offset = offset_array->at(code_offset_pair_index_).offset(); + int32_t next_offset = offset_array->at(code_offset_pair_index_ + 1).offset(); + int32_t glyph_code = offset_array->at(code_offset_pair_index_).glyph_code(); + output = new BitmapGlyphInfo(glyph_code, + container()->image_data_offset(), + offset, + next_offset - offset, + container()->image_format()); + code_offset_pair_index_++; + return output.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.h b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.h new file mode 100644 index 0000000000..efd540f178 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format4.h @@ -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_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_ + +#include "sfntly/table/bitmap/index_sub_table.h" + +namespace sfntly { + +class IndexSubTableFormat4 : public IndexSubTable, + public RefCounted { + public: + class CodeOffsetPair { + public: + int32_t glyph_code() const { return glyph_code_; } + int32_t offset() const { return offset_; } + + protected: + CodeOffsetPair(int32_t glyph_code, int32_t offset); + + // TODO(arthurhsu): C++ style guide prohibits protected members. + int32_t glyph_code_; + int32_t offset_; + }; + + class CodeOffsetPairBuilder : public CodeOffsetPair { + public: + CodeOffsetPairBuilder(); + CodeOffsetPairBuilder(int32_t glyph_code, int32_t offset); + void set_glyph_code(int32_t v) { glyph_code_ = v; } + void set_offset(int32_t v) { offset_ = v; } + }; + + class CodeOffsetPairGlyphCodeComparator { + public: + bool operator()(const CodeOffsetPair& lhs, const CodeOffsetPair& rhs); + }; + + class Builder : public IndexSubTable::Builder, + public RefCounted { + public: + class BitmapGlyphInfoIterator + : public RefIterator { + public: + explicit BitmapGlyphInfoIterator(Builder* container); + virtual ~BitmapGlyphInfoIterator() {} + + virtual bool HasNext(); + CALLER_ATTACH virtual BitmapGlyphInfo* Next(); + + private: + int32_t code_offset_pair_index_; + }; + + virtual ~Builder(); + virtual int32_t NumGlyphs(); + virtual int32_t GlyphLength(int32_t glyph_id); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + void Revert(); + void SetOffsetArray(const std::vector& pair_array); + + static CALLER_ATTACH Builder* CreateBuilder(); + static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + private: + Builder(); + Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + std::vector* GetOffsetArray(); + void Initialize(ReadableFontData* data); + int32_t FindCodeOffsetPair(int32_t glyph_id); + + static int32_t DataLength(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + std::vector offset_pair_array_; + }; + + virtual ~IndexSubTableFormat4(); + + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + + private: + IndexSubTableFormat4(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + + int32_t FindCodeOffsetPair(int32_t glyph_id); + static int32_t NumGlyphs(ReadableFontData* data, int32_t table_offset); + + friend class Builder; +}; +typedef Ptr IndexSubTableFormat4Ptr; +typedef Ptr IndexSubTableFormat4BuilderPtr; +typedef std::vector + CodeOffsetPairBuilderList; +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.cc b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.cc new file mode 100644 index 0000000000..b4ab1b8e9e --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.cc @@ -0,0 +1,344 @@ +/* + * 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/index_sub_table_format5.h" + +#include + +#include "sfntly/table/bitmap/eblc_table.h" + +namespace sfntly { +/****************************************************************************** + * IndexSubTableFormat5 class + ******************************************************************************/ +IndexSubTableFormat5::~IndexSubTableFormat5() { +} + +int32_t IndexSubTableFormat5::NumGlyphs() { + return NumGlyphs(data_, 0); +} + +int32_t IndexSubTableFormat5::GlyphStartOffset(int32_t glyph_id) { + int32_t check = CheckGlyphRange(glyph_id); + if (check == -1) { + return -1; + } + int32_t loca = ReadFontData()->SearchUShort( + EblcTable::Offset::kIndexSubTable5_glyphArray, + DataSize::kUSHORT, + NumGlyphs(), + glyph_id); + if (loca == -1) { + return loca; + } + return loca * ImageSize(); +} + +int32_t IndexSubTableFormat5::GlyphLength(int32_t glyph_id) { + int32_t check = CheckGlyphRange(glyph_id); + if (check == -1) { + return 0; + } + return image_size_; +} + +int32_t IndexSubTableFormat5::ImageSize() { + return data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable5_imageSize); +} + +CALLER_ATTACH BigGlyphMetrics* IndexSubTableFormat5::BigMetrics() { + ReadableFontDataPtr data; + data.Attach(down_cast(data_->Slice( + EblcTable::Offset::kIndexSubTable5_bigGlyphMetrics, + BigGlyphMetrics::Offset::kMetricsLength))); + BigGlyphMetricsPtr output = new BigGlyphMetrics(data); + return output.Detach(); +} + +IndexSubTableFormat5::IndexSubTableFormat5(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable(data, first_glyph_index, last_glyph_index) { + image_size_ = data_->ReadULongAsInt( + EblcTable::Offset::kIndexSubTable5_imageSize); +} + +// static +int32_t IndexSubTableFormat5::NumGlyphs(ReadableFontData* data, + int32_t table_offset) { + int32_t num_glyphs = data->ReadULongAsInt(table_offset + + EblcTable::Offset::kIndexSubTable5_numGlyphs); + return num_glyphs; +} + +/****************************************************************************** + * IndexSubTableFormat5::Builder class + ******************************************************************************/ +IndexSubTableFormat5::Builder::~Builder() { +} + +int32_t IndexSubTableFormat5::Builder::NumGlyphs() { + return GetGlyphArray()->size(); +} + +int32_t IndexSubTableFormat5::Builder::GlyphLength(int32_t glyph_id) { + UNREFERENCED_PARAMETER(glyph_id); + return ImageSize(); +} + +int32_t IndexSubTableFormat5::Builder::GlyphStartOffset(int32_t glyph_id) { + int32_t check = CheckGlyphRange(glyph_id); + if (check == -1) { + return -1; + } + IntegerList* glyph_array = GetGlyphArray(); + IntegerList::iterator it = std::find(glyph_array->begin(), + glyph_array->end(), + glyph_id); + if (it == glyph_array->end()) { + return -1; + } + return (it - glyph_array->begin()) * ImageSize(); +} + +CALLER_ATTACH IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat5::Builder::GetIterator() { + Ptr it = + new IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator(this); + return it.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat5::Builder* +IndexSubTableFormat5::Builder::CreateBuilder() { + IndexSubTableFormat5BuilderPtr output = new IndexSubTableFormat5::Builder(); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat5::Builder* +IndexSubTableFormat5::Builder::CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + ReadableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat5BuilderPtr output = + new IndexSubTableFormat5::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +// static +CALLER_ATTACH IndexSubTableFormat5::Builder* +IndexSubTableFormat5::Builder::CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t length = Builder::DataLength(data, + index_sub_table_offset, + first_glyph_index, + last_glyph_index); + WritableFontDataPtr new_data; + new_data.Attach(down_cast( + data->Slice(index_sub_table_offset, length))); + IndexSubTableFormat5BuilderPtr output = + new IndexSubTableFormat5::Builder(new_data, + first_glyph_index, + last_glyph_index); + return output.Detach(); +} + +CALLER_ATTACH FontDataTable* IndexSubTableFormat5::Builder::SubBuildTable( + ReadableFontData* data) { + IndexSubTableFormat5Ptr output = new IndexSubTableFormat5( + data, first_glyph_index(), last_glyph_index()); + return output.Detach(); +} + +void IndexSubTableFormat5::Builder::SubDataSet() { + Revert(); +} + +int32_t IndexSubTableFormat5::Builder::SubDataSizeToSerialize() { + if (glyph_array_.empty()) { + return InternalReadData()->Length(); + } + return EblcTable::Offset::kIndexSubTable5_builderDataSize + + glyph_array_.size() * DataSize::kUSHORT; +} + +bool IndexSubTableFormat5::Builder::SubReadyToSerialize() { + if (!glyph_array_.empty()) { + return true; + } + return false; +} + +int32_t IndexSubTableFormat5::Builder::SubSerialize( + WritableFontData* new_data) { + int32_t size = SerializeIndexSubHeader(new_data); + if (!model_changed()) { + ReadableFontDataPtr source; + WritableFontDataPtr target; + source.Attach(down_cast(InternalReadData()->Slice( + EblcTable::Offset::kIndexSubTable5_imageSize))); + target.Attach(down_cast(new_data->Slice( + EblcTable::Offset::kIndexSubTable5_imageSize))); + size += source->CopyTo(target); + } else { + size += new_data->WriteULong(EblcTable::Offset::kIndexSubTable5_imageSize, + ImageSize()); + WritableFontDataPtr slice; + slice.Attach(down_cast(new_data->Slice(size))); + size += BigMetrics()->SubSerialize(slice); + size += new_data->WriteULong(size, glyph_array_.size()); + for (IntegerList::iterator b = glyph_array_.begin(), e = glyph_array_.end(); + b != e; b++) { + size += new_data->WriteUShort(size, *b); + } + } + return size; +} + +int32_t IndexSubTableFormat5::Builder::ImageSize() { + return InternalReadData()->ReadULongAsInt( + EblcTable::Offset::kIndexSubTable5_imageSize); +} + +void IndexSubTableFormat5::Builder::SetImageSize(int32_t image_size) { + InternalWriteData()->WriteULong( + EblcTable::Offset::kIndexSubTable5_imageSize, image_size); +} + +BigGlyphMetrics::Builder* IndexSubTableFormat5::Builder::BigMetrics() { + if (metrics_ == NULL) { + WritableFontDataPtr data; + data.Attach(down_cast(InternalWriteData()->Slice( + EblcTable::Offset::kIndexSubTable5_bigGlyphMetrics, + BigGlyphMetrics::Offset::kMetricsLength))); + metrics_ = new BigGlyphMetrics::Builder(data); + set_model_changed(); + } + return metrics_; +} + +IntegerList* IndexSubTableFormat5::Builder::GlyphArray() { + return GetGlyphArray(); +} + +void IndexSubTableFormat5::Builder::SetGlyphArray(const IntegerList& v) { + glyph_array_.clear(); + glyph_array_ = v; + set_model_changed(); +} + +void IndexSubTableFormat5::Builder::Revert() { + glyph_array_.clear(); + IndexSubTable::Builder::Revert(); +} + +IndexSubTableFormat5::Builder::Builder() + : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable5_builderDataSize, + IndexSubTable::Format::FORMAT_5) { +} + +IndexSubTableFormat5::Builder::Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IndexSubTableFormat5::Builder::Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index) + : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) { +} + +IntegerList* IndexSubTableFormat5::Builder::GetGlyphArray() { + if (glyph_array_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &glyph_array_; +} + +void IndexSubTableFormat5::Builder::Initialize(ReadableFontData* data) { + glyph_array_.clear(); + if (data) { + int32_t num_glyphs = IndexSubTableFormat5::NumGlyphs(data, 0); + for (int32_t i = 0; i < num_glyphs; ++i) { + glyph_array_.push_back(data->ReadUShort( + EblcTable::Offset::kIndexSubTable5_glyphArray + + i * DataSize::kUSHORT)); + } + } +} + +// static +int32_t IndexSubTableFormat5::Builder::DataLength( + ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index) { + int32_t num_glyphs = IndexSubTableFormat5::NumGlyphs(data, + index_sub_table_offset); + UNREFERENCED_PARAMETER(first_glyph_index); + UNREFERENCED_PARAMETER(last_glyph_index); + return EblcTable::Offset::kIndexSubTable5_glyphArray + + num_glyphs * DataSize::kUSHORT; +} + +/****************************************************************************** + * IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator class + ******************************************************************************/ +IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator( + IndexSubTableFormat5::Builder* container) + : RefIterator(container), + offset_index_(0) { +} + +bool IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator::HasNext() { + if (offset_index_ < (int32_t)(container()->GetGlyphArray()->size())) { + return true; + } + return false; +} + +CALLER_ATTACH BitmapGlyphInfo* +IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator::Next() { + BitmapGlyphInfoPtr output; + if (!HasNext()) { + // Note: In C++, we do not throw exception when there's no element. + return NULL; + } + output = new BitmapGlyphInfo(container()->GetGlyphArray()->at(offset_index_), + container()->image_data_offset(), + offset_index_ * container()->ImageSize(), + container()->ImageSize(), + container()->image_format()); + offset_index_++; + return output.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.h b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.h new file mode 100644 index 0000000000..a39e88caca --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/index_sub_table_format5.h @@ -0,0 +1,118 @@ +/* + * 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_INDEX_SUBTABLE_FORMAT5_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT5_H_ + +#include "sfntly/table/bitmap/big_glyph_metrics.h" +#include "sfntly/table/bitmap/index_sub_table.h" + +namespace sfntly { + +class IndexSubTableFormat5 : public IndexSubTable, + public RefCounted { + public: + class Builder : public IndexSubTable::Builder, + public RefCounted { + public: + class BitmapGlyphInfoIterator + : public RefIterator { + public: + explicit BitmapGlyphInfoIterator(Builder* container); + virtual ~BitmapGlyphInfoIterator() {} + + virtual bool HasNext(); + CALLER_ATTACH virtual BitmapGlyphInfo* Next(); + + private: + int32_t offset_index_; + }; + virtual ~Builder(); + virtual int32_t NumGlyphs(); + virtual int32_t GlyphLength(int32_t glyph_id); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); + + 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 ImageSize(); + void SetImageSize(int32_t image_size); + BigGlyphMetrics::Builder* BigMetrics(); + IntegerList* GlyphArray(); + void SetGlyphArray(const IntegerList& v); + + static CALLER_ATTACH Builder* CreateBuilder(); + static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + protected: + void Revert(); + + private: + Builder(); + Builder(WritableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + Builder(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + + IntegerList* GetGlyphArray(); + void Initialize(ReadableFontData* data); + + static int32_t DataLength(ReadableFontData* data, + int32_t index_sub_table_offset, + int32_t first_glyph_index, + int32_t last_glyph_index); + + IntegerList glyph_array_; + BigGlyphMetricsBuilderPtr metrics_; + }; + virtual ~IndexSubTableFormat5(); + + virtual int32_t NumGlyphs(); + virtual int32_t GlyphStartOffset(int32_t glyph_id); + virtual int32_t GlyphLength(int32_t glyph_id); + + int32_t ImageSize(); + CALLER_ATTACH BigGlyphMetrics* BigMetrics(); + + private: + IndexSubTableFormat5(ReadableFontData* data, + int32_t first_glyph_index, + int32_t last_glyph_index); + + static int32_t NumGlyphs(ReadableFontData* dta, int32_t table_offset); + + int32_t image_size_; + + friend class Builder; +}; +typedef Ptr IndexSubTableFormat5Ptr; +typedef Ptr IndexSubTableFormat5BuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT5_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.cc b/src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.cc new file mode 100644 index 0000000000..87031a142b --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.cc @@ -0,0 +1,45 @@ +/* + * 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/simple_bitmap_glyph.h" + +namespace sfntly { + +SimpleBitmapGlyph::SimpleBitmapGlyph(ReadableFontData* data, int32_t format) + : BitmapGlyph(data, format) { +} + +SimpleBitmapGlyph::~SimpleBitmapGlyph() { +} + +SimpleBitmapGlyph::Builder::Builder(ReadableFontData* data, int32_t format) + : BitmapGlyph::Builder(data, format) { +} + +SimpleBitmapGlyph::Builder::Builder(WritableFontData* data, int32_t format) + : BitmapGlyph::Builder(data, format) { +} + +SimpleBitmapGlyph::Builder::~Builder() { +} + +CALLER_ATTACH FontDataTable* +SimpleBitmapGlyph::Builder::SubBuildTable(ReadableFontData* data) { + Ptr glyph = new SimpleBitmapGlyph(data, format()); + return glyph.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.h b/src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.h new file mode 100644 index 0000000000..56ede10538 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/simple_bitmap_glyph.h @@ -0,0 +1,44 @@ +/* + * 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_SIMPLE_BITMAP_GLYPH_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SIMPLE_BITMAP_GLYPH_H_ + +#include "sfntly/table/bitmap/bitmap_glyph.h" + +namespace sfntly { + +class SimpleBitmapGlyph : public BitmapGlyph, + public RefCounted { + public: + class Builder : public BitmapGlyph::Builder, + public RefCounted { + public: + Builder(WritableFontData* data, int32_t format); + Builder(ReadableFontData* data, int32_t format); + virtual ~Builder(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + }; + + SimpleBitmapGlyph(ReadableFontData* data, int32_t format); + virtual ~SimpleBitmapGlyph(); +}; +typedef Ptr SimpleBitmapGlyphPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SIMPLE_BITMAP_GLYPH_H_ diff --git a/src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.cc b/src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.cc new file mode 100644 index 0000000000..0f3c1e91d6 --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.cc @@ -0,0 +1,126 @@ +/* + * 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/small_glyph_metrics.h" + +namespace sfntly { +/****************************************************************************** + * SmallGlyphMetrics class + ******************************************************************************/ +SmallGlyphMetrics::SmallGlyphMetrics(ReadableFontData* data) + : GlyphMetrics(data) { +} + +SmallGlyphMetrics::~SmallGlyphMetrics() { +} + +int32_t SmallGlyphMetrics::Height() { + return data_->ReadByte(Offset::kHeight); +} + +int32_t SmallGlyphMetrics::Width() { + return data_->ReadByte(Offset::kWidth); +} + +int32_t SmallGlyphMetrics::BearingX() { + return data_->ReadByte(Offset::kBearingX); +} + +int32_t SmallGlyphMetrics::BearingY() { + return data_->ReadByte(Offset::kBearingY); +} + +int32_t SmallGlyphMetrics::Advance() { + return data_->ReadByte(Offset::kAdvance); +} + +/****************************************************************************** + * SmallGlyphMetrics::Builder class + ******************************************************************************/ +SmallGlyphMetrics::Builder::Builder(WritableFontData* data) + : GlyphMetrics::Builder(data) { +} + +SmallGlyphMetrics::Builder::Builder(ReadableFontData* data) + : GlyphMetrics::Builder(data) { +} + +SmallGlyphMetrics::Builder::~Builder() { +} + +int32_t SmallGlyphMetrics::Builder::Height() { + return InternalReadData()->ReadByte(Offset::kHeight); +} + +void SmallGlyphMetrics::Builder::SetHeight(byte_t height) { + InternalWriteData()->WriteByte(Offset::kHeight, height); +} + +int32_t SmallGlyphMetrics::Builder::Width() { + return InternalReadData()->ReadByte(Offset::kWidth); +} + +void SmallGlyphMetrics::Builder::SetWidth(byte_t width) { + InternalWriteData()->WriteByte(Offset::kWidth, width); +} + +int32_t SmallGlyphMetrics::Builder::BearingX() { + return InternalReadData()->ReadByte(Offset::kBearingX); +} + +void SmallGlyphMetrics::Builder::SetBearingX(byte_t bearing) { + InternalWriteData()->WriteByte(Offset::kBearingX, bearing); +} + +int32_t SmallGlyphMetrics::Builder::BearingY() { + return InternalReadData()->ReadByte(Offset::kBearingY); +} + +void SmallGlyphMetrics::Builder::SetBearingY(byte_t bearing) { + InternalWriteData()->WriteByte(Offset::kBearingY, bearing); +} + +int32_t SmallGlyphMetrics::Builder::Advance() { + return InternalReadData()->ReadByte(Offset::kAdvance); +} + +void SmallGlyphMetrics::Builder::SetAdvance(byte_t advance) { + InternalWriteData()->WriteByte(Offset::kAdvance, advance); +} + +CALLER_ATTACH FontDataTable* + SmallGlyphMetrics::Builder::SubBuildTable(ReadableFontData* data) { + SmallGlyphMetricsPtr output = new SmallGlyphMetrics(data); + return output.Detach(); +} + +void SmallGlyphMetrics::Builder::SubDataSet() { + // NOP. +} + +int32_t SmallGlyphMetrics::Builder::SubDataSizeToSerialize() { + return 0; +} + +bool SmallGlyphMetrics::Builder::SubReadyToSerialize() { + return false; +} + +int32_t SmallGlyphMetrics::Builder::SubSerialize(WritableFontData* new_data) { + return Data()->CopyTo(new_data); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.h b/src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.h new file mode 100644 index 0000000000..ea13720d8f --- /dev/null +++ b/src/sfntly/src/sfntly/table/bitmap/small_glyph_metrics.h @@ -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_TABLE_BITMAP_SMALL_GLYPH_METRICS_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SMALL_GLYPH_METRICS_H_ + +#include "sfntly/port/refcount.h" +#include "sfntly/table/bitmap/glyph_metrics.h" + +namespace sfntly { + +class SmallGlyphMetrics : public GlyphMetrics, + public RefCounted { + public: + struct Offset { + enum { + kMetricsLength = 5, + kHeight = 0, + kWidth = 1, + kBearingX = 2, + kBearingY = 3, + kAdvance = 4, + }; + }; + + class Builder : public GlyphMetrics::Builder, + public RefCounted { + 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 BearingX(); + void SetBearingX(byte_t bearing); + int32_t BearingY(); + void SetBearingY(byte_t bearing); + int32_t Advance(); + void SetAdvance(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); + }; + + explicit SmallGlyphMetrics(ReadableFontData* data); + virtual ~SmallGlyphMetrics(); + + int32_t Height(); + int32_t Width(); + int32_t BearingX(); + int32_t BearingY(); + int32_t Advance(); +}; +typedef Ptr SmallGlyphMetricsPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SMALL_GLYPH_METRICS_H_ diff --git a/src/sfntly/src/sfntly/table/byte_array_table_builder.cc b/src/sfntly/src/sfntly/table/byte_array_table_builder.cc new file mode 100644 index 0000000000..631a05fdd6 --- /dev/null +++ b/src/sfntly/src/sfntly/table/byte_array_table_builder.cc @@ -0,0 +1,70 @@ +/* + * 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/byte_array_table_builder.h" + +namespace sfntly { + +ByteArrayTableBuilder::~ByteArrayTableBuilder() {} + +int32_t ByteArrayTableBuilder::ByteValue(int32_t index) { + ReadableFontDataPtr data = InternalReadData(); + if (data == NULL) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IOException("No font data for the table"); +#endif + return -1; + } + return data->ReadByte(index); +} + +void ByteArrayTableBuilder::SetByteValue(int32_t index, byte_t b) { + WritableFontDataPtr data = InternalWriteData(); + if (data == NULL) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IOException("No font data for the table"); +#endif + return; + } + data->WriteByte(index, b); +} + +int32_t ByteArrayTableBuilder::ByteCount() { + ReadableFontDataPtr data = InternalReadData(); + if (data == NULL) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IOException("No font data for the table"); +#endif + return 0; + } + return data->Length(); +} + +ByteArrayTableBuilder::ByteArrayTableBuilder(Header* header, + WritableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +ByteArrayTableBuilder::ByteArrayTableBuilder(Header* header, + ReadableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +ByteArrayTableBuilder::ByteArrayTableBuilder(Header* header) + : TableBasedTableBuilder(header) { +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/byte_array_table_builder.h b/src/sfntly/src/sfntly/table/byte_array_table_builder.h new file mode 100644 index 0000000000..42d27a8df0 --- /dev/null +++ b/src/sfntly/src/sfntly/table/byte_array_table_builder.h @@ -0,0 +1,53 @@ +/* + * 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_BYTE_ARRAY_TABLE_BUILDER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_ + +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +// An abstract builder base for byte array based tables. +class ByteArrayTableBuilder : public TableBasedTableBuilder { + public: + virtual ~ByteArrayTableBuilder(); + + // Get the byte value at the specified index. The index is relative to the + // start of the table. + // @param index index relative to the start of the table + // @return byte value at the given index + virtual int32_t ByteValue(int32_t index); + + // Set the byte value at the specified index. The index is relative to the + // start of the table. + // @param index index relative to the start of the table + // @param b byte value to set + virtual void SetByteValue(int32_t index, byte_t b); + + // Get the number of bytes set for this table. It may include padding bytes at + // the end. + virtual int32_t ByteCount(); + + protected: + ByteArrayTableBuilder(Header* header, WritableFontData* data); + ByteArrayTableBuilder(Header* header, ReadableFontData* data); + explicit ByteArrayTableBuilder(Header* header); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_ diff --git a/src/sfntly/src/sfntly/table/core/cmap_table.cc b/src/sfntly/src/sfntly/table/core/cmap_table.cc new file mode 100644 index 0000000000..0b4f89a2bd --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/cmap_table.cc @@ -0,0 +1,1288 @@ +/* + * 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. + */ + +// type.h needs to be included first because of building issues on Windows +// Type aliases we delcare are defined in other headers and make the build +// fail otherwise. +#include "sfntly/port/type.h" +#include "sfntly/table/core/cmap_table.h" + +#include +#include + +#include + +#include "sfntly/font.h" +#include "sfntly/math/font_math.h" +#include "sfntly/port/endian.h" +#include "sfntly/port/exception_type.h" +#include "sfntly/table/core/name_table.h" + +namespace sfntly { + +const int32_t CMapTable::NOTDEF = 0; + +CMapTable::CMapId CMapTable::WINDOWS_BMP = { + PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS2 +}; +CMapTable::CMapId CMapTable::WINDOWS_UCS4 = { + PlatformId::kWindows, + WindowsEncodingId::kUnicodeUCS4 +}; +CMapTable::CMapId CMapTable::MAC_ROMAN = { + PlatformId::kWindows, + MacintoshEncodingId::kRoman +}; + +/****************************************************************************** + * CMapTable class + ******************************************************************************/ +CMapTable::CMapTable(Header* header, ReadableFontData* data) + : SubTableContainerTable(header, data) { +} + +CMapTable::~CMapTable() {} + +CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t index) { + if (index < 0 || index > NumCMaps()) { +#ifndef SFNTLY_NO_EXCEPTION + throw IndexOutOfBoundException("Requested CMap index is out of bounds."); +#else + return NULL; +#endif + } + int32_t platform_id = PlatformId(index); + int32_t encoding_id = EncodingId(index); + CMapId cmap_id = NewCMapId(platform_id, encoding_id); + int32_t offset_ = Offset(index); + Ptr cmap_builder = + (CMap::Builder::GetBuilder(data_, offset_, cmap_id)); + if (!cmap_builder) { +#ifndef SFNTLY_NO_EXCEPTION + throw NoSuchElementException("Cannot find builder for requested CMap."); +#else + return NULL; +#endif + } + return down_cast(cmap_builder->Build()); +} + +CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t platform_id, + const int32_t encoding_id) { + return GetCMap(NewCMapId(platform_id, encoding_id)); +} + +CALLER_ATTACH CMapTable::CMap* +CMapTable::GetCMap(const CMapTable::CMapId cmap_id) { + CMapIdFilter id_filter(cmap_id); + CMapIterator cmap_iterator(this, &id_filter); + // There can only be one cmap with a particular CMapId + if (cmap_iterator.HasNext()) { + Ptr cmap; + cmap.Attach(cmap_iterator.Next()); + return cmap.Detach(); + } +#ifndef SFNTLY_NO_EXCEPTION + throw NoSuchElementException(); +#else + return NULL; +#endif +} + +int32_t CMapTable::Version() { + return data_->ReadUShort(Offset::kVersion); +} + +int32_t CMapTable::NumCMaps() { + return data_->ReadUShort(Offset::kNumTables); +} + +CMapTable::CMapId CMapTable::GetCMapId(int32_t index) { + return NewCMapId(PlatformId(index), EncodingId(index)); +} + +int32_t CMapTable::PlatformId(int32_t index) { + return data_->ReadUShort(Offset::kEncodingRecordPlatformId + + OffsetForEncodingRecord(index)); +} + +int32_t CMapTable::EncodingId(int32_t index) { + return data_->ReadUShort(Offset::kEncodingRecordEncodingId + + OffsetForEncodingRecord(index)); +} + +int32_t CMapTable::Offset(int32_t index) { + return data_->ReadULongAsInt(Offset::kEncodingRecordOffset + + OffsetForEncodingRecord(index)); +} + +int32_t CMapTable::OffsetForEncodingRecord(int32_t index) { + return Offset::kEncodingRecordStart + index * Offset::kEncodingRecordSize; +} + +CMapTable::CMapId CMapTable::NewCMapId(int32_t platform_id, + int32_t encoding_id) { + CMapId result; + result.platform_id = platform_id; + result.encoding_id = encoding_id; + return result; +} + +CMapTable::CMapId CMapTable::NewCMapId(const CMapId& obj) { + CMapId result; + result.platform_id = obj.platform_id; + result.encoding_id = obj.encoding_id; + return result; +} + +/****************************************************************************** + * CMapTable::CMapIterator class + ******************************************************************************/ +CMapTable::CMapIterator::CMapIterator(CMapTable* table, + const CMapFilter* filter) + : table_index_(0), filter_(filter), table_(table) { +} + +bool CMapTable::CMapIterator::HasNext() { + if (!filter_) { + if (table_index_ < table_->NumCMaps()) { + return true; + } + return false; + } + + for (; table_index_ < table_->NumCMaps(); ++table_index_) { + if (filter_->accept(table_->GetCMapId(table_index_))) { + return true; + } + } + return false; +} + +CALLER_ATTACH CMapTable::CMap* CMapTable::CMapIterator::Next() { + if (!HasNext()) { +#ifndef SFNTLY_NO_EXCEPTION + throw NoSuchElementException(); +#else + return NULL; +#endif + } + CMapPtr next_cmap; + next_cmap.Attach(table_->GetCMap(table_index_++)); + if (next_cmap == NULL) { +#ifndef SFNTLY_NO_EXCEPTION + throw NoSuchElementException("Error during the creation of the CMap"); +#else + return NULL; +#endif + } + return next_cmap.Detach(); +} + +/****************************************************************************** + * CMapTable::CMapId class + ******************************************************************************/ + +/****************************************************************************** + * CMapTable::CMapIdComparator class + ******************************************************************************/ + +bool CMapTable::CMapIdComparator::operator()(const CMapId& lhs, + const CMapId& rhs) const { + return ((lhs.platform_id << 8 | lhs.encoding_id) > + (rhs.platform_id << 8 | rhs.encoding_id)); +} + +/****************************************************************************** + * CMapTable::CMapIdFilter class + ******************************************************************************/ +CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id) + : wanted_id_(wanted_id), + comparator_(NULL) { +} + +CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id, + const CMapIdComparator* comparator) + : wanted_id_(wanted_id), + comparator_(comparator) { +} + +bool CMapTable::CMapIdFilter::accept(const CMapId& cmap_id) const { + if (!comparator_) + return wanted_id_ == cmap_id; + return (*comparator_)(wanted_id_, cmap_id); +} + +/****************************************************************************** + * CMapTable::CMap class + ******************************************************************************/ +CMapTable::CMap::CMap(ReadableFontData* data, int32_t format, + const CMapId& cmap_id) + : SubTable(data), format_(format), cmap_id_(cmap_id) { +} + +CMapTable::CMap::~CMap() { +} + +/****************************************************************************** + * CMapTable::CMap::Builder class + ******************************************************************************/ +CMapTable::CMap::Builder::~Builder() { +} + +CALLER_ATTACH CMapTable::CMap::Builder* + CMapTable::CMap::Builder::GetBuilder(ReadableFontData* data, int32_t offset, + const CMapId& cmap_id) { + // NOT IMPLEMENTED: Java enum value validation + int32_t format = data->ReadUShort(offset); + CMapBuilderPtr builder; + switch (format) { + case CMapFormat::kFormat0: + builder.Attach(CMapFormat0::Builder::NewInstance(data, offset, cmap_id)); + break; + case CMapFormat::kFormat2: +#if defined (SFNTLY_DEBUG_CMAP) + fprintf(stderr, "Requesting Format2 builder, but it's unsupported; " + "returning NULL\n"); +#endif + break; + case CMapFormat::kFormat4: + builder.Attach(CMapFormat4::Builder::NewInstance(data, offset, cmap_id)); + break; + default: +#ifdef SFNTLY_DEBUG_CMAP + fprintf(stderr, "Unknown builder format requested\n"); +#endif + break; + } + return builder.Detach(); +} + +CALLER_ATTACH CMapTable::CMap::Builder* +CMapTable::CMap::Builder::GetBuilder(int32_t format, const CMapId& cmap_id) { + Ptr builder; + switch (format) { + case CMapFormat::kFormat0: + builder.Attach(CMapFormat0::Builder::NewInstance(cmap_id)); + break; + case CMapFormat::kFormat2: +#if defined (SFNTLY_DEBUG_CMAP) + fprintf(stderr, "Requesting Format2 builder, but it's unsupported; " + "returning NULL\n"); +#endif + break; + case CMapFormat::kFormat4: + builder.Attach(CMapFormat4::Builder::NewInstance(cmap_id)); + break; + default: +#ifdef SFNTLY_DEBUG_CMAP + fprintf(stderr, "Unknown builder format requested\n"); +#endif + break; + } + return builder.Detach(); +} + +CMapTable::CMap::Builder::Builder(ReadableFontData* data, + int32_t format, + const CMapId& cmap_id) + : SubTable::Builder(data), + format_(format), + cmap_id_(cmap_id), + language_(0) { +} + +CMapTable::CMap::Builder::Builder(WritableFontData* data, + int32_t format, + const CMapId& cmap_id) + : SubTable::Builder(data), + format_(format), + cmap_id_(cmap_id), + language_(0) { +} + +int32_t CMapTable::CMap::Builder::SubSerialize(WritableFontData* new_data) { + return InternalReadData()->CopyTo(new_data); +} + +bool CMapTable::CMap::Builder::SubReadyToSerialize() { + return true; +} + +int32_t CMapTable::CMap::Builder::SubDataSizeToSerialize() { + ReadableFontDataPtr read_data = InternalReadData(); + if (!read_data) + return 0; + return read_data->Length(); +} + +void CMapTable::CMap::Builder::SubDataSet() { + // NOP +} + +/****************************************************************************** + * CMapTable::CMapFormat0 + ******************************************************************************/ +CMapTable::CMapFormat0::~CMapFormat0() { +} + +int32_t CMapTable::CMapFormat0::Language() { + return 0; +} + +int32_t CMapTable::CMapFormat0::GlyphId(int32_t character) { + if (character < 0 || character > 255) { + return CMapTable::NOTDEF; + } + return data_->ReadUByte(character + Offset::kFormat0GlyphIdArray); +} + +CMapTable::CMapFormat0::CMapFormat0(ReadableFontData* data, + const CMapId& cmap_id) + : CMap(data, CMapFormat::kFormat0, cmap_id) { +} + +CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat0::Iterator() { + return new CMapTable::CMapFormat0::CharacterIterator(0, 0xff); +} + + +/****************************************************************************** + * CMapTable::CMapFormat0::CharacterIterator + ******************************************************************************/ +CMapTable::CMapFormat0::CharacterIterator::CharacterIterator(int32_t start, + int32_t end) + : character_(start), + max_character_(end) { +} + +CMapTable::CMapFormat0::CharacterIterator::~CharacterIterator() {} + +bool CMapTable::CMapFormat0::CharacterIterator::HasNext() { + return character_ < max_character_; +} + +int32_t CMapTable::CMapFormat0::CharacterIterator::Next() { + if (HasNext()) + return character_++; +#ifndef SFNTLY_NO_EXCEPTION + throw NoSuchElementException("No more characters to iterate."); +#endif + return -1; +} + +/****************************************************************************** + * CMapTable::CMapFormat0::Builder + ******************************************************************************/ +// static +CALLER_ATTACH CMapTable::CMapFormat0::Builder* +CMapTable::CMapFormat0::Builder::NewInstance(WritableFontData* data, + int32_t offset, + const CMapId& cmap_id) { + WritableFontDataPtr wdata; + if (data) { + wdata.Attach(down_cast( + data->Slice(offset, + data->ReadUShort(offset + Offset::kFormat0Length)))); + } + return new Builder(wdata, CMapFormat::kFormat0, cmap_id); +} + +// static +CALLER_ATTACH CMapTable::CMapFormat0::Builder* +CMapTable::CMapFormat0::Builder::NewInstance(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id) { + ReadableFontDataPtr rdata; + if (data) { + rdata.Attach(down_cast( + data->Slice(offset, + data->ReadUShort(offset + Offset::kFormat0Length)))); + } + return new Builder(rdata, CMapFormat::kFormat0, cmap_id); +} + +// static +CALLER_ATTACH CMapTable::CMapFormat0::Builder* +CMapTable::CMapFormat0::Builder::NewInstance(const CMapId& cmap_id) { + return new Builder(cmap_id); +} + +// Always call NewInstance instead of the constructor for creating a new builder +// object! This refactoring avoids memory leaks when slicing the font data. +CMapTable::CMapFormat0::Builder::Builder(WritableFontData* data, int32_t offset, + const CMapId& cmap_id) + : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) { + UNREFERENCED_PARAMETER(offset); +} + +CMapTable::CMapFormat0::Builder::Builder( + ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id) + : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) { + UNREFERENCED_PARAMETER(offset); +} + +CMapTable::CMapFormat0::Builder::Builder(const CMapId& cmap_id) + : CMap::Builder(reinterpret_cast(NULL), + CMapFormat::kFormat0, + cmap_id) { +} + +CMapTable::CMapFormat0::Builder::~Builder() { +} + +CALLER_ATTACH FontDataTable* + CMapTable::CMapFormat0::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new CMapFormat0(data, cmap_id()); + return table.Detach(); +} + +/****************************************************************************** + * CMapTable::CMapFormat2 + ******************************************************************************/ +CMapTable::CMapFormat2::~CMapFormat2() { +} + +int32_t CMapTable::CMapFormat2::Language() { + return 0; +} + +int32_t CMapTable::CMapFormat2::GlyphId(int32_t character) { + if (character > 0xffff) { + return CMapTable::NOTDEF; + } + + uint32_t c = ToBE32(character); + byte_t high_byte = (c >> 8) & 0xff; + byte_t low_byte = c & 0xff; + int32_t offset = SubHeaderOffset(high_byte); + + if (offset == 0) { + low_byte = high_byte; + high_byte = 0; + } + + int32_t first_code = FirstCode(high_byte); + int32_t entry_count = EntryCount(high_byte); + + if (low_byte < first_code || low_byte >= first_code + entry_count) { + return CMapTable::NOTDEF; + } + + int32_t id_range_offset = IdRangeOffset(high_byte); + + // position of idRangeOffset + value of idRangeOffset + index for low byte + // = firstcode + int32_t p_location = (offset + Offset::kFormat2SubHeader_idRangeOffset) + + id_range_offset + + (low_byte - first_code) * DataSize::kUSHORT; + int p = data_->ReadUShort(p_location); + if (p == 0) { + return CMapTable::NOTDEF; + } + + if (offset == 0) { + return p; + } + int id_delta = IdDelta(high_byte); + return (p + id_delta) % 65536; +} + +int32_t CMapTable::CMapFormat2::BytesConsumed(int32_t character) { + uint32_t c = ToBE32(character); + int32_t high_byte = (c >> 8) & 0xff; + int32_t offset = SubHeaderOffset(high_byte); + return (offset == 0) ? 1 : 2; +} + +CMapTable::CMapFormat2::CMapFormat2(ReadableFontData* data, + const CMapId& cmap_id) + : CMap(data, CMapFormat::kFormat2, cmap_id) { +} + +int32_t CMapTable::CMapFormat2::SubHeaderOffset(int32_t sub_header_index) { + return data_->ReadUShort(Offset::kFormat2SubHeaderKeys + + sub_header_index * DataSize::kUSHORT); +} + +int32_t CMapTable::CMapFormat2::FirstCode(int32_t sub_header_index) { + int32_t sub_header_offset = SubHeaderOffset(sub_header_index); + return data_->ReadUShort(sub_header_offset + + Offset::kFormat2SubHeaderKeys + + Offset::kFormat2SubHeader_firstCode); +} + +int32_t CMapTable::CMapFormat2::EntryCount(int32_t sub_header_index) { + int32_t sub_header_offset = SubHeaderOffset(sub_header_index); + return data_->ReadUShort(sub_header_offset + + Offset::kFormat2SubHeaderKeys + + Offset::kFormat2SubHeader_entryCount); +} + +int32_t CMapTable::CMapFormat2::IdRangeOffset(int32_t sub_header_index) { + int32_t sub_header_offset = SubHeaderOffset(sub_header_index); + return data_->ReadUShort(sub_header_offset + + Offset::kFormat2SubHeaderKeys + + Offset::kFormat2SubHeader_idRangeOffset); +} + +int32_t CMapTable::CMapFormat2::IdDelta(int32_t sub_header_index) { + int32_t sub_header_offset = SubHeaderOffset(sub_header_index); + return data_->ReadUShort(sub_header_offset + + Offset::kFormat2SubHeaderKeys + + Offset::kFormat2SubHeader_idDelta); +} + +CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat2::Iterator() { + // UNIMPLEMENTED + return NULL; +} + +/****************************************************************************** + * CMapTable::CMapFormat2::Builder + ******************************************************************************/ +CMapTable::CMapFormat2::Builder::Builder(WritableFontData* data, + int32_t offset, + const CMapId& cmap_id) + : CMapTable::CMap::Builder(data ? down_cast( + data->Slice(offset, data->ReadUShort( + offset + Offset::kFormat0Length))) + : reinterpret_cast(NULL), + CMapFormat::kFormat2, cmap_id) { + // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix. +} + +CMapTable::CMapFormat2::Builder::Builder(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id) + : CMapTable::CMap::Builder(data ? down_cast( + data->Slice(offset, data->ReadUShort( + offset + Offset::kFormat0Length))) + : reinterpret_cast(NULL), + CMapFormat::kFormat2, cmap_id) { + // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix. +} + +CMapTable::CMapFormat2::Builder::~Builder() { +} + +CALLER_ATTACH FontDataTable* + CMapTable::CMapFormat2::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new CMapFormat2(data, cmap_id()); + return table.Detach(); +} + +/****************************************************************************** + * CMapTable::CMapFormat4 + ******************************************************************************/ +CMapTable::CMapFormat4::CMapFormat4(ReadableFontData* data, + const CMapId& cmap_id) + : CMap(data, CMapFormat::kFormat4, cmap_id), + seg_count_(SegCount(data)), + start_code_offset_(StartCodeOffset(seg_count_)), + end_code_offset_(Offset::kFormat4EndCount), + id_delta_offset_(IdDeltaOffset(seg_count_)), + glyph_id_array_offset_(GlyphIdArrayOffset(seg_count_)) { +} + +CMapTable::CMapFormat4::~CMapFormat4() { +} + +int32_t CMapTable::CMapFormat4::GlyphId(int32_t character) { + int32_t segment = data_->SearchUShort(StartCodeOffset(seg_count_), + DataSize::kUSHORT, + Offset::kFormat4EndCount, + DataSize::kUSHORT, + seg_count_, + character); + if (segment == -1) { + return CMapTable::NOTDEF; + } + int32_t start_code = StartCode(segment); + return RetrieveGlyphId(segment, start_code, character); +} + +int32_t CMapTable::CMapFormat4::RetrieveGlyphId(int32_t segment, + int32_t start_code, + int32_t character) { + if (character < start_code) { + return CMapTable::NOTDEF; + } + int32_t id_range_offset = IdRangeOffset(segment); + if (id_range_offset == 0) { + return (character + IdDelta(segment)) % 65536; + } + return data_->ReadUShort(id_range_offset + + IdRangeOffsetLocation(segment) + + 2 * (character - start_code)); +} + +int32_t CMapTable::CMapFormat4::seg_count() { + return seg_count_; +} + +int32_t CMapTable::CMapFormat4::Length() { + return Length(data_); +} + +int32_t CMapTable::CMapFormat4::StartCode(int32_t segment) { + if (!IsValidIndex(segment)) { + return -1; + } + return StartCode(data_.p_, seg_count_, segment); +} + +// static +int32_t CMapTable::CMapFormat4::Language(ReadableFontData* data) { + int32_t language = data->ReadUShort(Offset::kFormat4Language); + return language; +} + +// static +int32_t CMapTable::CMapFormat4::Length(ReadableFontData* data) { + int32_t length = data->ReadUShort(Offset::kFormat4Length); + return length; +} + +// static +int32_t CMapTable::CMapFormat4::SegCount(ReadableFontData* data) { + int32_t seg_count = data->ReadUShort(Offset::kFormat4SegCountX2) / 2; + return seg_count; +} + +// static +int32_t CMapTable::CMapFormat4::StartCode(ReadableFontData* data, + int32_t seg_count, + int32_t index) { + int32_t start_code = data->ReadUShort(StartCodeOffset(seg_count) + + index * DataSize::kUSHORT); + return start_code; +} + +// static +int32_t CMapTable::CMapFormat4::StartCodeOffset(int32_t seg_count) { + int32_t start_code_offset = Offset::kFormat4EndCount + + (seg_count + 1) * DataSize::kUSHORT; + return start_code_offset; +} + +// static +int32_t CMapTable::CMapFormat4::EndCode(ReadableFontData* data, + int32_t seg_count, + int32_t index) { + UNREFERENCED_PARAMETER(seg_count); + int32_t end_code = data->ReadUShort(Offset::kFormat4EndCount + + index * DataSize::kUSHORT); + return end_code; +} + +// static +int32_t CMapTable::CMapFormat4::IdDelta(ReadableFontData* data, + int32_t seg_count, + int32_t index) { + int32_t id_delta = data->ReadUShort(IdDeltaOffset(seg_count) + + index * DataSize::kUSHORT); + return id_delta; +} + +// static +int32_t CMapTable::CMapFormat4::IdDeltaOffset(int32_t seg_count) { + int32_t id_delta_offset = + Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT; + return id_delta_offset; +} + +// static +int32_t CMapTable::CMapFormat4::IdRangeOffset(ReadableFontData* data, + int32_t seg_count, + int32_t index) { + int32_t id_range_offset = + data->ReadUShort(IdRangeOffsetOffset(seg_count) + + index * DataSize::kUSHORT); + return id_range_offset; +} + +// static +int32_t CMapTable::CMapFormat4::IdRangeOffsetOffset(int32_t seg_count) { + int32_t id_range_offset_offset = + Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT + + seg_count * DataSize::kSHORT; + return id_range_offset_offset; +} + +// static +int32_t CMapTable::CMapFormat4::GlyphIdArrayOffset(int32_t seg_count) { + int32_t glyph_id_array_offset = + Offset::kFormat4EndCount + (3 * seg_count + 1) * DataSize::kUSHORT + + seg_count * DataSize::kSHORT; + return glyph_id_array_offset; +} + +int32_t CMapTable::CMapFormat4::EndCode(int32_t segment) { + if (IsValidIndex(segment)) { + return EndCode(data_, seg_count_, segment); + } +#if defined (SFNTLY_NO_EXCEPTION) + return -1; +#else + throw IllegalArgumentException(); +#endif +} + +bool CMapTable::CMapFormat4::IsValidIndex(int32_t segment) { + if (segment < 0 || segment >= seg_count_) { +#if defined (SFNTLY_NO_EXCEPTION) + return false; +#else + throw IllegalArgumentException(); +#endif + } + return true; +} + +int32_t CMapTable::CMapFormat4::IdDelta(int32_t segment) { + if (IsValidIndex(segment)) + return IdDelta(data_, seg_count_, segment); + return -1; +} + +int32_t CMapTable::CMapFormat4::IdRangeOffset(int32_t segment) { + if (IsValidIndex(segment)) + return data_->ReadUShort(IdRangeOffsetLocation(segment)); + return -1; +} + +int32_t CMapTable::CMapFormat4::IdRangeOffsetLocation(int32_t segment) { + if (IsValidIndex(segment)) + return IdRangeOffsetOffset(seg_count_) + segment * DataSize::kUSHORT; + return -1; +} + +int32_t CMapTable::CMapFormat4::GlyphIdArray(int32_t index) { + return data_->ReadUShort(glyph_id_array_offset_ + index * DataSize::kUSHORT); +} + +int32_t CMapTable::CMapFormat4::Language() { + return Language(data_); +} + + +CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat4::Iterator() { + return new CharacterIterator(this); +} + +/****************************************************************************** + * CMapTable::CMapFormat4::CharacterIterator class + ******************************************************************************/ +CMapTable::CMapFormat4::CharacterIterator::CharacterIterator( + CMapFormat4* parent) + : parent_(parent), + segment_index_(0), + first_char_in_segment_(-1), + last_char_in_segment_(-1), + next_char_(-1), + next_char_set_(false) { +} + +bool CMapTable::CMapFormat4::CharacterIterator::HasNext() { + if (next_char_set_) + return true; + while (segment_index_ < parent_->seg_count_) { + if (first_char_in_segment_ < 0) { + first_char_in_segment_ = parent_->StartCode(segment_index_); + last_char_in_segment_ = parent_->EndCode(segment_index_); + next_char_ = first_char_in_segment_; + next_char_set_ = true; + return true; + } + if (next_char_ < last_char_in_segment_) { + next_char_++; + next_char_set_ = true; + return true; + } + segment_index_++; + first_char_in_segment_ = -1; + } + return false; +} + +int32_t CMapTable::CMapFormat4::CharacterIterator::Next() { + if (!next_char_set_) { + if (!HasNext()) { +#if defined (SFNTLY_NO_EXCEPTION) + return -1; +#else + throw NoSuchElementException("No more characters to iterate."); +#endif + } + } + next_char_set_ = false; + return next_char_; +} + +/****************************************************************************** + * CMapTable::CMapFormat4::Builder::Segment class + ******************************************************************************/ +CMapTable::CMapFormat4::Builder::Segment::Segment() {} + +CMapTable::CMapFormat4::Builder::Segment::Segment(Segment* other) + : start_count_(other->start_count_), + end_count_(other->end_count_), + id_delta_(other->id_delta_), + id_range_offset_(other->id_range_offset_) { +} + +CMapTable::CMapFormat4::Builder::Segment::Segment(int32_t start_count, + int32_t end_count, + int32_t id_delta, + int32_t id_range_offset) + : start_count_(start_count), + end_count_(end_count), + id_delta_(id_delta), + id_range_offset_(id_range_offset) { +} + +CMapTable::CMapFormat4::Builder::Segment::~Segment() {} + +int32_t CMapTable::CMapFormat4::Builder::Segment::start_count() { + return start_count_; +} + +void +CMapTable::CMapFormat4::Builder::Segment::set_start_count(int32_t start_count) { + start_count_ = start_count; +} + +int32_t CMapTable::CMapFormat4::Builder::Segment::end_count() { + return end_count_; +} + +void +CMapTable::CMapFormat4::Builder::Segment::set_end_count(int32_t end_count) { + end_count_ = end_count; +} + +int32_t CMapTable::CMapFormat4::Builder::Segment::id_delta() { + return id_delta_; +} + +void +CMapTable::CMapFormat4::Builder::Segment::set_id_delta(int32_t id_delta) { + id_delta_ = id_delta; +} + +int32_t CMapTable::CMapFormat4::Builder::Segment::id_range_offset() { + return id_range_offset_; +} + +void +CMapTable::CMapFormat4::Builder::Segment:: +set_id_range_offset(int32_t id_range_offset) { + id_range_offset_ = id_range_offset; +} + +// static +CALLER_ATTACH SegmentList* +CMapTable::CMapFormat4::Builder::Segment::DeepCopy(SegmentList* original) { + SegmentList* list = new SegmentList; + for (SegmentList::iterator it = original->begin(), + e = original->end(); it != e; ++it) { + list->push_back(*it); + } + return list; +} + +/****************************************************************************** + * CMapTable::CMapFormat4::Builder class + ******************************************************************************/ +CALLER_ATTACH CMapTable::CMapFormat4::Builder* +CMapTable::CMapFormat4::Builder::NewInstance(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id) { + ReadableFontDataPtr rdata; + if (data) { + rdata.Attach + (down_cast + (data->Slice(offset, + data->ReadUShort(offset + Offset::kFormat4Length)))); + } + return new Builder(rdata, CMapFormat::kFormat4, cmap_id); +} + +CALLER_ATTACH CMapTable::CMapFormat4::Builder* +CMapTable::CMapFormat4::Builder::NewInstance(WritableFontData* data, + int32_t offset, + const CMapId& cmap_id) { + WritableFontDataPtr wdata; + if (data) { + wdata.Attach + (down_cast + (data->Slice(offset, + data->ReadUShort(offset + Offset::kFormat4Length)))); + } + return new Builder(wdata, CMapFormat::kFormat4, cmap_id); +} + +CALLER_ATTACH CMapTable::CMapFormat4::Builder* +CMapTable::CMapFormat4::Builder::NewInstance(const CMapId& cmap_id) { + return new Builder(cmap_id); +} + +CMapTable::CMapFormat4::Builder::Builder(ReadableFontData* data, int32_t offset, + const CMapId& cmap_id) + : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) { + UNREFERENCED_PARAMETER(offset); +} + +CMapTable::CMapFormat4::Builder::Builder(WritableFontData* data, int32_t offset, + const CMapId& cmap_id) + : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) { + UNREFERENCED_PARAMETER(offset); +} + +CMapTable::CMapFormat4::Builder::Builder(SegmentList* segments, + IntegerList* glyph_id_array, + const CMapId& cmap_id) + : CMap::Builder(reinterpret_cast(NULL), + CMapFormat::kFormat4, cmap_id), + segments_(segments->begin(), segments->end()), + glyph_id_array_(glyph_id_array->begin(), glyph_id_array->end()) { + set_model_changed(); +} + +CMapTable::CMapFormat4::Builder::Builder(const CMapId& cmap_id) + : CMap::Builder(reinterpret_cast(NULL), + CMapFormat::kFormat4, cmap_id) { +} + +CMapTable::CMapFormat4::Builder::~Builder() {} + +void CMapTable::CMapFormat4::Builder::Initialize(ReadableFontData* data) { + if (data == NULL || data->Length() == 0) + return; + + // build segments + int32_t seg_count = CMapFormat4::SegCount(data); + for (int32_t index = 0; index < seg_count; ++index) { + Ptr segment = new Segment; + segment->set_start_count(CMapFormat4::StartCode(data, seg_count, index)); +#if defined SFNTLY_DEBUG_CMAP + fprintf(stderr, "Segment %d; start %d\n", index, segment->start_count()); +#endif + segment->set_end_count(CMapFormat4::EndCode(data, seg_count, index)); + segment->set_id_delta(CMapFormat4::IdDelta(data, seg_count, index)); + segment->set_id_range_offset(CMapFormat4::IdRangeOffset(data, + seg_count, + index)); + segments_.push_back(segment); + } + + // build glyph id array + int32_t glyph_id_array_offset = CMapFormat4::GlyphIdArrayOffset(seg_count); + int32_t glyph_id_array_length = + (CMapFormat4::Length(data) - glyph_id_array_offset) + / DataSize::kUSHORT; + fprintf(stderr, "id array size %d\n", glyph_id_array_length); + for (int32_t i = 0; i < glyph_id_array_length; i += DataSize::kUSHORT) { + glyph_id_array_.push_back(data->ReadUShort(glyph_id_array_offset + i)); + } +} + +SegmentList* CMapTable::CMapFormat4::Builder::segments() { + if (segments_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &segments_; +} + +void CMapTable::CMapFormat4::Builder::set_segments(SegmentList* segments) { + segments_.assign(segments->begin(), segments->end()); + set_model_changed(); +} + +IntegerList* CMapTable::CMapFormat4::Builder::glyph_id_array() { + if (glyph_id_array_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &glyph_id_array_; +} + +void CMapTable::CMapFormat4::Builder:: +set_glyph_id_array(IntegerList* glyph_id_array) { + glyph_id_array_.assign(glyph_id_array->begin(), glyph_id_array->end()); + set_model_changed(); +} + +CALLER_ATTACH FontDataTable* +CMapTable::CMapFormat4::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new CMapFormat4(data, cmap_id()); + return table.Detach(); +} + +void CMapTable::CMapFormat4::Builder::SubDataSet() { + segments_.clear(); + glyph_id_array_.clear(); + set_model_changed(); +} + +int32_t CMapTable::CMapFormat4::Builder::SubDataSizeToSerialize() { + if (!model_changed()) { + return CMap::Builder::SubDataSizeToSerialize(); + } + int32_t size = Offset::kFormat4FixedSize + segments_.size() + * (3 * DataSize::kUSHORT + DataSize::kSHORT) + + glyph_id_array_.size() * DataSize::kSHORT; + return size; +} + +bool CMapTable::CMapFormat4::Builder::SubReadyToSerialize() { + if (!model_changed()) { + return CMap::Builder::SubReadyToSerialize(); + } + if (!segments()->empty()) { + return true; + } + return false; +} + +int32_t +CMapTable::CMapFormat4::Builder::SubSerialize(WritableFontData* new_data) { + if (!model_changed()) { + return CMap::Builder::SubSerialize(new_data); + } + int32_t index = 0; + index += new_data->WriteUShort(index, CMapFormat::kFormat4); + index += DataSize::kUSHORT; // length - write this at the end + index += new_data->WriteUShort(index, language()); + + int32_t seg_count = segments_.size(); + index += new_data->WriteUShort(index, seg_count * 2); + int32_t log2_seg_count = FontMath::Log2(seg_count); + int32_t search_range = 1 << (log2_seg_count + 1); + index += new_data->WriteUShort(index, search_range); + int32_t entry_selector = log2_seg_count; + index += new_data->WriteUShort(index, entry_selector); + int32_t range_shift = 2 * seg_count - search_range; + index += new_data->WriteUShort(index, range_shift); + + for (int32_t i = 0; i < seg_count; ++i) { + index += new_data->WriteUShort(index, segments_[i]->end_count()); + } + index += new_data->WriteUShort(index, 0); // reserved ushort + for (int32_t i = 0; i < seg_count; ++i) { +#if defined SFNTLY_DEBUG_CMAP + fprintf(stderr, "Segment %d; start %d\n", i, segments_[i]->start_count()); +#endif + index += new_data->WriteUShort(index, segments_[i]->start_count()); + } + for (int32_t i = 0; i < seg_count; ++i) { + index += new_data->WriteShort(index, segments_[i]->id_delta()); + } + for (int32_t i = 0; i < seg_count; ++i) { + index += new_data->WriteUShort(index, segments_[i]->id_range_offset()); + } + +#if defined SFNTLY_DEBUG_CMAP + fprintf(stderr, "Glyph id array size %lu\n", glyph_id_array_.size()); +#endif + for (size_t i = 0; i < glyph_id_array_.size(); ++i) { + index += new_data->WriteUShort(index, glyph_id_array_[i]); + } + + new_data->WriteUShort(Offset::kFormat4Length, index); + return index; +} + +/****************************************************************************** + * CMapTable::Builder class + ******************************************************************************/ +CMapTable::Builder::Builder(Header* header, WritableFontData* data) + : SubTableContainerTable::Builder(header, data), version_(0) { +} + +CMapTable::Builder::Builder(Header* header, ReadableFontData* data) + : SubTableContainerTable::Builder(header, data), version_(0) { +} + +CMapTable::Builder::~Builder() { +} + +int32_t CMapTable::Builder::SubSerialize(WritableFontData* new_data) { + int32_t size = new_data->WriteUShort(CMapTable::Offset::kVersion, + version_); + size += new_data->WriteUShort(CMapTable::Offset::kNumTables, + GetCMapBuilders()->size()); + + int32_t index_offset = size; + size += GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize; + for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(), + e = GetCMapBuilders()->end(); it != e; ++it) { + CMapBuilderPtr b = it->second; + // header entry + index_offset += new_data->WriteUShort(index_offset, b->platform_id()); + index_offset += new_data->WriteUShort(index_offset, b->encoding_id()); + index_offset += new_data->WriteULong(index_offset, size); + + // cmap + FontDataPtr slice; + slice.Attach(new_data->Slice(size)); + size += b->SubSerialize(down_cast(slice.p_)); + } + return size; +} + +bool CMapTable::Builder::SubReadyToSerialize() { + if (GetCMapBuilders()->empty()) + return false; + + // check each table + for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(), + e = GetCMapBuilders()->end(); it != e; ++it) { + if (!it->second->SubReadyToSerialize()) + return false; + } + return true; +} + +int32_t CMapTable::Builder::SubDataSizeToSerialize() { + if (GetCMapBuilders()->empty()) + return 0; + + bool variable = false; + int32_t size = CMapTable::Offset::kEncodingRecordStart + + GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize; + + // calculate size of each table + for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(), + e = GetCMapBuilders()->end(); it != e; ++it) { + int32_t cmap_size = it->second->SubDataSizeToSerialize(); + size += abs(cmap_size); + variable |= cmap_size <= 0; + } + return variable ? -size : size; +} + +void CMapTable::Builder::SubDataSet() { + GetCMapBuilders()->clear(); + Table::Builder::set_model_changed(); +} + +CALLER_ATTACH FontDataTable* + CMapTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new CMapTable(header(), data); + return table.Detach(); +} + +CALLER_ATTACH CMapTable::Builder* + CMapTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new CMapTable::Builder(header, data); + return builder.Detach(); +} + +// static +CALLER_ATTACH CMapTable::CMap::Builder* + CMapTable::Builder::CMapBuilder(ReadableFontData* data, int32_t index) { + if (index < 0 || index > NumCMaps(data)) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException( + "CMap table is outside of the bounds of the known tables."); +#endif + return NULL; + } + + int32_t platform_id = data->ReadUShort(Offset::kEncodingRecordPlatformId + + OffsetForEncodingRecord(index)); + int32_t encoding_id = data->ReadUShort(Offset::kEncodingRecordEncodingId + + OffsetForEncodingRecord(index)); + int32_t offset = data->ReadULongAsInt(Offset::kEncodingRecordOffset + + OffsetForEncodingRecord(index)); + return CMap::Builder::GetBuilder(data, offset, + NewCMapId(platform_id, encoding_id)); +} + +// static +int32_t CMapTable::Builder::NumCMaps(ReadableFontData* data) { + if (data == NULL) { + return 0; + } + return data->ReadUShort(Offset::kNumTables); +} + +int32_t CMapTable::Builder::NumCMaps() { + return GetCMapBuilders()->size(); +} + +void CMapTable::Builder::Initialize(ReadableFontData* data) { + int32_t num_cmaps = NumCMaps(data); + for (int32_t i = 0; i < num_cmaps; ++i) { + CMapTable::CMap::Builder* cmap_builder = CMapBuilder(data, i); + if (!cmap_builder) + continue; + cmap_builders_[cmap_builder->cmap_id()] = cmap_builder; + } +} + +CMapTable::CMap::Builder* CMapTable::Builder::NewCMapBuilder( + const CMapId& cmap_id, + ReadableFontData* data) { + Ptr wfd; + wfd.Attach(WritableFontData::CreateWritableFontData(data->Size())); + data->CopyTo(wfd.p_); + CMapTable::CMapBuilderPtr builder; + builder.Attach(CMap::Builder::GetBuilder(wfd.p_, 0, cmap_id)); + CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders(); + cmap_builders->insert(std::make_pair(cmap_id, builder.p_)); + return builder.Detach(); +} + +CMapTable::CMap::Builder* +CMapTable::Builder::NewCMapBuilder(int32_t format, const CMapId& cmap_id) { + Ptr cmap_builder; + cmap_builder.Attach(CMap::Builder::GetBuilder(format, cmap_id)); + CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders(); + cmap_builders->insert(std::make_pair(cmap_id, cmap_builder.p_)); + return cmap_builder.Detach(); +} + +CMapTable::CMap::Builder* +CMapTable::Builder::CMapBuilder(const CMapId& cmap_id) { + CMapBuilderMap* cmap_builders = this->GetCMapBuilders(); + CMapBuilderMap::iterator builder = cmap_builders->find(cmap_id); + if (builder != cmap_builders->end()) + return builder->second; +#ifndef SFNTLY_NO_EXCEPTION + throw NoSuchElementException("No builder found for cmap_id"); +#else + return NULL; +#endif +} + +CMapTable::CMapBuilderMap* CMapTable::Builder::GetCMapBuilders() { + if (cmap_builders_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &cmap_builders_; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/cmap_table.h b/src/sfntly/src/sfntly/table/core/cmap_table.h new file mode 100644 index 0000000000..29ce3e4189 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/cmap_table.h @@ -0,0 +1,711 @@ +/* + * 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_CORE_CMAP_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_CMAP_TABLE_H_ + +// type.h needs to be included first because of building issues on Windows +// Type aliases we delcare are defined in other headers and make the build +// fail otherwise. +#include "sfntly/port/type.h" +#include +#include + +#include "sfntly/port/refcount.h" +#include "sfntly/table/subtable.h" +#include "sfntly/table/subtable_container_table.h" + +namespace sfntly { + +// CMap subtable formats +struct CMapFormat { + enum { + kFormat0 = 0, + kFormat2 = 2, + kFormat4 = 4, + kFormat6 = 6, + kFormat8 = 8, + kFormat10 = 10, + kFormat12 = 12, + kFormat13 = 13, + kFormat14 = 14 + }; +}; + +// A CMap table +class CMapTable : public SubTableContainerTable, public RefCounted { +public: + // CMapTable::CMapId + struct CMapId { + int32_t platform_id; + int32_t encoding_id; + bool operator==(const CMapId& obj) const { + return platform_id == obj.platform_id && encoding_id == obj.encoding_id; + } + }; + static CMapId WINDOWS_BMP; + static CMapId WINDOWS_UCS4; + static CMapId MAC_ROMAN; + + // CMapTable::CMapIdComparator + class CMapIdComparator { + public: + bool operator()(const CMapId& lhs, const CMapId& rhs) const; + }; + + // A filter on cmap + // CMapTable::CMapFilter + class CMapFilter { + public: + // Test on whether the cmap is acceptable or not + // @param cmap_id the id of the cmap + // @return true if the cmap is acceptable; false otherwise + virtual bool accept(const CMapId& cmap_id) const = 0; + // Make gcc -Wnon-virtual-dtor happy. + virtual ~CMapFilter() {} + }; + + // Filters CMaps by CMapId to implement CMapTable::get() + // wanted_id is the CMap we'd like to find. + // We compare the current CMap to it either by equality (==) or using a + // comparator. + // CMapTable::CMapIdFilter + class CMapIdFilter : public CMapFilter { + public: + explicit CMapIdFilter(const CMapId wanted_id); + CMapIdFilter(const CMapId wanted_id, + const CMapIdComparator* comparator); + ~CMapIdFilter() {} + virtual bool accept(const CMapId& cmap_id) const; + private: + CMapIdFilter& operator=(const CMapIdFilter& that); + const CMapId wanted_id_; + const CMapIdComparator *comparator_; + }; + + // The abstract base class for all cmaps. + // + // CMap equality is based on the equality of the (@link {@link CMapId} that + // defines the CMap. In the cmap table for a font there can only be one cmap + // with a given cmap id (pair of platform and encoding ids) no matter what the + // type of the cmap is. + // + // The cmap offers CharacterIterator to allow iteration over + // characters that are mapped by the cmap. This iteration mostly returns the + // characters mapped by the cmap. It will return all characters mapped by the + // cmap to anything but .notdef but it may return some that are not + // mapped or are mapped to .notdef. Various cmap tables provide ranges and + // such to describe characters for lookup but without going the full way to + // mapping to the glyph id it isn't always possible to tell if a character + // will end up with a valid glyph id. So, some of the characters returned from + // the Iterator may still end up pointing to the .notdef glyph. However, the + // number of such characters should be small in most cases with well designed + // cmaps. + class Builder; + class CMap : public SubTable { + public: + // CMapTable::CMap::Builder + class Builder : public SubTable::Builder { + public: + virtual ~Builder(); + + CALLER_ATTACH static Builder* + GetBuilder(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id); + CALLER_ATTACH static Builder* + GetBuilder(int32_t format, + const CMapId& cmap_id); + + // Note: yes, an object is returned on stack since it's small enough. + virtual CMapId cmap_id() { return cmap_id_; } + virtual int32_t platform_id() { return cmap_id_.platform_id; } + virtual int32_t encoding_id() { return cmap_id_.encoding_id; } + virtual int32_t format() { return format_; } + virtual int32_t language() { return language_; } + virtual void set_language(int32_t language) { language_ = language; } + + protected: + Builder(ReadableFontData* data, + int32_t format, + const CMapId& cmap_id); + Builder(WritableFontData* data, + int32_t format, + const CMapId& cmap_id); + + virtual int32_t SubSerialize(WritableFontData* new_data); + virtual bool SubReadyToSerialize(); + virtual int32_t SubDataSizeToSerialize(); + virtual void SubDataSet(); + + private: + int32_t format_; + CMapId cmap_id_; + int32_t language_; + + friend class CMapTable::Builder; + }; + // Abstract CMap character iterator + // The fully qualified name is CMapTable::CMap::CharacterIterator + class CharacterIterator { + public: + virtual ~CharacterIterator() {} + virtual bool HasNext() = 0; + // Returns -1 if there are no more characters to iterate through + // and exceptions are turned off + virtual int32_t Next() = 0; + + protected: + // Use the CMap::Iterator method below instead of directly requesting + // a CharacterIterator. + CharacterIterator() {} + }; + + CMap(ReadableFontData* data, int32_t format, const CMapId& cmap_id); + virtual ~CMap(); + + virtual CMap::CharacterIterator* Iterator() = 0; + + virtual int32_t format() { return format_; } + virtual CMapId cmap_id() { return cmap_id_; } + virtual int32_t platform_id() { return cmap_id_.platform_id; } + virtual int32_t encoding_id() { return cmap_id_.encoding_id; } + + // Get the language of the cmap. + // + // Note on the language field in 'cmap' subtables: The language field must + // be set to zero for all cmap subtables whose platform IDs are other than + // Macintosh (platform ID 1). For cmap subtables whose platform IDs are + // Macintosh, set this field to the Macintosh language ID of the cmap + // subtable plus one, or to zero if the cmap subtable is not + // language-specific. For example, a Mac OS Turkish cmap subtable must set + // this field to 18, since the Macintosh language ID for Turkish is 17. A + // Mac OS Roman cmap subtable must set this field to 0, since Mac OS Roman + // is not a language-specific encoding. + // + // @return the language id + virtual int32_t Language() = 0; + + // Gets the glyph id for the character code provided. + // The character code provided must be in the encoding used by the cmap + // table. + virtual int32_t GlyphId(int32_t character) = 0; + + private: + int32_t format_; + CMapId cmap_id_; + }; + typedef Ptr CMapPtr; + typedef Ptr CMapBuilderPtr; + typedef std::map CMapBuilderMap; + + // A cmap format 0 sub table + class CMapFormat0 : public CMap, public RefCounted { + public: + // The fully qualified name is CMapTable::CMapFormat0::Builder + class Builder : public CMap::Builder, + public RefCounted { + public: + CALLER_ATTACH static Builder* NewInstance(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id); + CALLER_ATTACH static Builder* NewInstance(WritableFontData* data, + int32_t offset, + const CMapId& cmap_id); + CALLER_ATTACH static Builder* NewInstance(const CMapId& cmap_id); + virtual ~Builder(); + + protected: + virtual CALLER_ATTACH FontDataTable* + SubBuildTable(ReadableFontData* data); + + private: + // When creating a new CMapFormat0 Builder, use NewInstance instead of + // the constructors! This avoids a memory leak when slicing the FontData. + Builder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id); + Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id); + Builder(const CMapId& cmap_id); + }; + + // The fully qualified name is CMapTable::CMapFormat0::CharacterIterator + class CharacterIterator : public CMap::CharacterIterator { + public: + virtual ~CharacterIterator(); + virtual bool HasNext(); + virtual int32_t Next(); + + private: + CharacterIterator(int32_t start, int32_t end); + friend class CMapFormat0; + int32_t character_, max_character_; + }; + + virtual ~CMapFormat0(); + virtual int32_t Language(); + virtual int32_t GlyphId(int32_t character); + CMap::CharacterIterator* Iterator(); + + private: + CMapFormat0(ReadableFontData* data, const CMapId& cmap_id); + }; + + // A cmap format 2 sub table + // The format 2 cmap is used for multi-byte encodings such as SJIS, + // EUC-JP/KR/CN, Big5, etc. + class CMapFormat2 : public CMap, public RefCounted { + public: + // CMapTable::CMapFormat2::Builder + class Builder : public CMap::Builder, + public RefCounted { + public: + Builder(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id); + Builder(WritableFontData* data, + int32_t offset, + const CMapId& cmap_id); + virtual ~Builder(); + + protected: + virtual CALLER_ATTACH FontDataTable* + SubBuildTable(ReadableFontData* data); + }; + // CMapTable::CMapFormat2::CharacterIterator + class CharacterIterator : public CMap::CharacterIterator { + public: + virtual ~CharacterIterator(); + virtual bool hasNext(); + virtual int32_t next(); + + private: + CharacterIterator(); + }; + + virtual int32_t Language(); + virtual int32_t GlyphId(int32_t character); + + // Returns how many bytes would be consumed by a lookup of this character + // with this cmap. This comes about because the cmap format 2 table is + // designed around multi-byte encodings such as SJIS, EUC-JP, Big5, etc. + // return the number of bytes consumed from this "character" - either 1 or 2 + virtual int32_t BytesConsumed(int32_t character); + + virtual ~CMapFormat2(); + + private: + CMapFormat2(ReadableFontData* data, const CMapId& cmap_id); + + int32_t SubHeaderOffset(int32_t sub_header_index); + int32_t FirstCode(int32_t sub_header_index); + int32_t EntryCount(int32_t sub_header_index); + int32_t IdRangeOffset(int32_t sub_header_index); + int32_t IdDelta(int32_t sub_header_index); + CMap::CharacterIterator* Iterator(); + }; + + // CMapTable::CMapFormat4 + class CMapFormat4 : public CMap, + public RefCounted { + public: + // CMapTable::CMapFormat4::Builder + class Builder : public CMap::Builder, + public RefCounted { + public: + // CMapTable::CMapFormat4::Builder::Segment + class Segment : public RefCounted { + public: + Segment(); + explicit Segment(Segment* other); + Segment(int32_t start_count, + int32_t end_count, + int32_t id_delta, + int32_t id_range_offset); + ~Segment(); + + // @return the startCount + int32_t start_count(); + // @param startCount the startCount to set + void set_start_count(int32_t start_count); + // @return the endCount + int32_t end_count(); + // @param endcount the endCount to set + void set_end_count(int32_t end_count); + // @return the idDelta + int32_t id_delta(); + // @param idDelta the idDelta to set + void set_id_delta(int32_t id_delta); + // @return the idRangeOffset + int32_t id_range_offset(); + // @param idRangeOffset the idRangeOffset to set + void set_id_range_offset(int32_t id_range_offset); + + static CALLER_ATTACH + std::vector >* + DeepCopy(std::vector >* original); + + private: + int32_t start_count_; + int32_t end_count_; + int32_t id_delta_; + int32_t id_range_offset_; + }; + typedef std::vector > SegmentList; + + static CALLER_ATTACH Builder* NewInstance(WritableFontData* data, + int32_t offset, + const CMapId& cmap_id); + static CALLER_ATTACH Builder* NewInstance(ReadableFontData* data, + int32_t offset, + const CMapId& cmap_id); + static CALLER_ATTACH Builder* NewInstance(const CMapId& cmap_id); + virtual ~Builder(); + SegmentList* segments(); + void set_segments(SegmentList* segments); + IntegerList* glyph_id_array(); + void set_glyph_id_array(IntegerList* glyph_id_array); + + protected: + Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id); + Builder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id); + Builder(SegmentList* segments, IntegerList* glyph_id_array, + const CMapId& cmap_id); + explicit Builder(const CMapId& cmap_id); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable( + ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + private: + void Initialize(ReadableFontData* data); + + SegmentList segments_; + IntegerList glyph_id_array_; + }; + + CMap::CharacterIterator* Iterator(); + // CMapTable::CMapFormat4::CharacterIterator + class CharacterIterator : public CMap::CharacterIterator { + public: + bool HasNext(); + int32_t Next(); + virtual ~CharacterIterator() {} + + private: + explicit CharacterIterator(CMapFormat4 *parent); + friend CMap::CharacterIterator* CMapFormat4::Iterator(); + + CMapFormat4* parent_; + int32_t segment_index_; + int32_t first_char_in_segment_; + int32_t last_char_in_segment_; + int32_t next_char_; + bool next_char_set_; + }; + + virtual int32_t GlyphId(int32_t character); + + // Lower level glyph code retrieval that requires processing the Format 4 + // segments to use. + // @param segment the cmap segment + // @param startCode the start code for the segment + // @param character the character to be looked up + // @return the glyph id for the character; CMapTable.NOTDEF if not found + int32_t RetrieveGlyphId(int32_t segment, + int32_t start_count, + int32_t character); + virtual int32_t Language(); + + // Get the count of the number of segments in this cmap. + // @return the number of segments + int32_t seg_count(); + int32_t Length(); + // Get the start code for a segment. + // @param segment the segment in the lookup table + // @return the start code for a segment + int32_t StartCode(int32_t segment); + // Get the end code for a segment. + // @param segment the segment in the look up table + // @return the end code for the segment + int32_t EndCode(int32_t segment); + // Get the id delta for a segment + // @param segment the segment in the look up table + // @return the id delta for the segment + int32_t IdDelta(int32_t segment); + // Get the id range offset for a segment + // @param segment the segment in the look up table + // @return the id range offset for the segment + int32_t IdRangeOffset(int32_t segment); + // Get the location of the id range offset for a segment + // @param segment the segment in the look up table + // @return the location of the id range offset for the segment + int32_t IdRangeOffsetLocation(int32_t segment); + // Declared above to allow friending inside CharacterIterator class. + // CMap::CharacterIterator* Iterator(); + virtual ~CMapFormat4(); + + protected: + CMapFormat4(ReadableFontData* data, const CMapId& cmap_id); + + private: + static int32_t Language(ReadableFontData* data); + static int32_t Length(ReadableFontData* data); + static int32_t SegCount(ReadableFontData* data); + static int32_t StartCode(ReadableFontData* data, + int32_t seg_count, + int32_t index); + static int32_t StartCodeOffset(int32_t seg_count); + static int32_t EndCode(ReadableFontData* data, + int32_t seg_count, + int32_t index); + static int32_t IdDelta(ReadableFontData* data, + int32_t seg_count, + int32_t index); + static int32_t IdDeltaOffset(int32_t seg_count); + static int32_t IdRangeOffset(ReadableFontData* data, + int32_t seg_count, + int32_t index); + static int32_t IdRangeOffsetOffset(int32_t seg_count); + static int32_t GlyphIdArrayOffset(int32_t seg_count); + // Refactored void to bool to work without exceptions. + bool IsValidIndex(int32_t segment); + int32_t GlyphIdArray(int32_t index); + + int32_t seg_count_; + int32_t start_code_offset_; + int32_t end_code_offset_; + int32_t id_delta_offset_; + int32_t id_range_offset_offset_; + int32_t glyph_id_array_offset_; + }; + + // CMapTable::Builder + class Builder : public SubTableContainerTable::Builder, + public RefCounted { + public: + // Constructor scope is public because C++ does not allow base class to + // instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + virtual int32_t SubSerialize(WritableFontData* new_data); + virtual bool SubReadyToSerialize(); + virtual int32_t SubDataSizeToSerialize(); + virtual void SubDataSet(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + CMap::Builder* NewCMapBuilder(const CMapId& cmap_id, + ReadableFontData* data); + // Create a new empty CMapBuilder of the type specified in the id. + CMap::Builder* NewCMapBuilder(int32_t format, const CMapId& cmap_id); + CMap::Builder* CMapBuilder(const CMapId& cmap_id); + + int32_t NumCMaps(); + void SetVersion(int32_t version); + + CMapBuilderMap* GetCMapBuilders(); + + protected: + static CALLER_ATTACH CMap::Builder* CMapBuilder(ReadableFontData* data, + int32_t index); + + private: + void Initialize(ReadableFontData* data); + static int32_t NumCMaps(ReadableFontData* data); + + int32_t version_; + CMapBuilderMap cmap_builders_; + }; + typedef Ptr CMapTableBuilderPtr; + + class CMapIterator { + public: + // If filter is NULL, filter through all tables. + CMapIterator(CMapTable* table, const CMapFilter* filter); + bool HasNext(); + CMap* Next(); + + private: + int32_t table_index_; + const CMapFilter* filter_; + CMapTable* table_; + }; + + // Make a CMapId from a platform_id, encoding_id pair + static CMapId NewCMapId(int32_t platform_id, int32_t encoding_id); + // Make a CMapId from another CMapId + static CMapId NewCMapId(const CMapId& obj); + + // Get the CMap with the specified parameters if it exists. + // Returns NULL otherwise. + CALLER_ATTACH CMap* GetCMap(const int32_t index); + CALLER_ATTACH CMap* GetCMap(const int32_t platform_id, + const int32_t encoding_id); + CALLER_ATTACH CMap* GetCMap(const CMapId GetCMap_id); + + // Get the table version. + virtual int32_t Version(); + + // Get the number of cmaps within the CMap table. + virtual int32_t NumCMaps(); + + // Get the cmap id for the cmap with the given index. + // Note: yes, an object is returned on stack since it's small enough. + // This function is renamed from cmapId to GetCMapId(). + virtual CMapId GetCMapId(int32_t index); + + virtual int32_t PlatformId(int32_t index); + virtual int32_t EncodingId(int32_t index); + + // Get the offset in the table data for the cmap table with the given index. + // The offset is from the beginning of the table. + virtual int32_t Offset(int32_t index); + + virtual ~CMapTable(); + + static const int32_t NOTDEF; + + 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 { + kVersion = 0, + kNumTables = 2, + kEncodingRecordStart = 4, + + // offsets relative to the encoding record + kEncodingRecordPlatformId = 0, + kEncodingRecordEncodingId = 2, + kEncodingRecordOffset = 4, + kEncodingRecordSize = 8, + + kFormat = 0, + + // Format 0: Byte encoding table + kFormat0Format = 0, + kFormat0Length = 2, + kFormat0Language = 4, + kFormat0GlyphIdArray = 6, + + // Format 2: High-byte mapping through table + kFormat2Format = 0, + kFormat2Length = 2, + kFormat2Language = 4, + kFormat2SubHeaderKeys = 6, + kFormat2SubHeaders = 518, + // offset relative to the subHeader structure + kFormat2SubHeader_firstCode = 0, + kFormat2SubHeader_entryCount = 2, + kFormat2SubHeader_idDelta = 4, + kFormat2SubHeader_idRangeOffset = 6, + kFormat2SubHeader_structLength = 8, + + // Format 4: Segment mapping to delta values + kFormat4Format = 0, + kFormat4Length = 2, + kFormat4Language = 4, + kFormat4SegCountX2 = 6, + kFormat4SearchRange = 8, + kFormat4EntrySelector = 10, + kFormat4RangeShift = 12, + kFormat4EndCount = 14, + kFormat4FixedSize = 16, + + // format 6: Trimmed table mapping + kFormat6Format = 0, + kFormat6Length = 2, + kFormat6Language = 4, + kFormat6FirstCode = 6, + kFormat6EntryCount = 8, + kFormat6GlyphIdArray = 10, + + // Format 8: mixed 16-bit and 32-bit coverage + kFormat8Format = 0, + kFormat8Length = 4, + kFormat8Language = 8, + kFormat8Is32 = 12, + kFormat8nGroups204 = 8204, + kFormat8Groups208 = 8208, + // offset relative to the group structure + kFormat8Group_startCharCode = 0, + kFormat8Group_endCharCode = 4, + kFormat8Group_startGlyphId = 8, + kFormat8Group_structLength = 12, + + // Format 10: Trimmed array + kFormat10Format = 0, + kFormat10Length = 4, + kFormat10Language = 8, + kFormat10StartCharCode = 12, + kFormat10NumChars = 16, + kFormat10Glyphs0 = 20, + + // Format 12: Segmented coverage + kFormat12Format = 0, + kFormat12Length = 4, + kFormat12Language = 8, + kFormat12nGroups = 12, + kFormat12Groups = 16, + kFormat12Groups_structLength = 12, + // offsets within the group structure + kFormat12_startCharCode = 0, + kFormat12_endCharCode = 4, + kFormat12_startGlyphId = 8, + + // Format 13: Last Resort Font + kFormat13Format = 0, + kFormat13Length = 4, + kFormat13Language = 8, + kFormat13nGroups = 12, + kFormat13Groups = 16, + kFormat13Groups_structLength = 12, + // offsets within the group structure + kFormat13_startCharCode = 0, + kFormat13_endCharCode = 4, + kFormat13_glyphId = 8, + + // Format 14: Unicode Variation Sequences + kFormat14Format = 0, + kFormat14Length = 2, + + // TODO(stuartg): finish tables + // Default UVS Table + + // Non-default UVS Table + kLast = -1 + }; + }; + + CMapTable(Header* header, ReadableFontData* data); + + // Get the offset in the table data for the encoding record for the cmap with + // the given index. The offset is from the beginning of the table. + static int32_t OffsetForEncodingRecord(int32_t index); +}; +typedef std::vector CMapIdList; +typedef Ptr CMapTablePtr; +typedef std::vector > SegmentList; +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_CMAP_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/font_header_table.cc b/src/sfntly/src/sfntly/table/core/font_header_table.cc new file mode 100644 index 0000000000..60015ca954 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/font_header_table.cc @@ -0,0 +1,265 @@ +/* + * 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/core/font_header_table.h" + +namespace sfntly { +/****************************************************************************** + * FontHeaderTable class + ******************************************************************************/ +FontHeaderTable::~FontHeaderTable() {} + +int32_t FontHeaderTable::TableVersion() { + return data_->ReadFixed(Offset::kTableVersion); +} + +int32_t FontHeaderTable::FontRevision() { + return data_->ReadFixed(Offset::kFontRevision); +} + +int64_t FontHeaderTable::ChecksumAdjustment() { + return data_->ReadULong(Offset::kCheckSumAdjustment); +} + +int64_t FontHeaderTable::MagicNumber() { + return data_->ReadULong(Offset::kMagicNumber); +} + +int32_t FontHeaderTable::FlagsAsInt() { + return data_->ReadUShort(Offset::kFlags); +} + +int32_t FontHeaderTable::UnitsPerEm() { + return data_->ReadUShort(Offset::kUnitsPerEm); +} + +int64_t FontHeaderTable::Created() { + return data_->ReadDateTimeAsLong(Offset::kCreated); +} + +int64_t FontHeaderTable::Modified() { + return data_->ReadDateTimeAsLong(Offset::kModified); +} + +int32_t FontHeaderTable::XMin() { + return data_->ReadUShort(Offset::kXMin); +} + +int32_t FontHeaderTable::YMin() { + return data_->ReadUShort(Offset::kYMin); +} + +int32_t FontHeaderTable::XMax() { + return data_->ReadUShort(Offset::kXMax); +} + +int32_t FontHeaderTable::YMax() { + return data_->ReadUShort(Offset::kYMax); +} + +int32_t FontHeaderTable::MacStyleAsInt() { + return data_->ReadUShort(Offset::kMacStyle); +} + +int32_t FontHeaderTable::LowestRecPPEM() { + return data_->ReadUShort(Offset::kLowestRecPPEM); +} + +int32_t FontHeaderTable::FontDirectionHint() { + return data_->ReadShort(Offset::kFontDirectionHint); +} + +int32_t FontHeaderTable::IndexToLocFormat() { + return data_->ReadShort(Offset::kIndexToLocFormat); +} + +int32_t FontHeaderTable::GlyphDataFormat() { + return data_->ReadShort(Offset::kGlyphDataFormat); +} + +FontHeaderTable::FontHeaderTable(Header* header, ReadableFontData* data) + : Table(header, data) { + IntegerList checksum_ranges; + checksum_ranges.push_back(0); + checksum_ranges.push_back(Offset::kCheckSumAdjustment); + checksum_ranges.push_back(Offset::kMagicNumber); + data_->SetCheckSumRanges(checksum_ranges); +} + +/****************************************************************************** + * FontHeaderTable::Builder class + ******************************************************************************/ +FontHeaderTable::Builder::Builder(Header* header, WritableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +FontHeaderTable::Builder::Builder(Header* header, ReadableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +FontHeaderTable::Builder::~Builder() {} + +CALLER_ATTACH FontDataTable* FontHeaderTable::Builder::SubBuildTable( + ReadableFontData* data) { + FontDataTablePtr table = new FontHeaderTable(header(), data); + return table.Detach(); +} + +int32_t FontHeaderTable::Builder::TableVersion() { + return down_cast(GetTable())->TableVersion(); +} + +void FontHeaderTable::Builder::SetTableVersion(int32_t version) { + InternalWriteData()->WriteFixed(Offset::kTableVersion, version); +} + +int32_t FontHeaderTable::Builder::FontRevision() { + return down_cast(GetTable())->FontRevision(); +} + +void FontHeaderTable::Builder::SetFontRevision(int32_t revision) { + InternalWriteData()->WriteFixed(Offset::kFontRevision, revision); +} + +int64_t FontHeaderTable::Builder::ChecksumAdjustment() { + return down_cast(GetTable())->ChecksumAdjustment(); +} + +void FontHeaderTable::Builder::SetChecksumAdjustment(int64_t adjustment) { + InternalWriteData()->WriteULong(Offset::kCheckSumAdjustment, adjustment); +} + +int64_t FontHeaderTable::Builder::MagicNumber() { + return down_cast(GetTable())->MagicNumber(); +} + +void FontHeaderTable::Builder::SetMagicNumber(int64_t magic_number) { + InternalWriteData()->WriteULong(Offset::kMagicNumber, magic_number); +} + +int32_t FontHeaderTable::Builder::FlagsAsInt() { + return down_cast(GetTable())->FlagsAsInt(); +} + +void FontHeaderTable::Builder::SetFlagsAsInt(int32_t flags) { + InternalWriteData()->WriteUShort(Offset::kFlags, flags); +} + +int32_t FontHeaderTable::Builder::UnitsPerEm() { + return down_cast(GetTable())->UnitsPerEm(); +} + +void FontHeaderTable::Builder::SetUnitsPerEm(int32_t units) { + InternalWriteData()->WriteUShort(Offset::kUnitsPerEm, units); +} + +int64_t FontHeaderTable::Builder::Created() { + return down_cast(GetTable())->Created(); +} + +void FontHeaderTable::Builder::SetCreated(int64_t date) { + InternalWriteData()->WriteDateTime(Offset::kCreated, date); +} + +int64_t FontHeaderTable::Builder::Modified() { + return down_cast(GetTable())->Modified(); +} + +void FontHeaderTable::Builder::SetModified(int64_t date) { + InternalWriteData()->WriteDateTime(Offset::kModified, date); +} + +int32_t FontHeaderTable::Builder::XMin() { + return down_cast(GetTable())->XMin(); +} + +void FontHeaderTable::Builder::SetXMin(int32_t xmin) { + InternalWriteData()->WriteShort(Offset::kXMin, xmin); +} + +int32_t FontHeaderTable::Builder::YMin() { + return down_cast(GetTable())->YMin(); +} + +void FontHeaderTable::Builder::SetYMin(int32_t ymin) { + InternalWriteData()->WriteShort(Offset::kYMin, ymin); +} + +int32_t FontHeaderTable::Builder::XMax() { + return down_cast(GetTable())->XMax(); +} + +void FontHeaderTable::Builder::SetXMax(int32_t xmax) { + InternalWriteData()->WriteShort(Offset::kXMax, xmax); +} + +int32_t FontHeaderTable::Builder::YMax() { + return down_cast(GetTable())->YMax(); +} + +void FontHeaderTable::Builder::SetYMax(int32_t ymax) { + InternalWriteData()->WriteShort(Offset::kYMax, ymax); +} + +int32_t FontHeaderTable::Builder::MacStyleAsInt() { + return down_cast(GetTable())->MacStyleAsInt(); +} + +void FontHeaderTable::Builder::SetMacStyleAsInt(int32_t style) { + InternalWriteData()->WriteUShort(Offset::kMacStyle, style); +} + +int32_t FontHeaderTable::Builder::LowestRecPPEM() { + return down_cast(GetTable())->LowestRecPPEM(); +} + +void FontHeaderTable::Builder::SetLowestRecPPEM(int32_t size) { + InternalWriteData()->WriteUShort(Offset::kLowestRecPPEM, size); +} + +int32_t FontHeaderTable::Builder::FontDirectionHint() { + return down_cast(GetTable())->FontDirectionHint(); +} + +void FontHeaderTable::Builder::SetFontDirectionHint(int32_t hint) { + InternalWriteData()->WriteShort(Offset::kFontDirectionHint, hint); +} + +int32_t FontHeaderTable::Builder::IndexToLocFormat() { + return down_cast(GetTable())->IndexToLocFormat(); +} + +void FontHeaderTable::Builder::SetIndexToLocFormat(int32_t format) { + InternalWriteData()->WriteShort(Offset::kIndexToLocFormat, format); +} + +int32_t FontHeaderTable::Builder::GlyphDataFormat() { + return down_cast(GetTable())->GlyphDataFormat(); +} + +void FontHeaderTable::Builder::SetGlyphDataFormat(int32_t format) { + InternalWriteData()->WriteShort(Offset::kGlyphDataFormat, format); +} + +CALLER_ATTACH FontHeaderTable::Builder* + FontHeaderTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new FontHeaderTable::Builder(header, data); + return builder.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/font_header_table.h b/src/sfntly/src/sfntly/table/core/font_header_table.h new file mode 100644 index 0000000000..841955b423 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/font_header_table.h @@ -0,0 +1,168 @@ +/* + * 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_CORE_FONT_HEADER_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_FONT_HEADER_TABLE_H_ + +#include "sfntly/table/table.h" +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +struct IndexToLocFormat { + enum { + kShortOffset = 0, + kLongOffset = 1 + }; +}; + +struct FontDirectionHint { + enum { + kFullyMixed = 0, + kOnlyStrongLTR = 1, + kStrongLTRAndNeutral = 2, + kOnlyStrongRTL = -1, + kStrongRTLAndNeutral = -2 + }; +}; + +class FontHeaderTable : public Table, public RefCounted { + public: + class Builder : public TableBasedTableBuilder, public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + virtual int32_t TableVersion(); + virtual void SetTableVersion(int32_t version); + virtual int32_t FontRevision(); + virtual void SetFontRevision(int32_t revision); + virtual int64_t ChecksumAdjustment(); + virtual void SetChecksumAdjustment(int64_t adjustment); + virtual int64_t MagicNumber(); + virtual void SetMagicNumber(int64_t magic_number); + virtual int32_t FlagsAsInt(); + virtual void SetFlagsAsInt(int32_t flags); + // TODO(arthurhsu): IMPLEMENT EnumSet Flags() + // TODO(arthurhsu): IMPLEMENT setFlags(EnumSet flags) + virtual int32_t UnitsPerEm(); + virtual void SetUnitsPerEm(int32_t units); + virtual int64_t Created(); + virtual void SetCreated(int64_t date); + virtual int64_t Modified(); + virtual void SetModified(int64_t date); + virtual int32_t XMin(); + virtual void SetXMin(int32_t xmin); + virtual int32_t YMin(); + virtual void SetYMin(int32_t ymin); + virtual int32_t XMax(); + virtual void SetXMax(int32_t xmax); + virtual int32_t YMax(); + virtual void SetYMax(int32_t ymax); + virtual int32_t MacStyleAsInt(); + virtual void SetMacStyleAsInt(int32_t style); + // TODO(arthurhsu): IMPLEMENT EnumSet macStyle() + // TODO(arthurhsu): IMPLEMENT setMacStyle(EnumSet style) + virtual int32_t LowestRecPPEM(); + virtual void SetLowestRecPPEM(int32_t size); + virtual int32_t FontDirectionHint(); + virtual void SetFontDirectionHint(int32_t hint); + virtual int32_t IndexToLocFormat(); + virtual void SetIndexToLocFormat(int32_t format); + virtual int32_t GlyphDataFormat(); + virtual void SetGlyphDataFormat(int32_t format); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + }; + + virtual ~FontHeaderTable(); + int32_t TableVersion(); + int32_t FontRevision(); + + // Get the checksum adjustment. To compute: set it to 0, sum the entire font + // as ULONG, then store 0xB1B0AFBA - sum. + int64_t ChecksumAdjustment(); + + // Get the magic number. Set to 0x5F0F3CF5. + int64_t MagicNumber(); + + // TODO(arthurhsu): IMPLEMENT: EnumSet + int32_t FlagsAsInt(); + // TODO(arthurhsu): IMPLEMENT: Flags() returning EnumSet + + int32_t UnitsPerEm(); + + // Get the created date. Number of seconds since 12:00 midnight, January 1, + // 1904. 64-bit integer. + int64_t Created(); + // Get the modified date. Number of seconds since 12:00 midnight, January 1, + // 1904. 64-bit integer. + int64_t Modified(); + + // Get the x min. For all glyph bounding boxes. + int32_t XMin(); + // Get the y min. For all glyph bounding boxes. + int32_t YMin(); + // Get the x max. For all glyph bounding boxes. + int32_t XMax(); + // Get the y max. For all glyph bounding boxes. + int32_t YMax(); + + // TODO(arthurhsu): IMPLEMENT: EnumSet + int32_t MacStyleAsInt(); + // TODO(arthurhsu): IMPLEMENT: macStyle() returning EnumSet + + int32_t LowestRecPPEM(); + int32_t FontDirectionHint(); // Note: no AsInt() form, already int + int32_t IndexToLocFormat(); // Note: no AsInt() form, already int + int32_t GlyphDataFormat(); + + private: + struct Offset { + enum { + kTableVersion = 0, + kFontRevision = 4, + kCheckSumAdjustment = 8, + kMagicNumber = 12, + kFlags = 16, + kUnitsPerEm = 18, + kCreated = 20, + kModified = 28, + kXMin = 36, + kYMin = 38, + kXMax = 40, + kYMax = 42, + kMacStyle = 44, + kLowestRecPPEM = 46, + kFontDirectionHint = 48, + kIndexToLocFormat = 50, + kGlyphDataFormat = 52 + }; + }; + + FontHeaderTable(Header* header, ReadableFontData* data); +}; +typedef Ptr FontHeaderTablePtr; +typedef Ptr FontHeaderTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_FONT_HEADER_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.cc b/src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.cc new file mode 100644 index 0000000000..50b0cf579d --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.cc @@ -0,0 +1,124 @@ +/* + * 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/core/horizontal_device_metrics_table.h" + +namespace sfntly { +/****************************************************************************** + * HorizontalDeviceMetricsTable class + ******************************************************************************/ +HorizontalDeviceMetricsTable:: ~HorizontalDeviceMetricsTable() {} + +int32_t HorizontalDeviceMetricsTable::Version() { + return data_->ReadUShort(Offset::kVersion); +} + +int32_t HorizontalDeviceMetricsTable::NumRecords() { + return data_->ReadShort(Offset::kNumRecords); +} + +int32_t HorizontalDeviceMetricsTable::RecordSize() { + return data_->ReadLong(Offset::kSizeDeviceRecord); +} + +int32_t HorizontalDeviceMetricsTable::PixelSize(int32_t record_index) { + if (record_index < 0 || record_index >= NumRecords()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundsException(); +#endif + return -1; + } + return data_->ReadUByte(Offset::kRecords + record_index * RecordSize() + + Offset::kDeviceRecordPixelSize); +} + +int32_t HorizontalDeviceMetricsTable::MaxWidth(int32_t record_index) { + if (record_index < 0 || record_index >= NumRecords()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundsException(); +#endif + return -1; + } + return data_->ReadUByte(Offset::kRecords + record_index * RecordSize() + + Offset::kDeviceRecordMaxWidth); +} + +int32_t HorizontalDeviceMetricsTable::Width(int32_t record_index, + int32_t glyph_num) { + if (record_index < 0 || record_index >= NumRecords() || + glyph_num < 0 || glyph_num >= num_glyphs_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundsException(); +#endif + return -1; + } + return data_->ReadUByte(Offset::kRecords + record_index * RecordSize() + + Offset::kDeviceRecordWidths + glyph_num); +} + +HorizontalDeviceMetricsTable::HorizontalDeviceMetricsTable( + Header* header, + ReadableFontData* data, + int32_t num_glyphs) + : Table(header, data), num_glyphs_(num_glyphs) { +} + +/****************************************************************************** + * HorizontalDeviceMetricsTable::Builder class + ******************************************************************************/ +HorizontalDeviceMetricsTable::Builder::Builder(Header* header, + WritableFontData* data) + : TableBasedTableBuilder(header, data), num_glyphs_(-1) { +} + +HorizontalDeviceMetricsTable::Builder::Builder(Header* header, + ReadableFontData* data) + : TableBasedTableBuilder(header, data), num_glyphs_(-1) { +} + +HorizontalDeviceMetricsTable::Builder::~Builder() {} + +CALLER_ATTACH FontDataTable* +HorizontalDeviceMetricsTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new HorizontalDeviceMetricsTable(header(), data, + num_glyphs_); + return table.Detach(); +} + +void HorizontalDeviceMetricsTable::Builder::SetNumGlyphs(int32_t num_glyphs) { + if (num_glyphs < 0) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalArgumentException("Number of glyphs can't be negative."); +#endif + return; + } + num_glyphs_ = num_glyphs; + HorizontalDeviceMetricsTable* table = + down_cast(GetTable()); + if (table) { + table->num_glyphs_ = num_glyphs; + } +} + +CALLER_ATTACH HorizontalDeviceMetricsTable::Builder* +HorizontalDeviceMetricsTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new HorizontalDeviceMetricsTable::Builder(header, data); + return builder.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.h b/src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.h new file mode 100644 index 0000000000..4a27ba0964 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/horizontal_device_metrics_table.h @@ -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. + */ + +#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_DEVICE_METRICS_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_DEVICE_METRICS_TABLE_H_ + +#include "sfntly/table/table.h" +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +// A Horizontal Device Metrics table - 'hdmx' +class HorizontalDeviceMetricsTable + : public Table, + public RefCounted { + public: + class Builder : public TableBasedTableBuilder, public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + void SetNumGlyphs(int32_t num_glyphs); + + private: + int32_t num_glyphs_; + }; + + virtual ~HorizontalDeviceMetricsTable(); + + int32_t Version(); + int32_t NumRecords(); + int32_t RecordSize(); + int32_t PixelSize(int32_t record_index); + int32_t MaxWidth(int32_t record_index); + int32_t Width(int32_t record_index, int32_t glyph_num); + + private: + struct Offset { + enum { + kVersion = 0, + kNumRecords = 2, + kSizeDeviceRecord = 4, + kRecords = 8, + + // Offsets within a device record + kDeviceRecordPixelSize = 0, + kDeviceRecordMaxWidth = 1, + kDeviceRecordWidths = 2, + }; + }; + HorizontalDeviceMetricsTable(Header* header, + ReadableFontData* data, + int32_t num_glyphs); + + int32_t num_glyphs_; +}; +typedef Ptr HorizontalDeviceMetricsTablePtr; +typedef Ptr + HorizontalDeviceMetricsTableBuilderPtr; +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_DEVICE_METRICS_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/horizontal_header_table.cc b/src/sfntly/src/sfntly/table/core/horizontal_header_table.cc new file mode 100644 index 0000000000..43c20585d3 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/horizontal_header_table.cc @@ -0,0 +1,213 @@ +/* + * 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/core/horizontal_header_table.h" + +namespace sfntly { +/****************************************************************************** + * HorizontalHeaderTable class + ******************************************************************************/ +HorizontalHeaderTable:: ~HorizontalHeaderTable() {} + +int32_t HorizontalHeaderTable::TableVersion() { + return data_->ReadFixed(Offset::kVersion); +} + +int32_t HorizontalHeaderTable::Ascender() { + return data_->ReadShort(Offset::kAscender); +} + +int32_t HorizontalHeaderTable::Descender() { + return data_->ReadShort(Offset::kDescender); +} + +int32_t HorizontalHeaderTable::LineGap() { + return data_->ReadShort(Offset::kLineGap); +} + +int32_t HorizontalHeaderTable::AdvanceWidthMax() { + return data_->ReadUShort(Offset::kAdvanceWidthMax); +} + +int32_t HorizontalHeaderTable::MinLeftSideBearing() { + return data_->ReadShort(Offset::kMinLeftSideBearing); +} + +int32_t HorizontalHeaderTable::MinRightSideBearing() { + return data_->ReadShort(Offset::kMinRightSideBearing); +} + +int32_t HorizontalHeaderTable::XMaxExtent() { + return data_->ReadShort(Offset::kXMaxExtent); +} + +int32_t HorizontalHeaderTable::CaretSlopeRise() { + return data_->ReadShort(Offset::kCaretSlopeRise); +} + +int32_t HorizontalHeaderTable::CaretSlopeRun() { + return data_->ReadShort(Offset::kCaretSlopeRun); +} + +int32_t HorizontalHeaderTable::CaretOffset() { + return data_->ReadShort(Offset::kCaretOffset); +} + +int32_t HorizontalHeaderTable::MetricDataFormat() { + return data_->ReadShort(Offset::kMetricDataFormat); +} + +int32_t HorizontalHeaderTable::NumberOfHMetrics() { + return data_->ReadUShort(Offset::kNumberOfHMetrics); +} + +HorizontalHeaderTable:: HorizontalHeaderTable(Header* header, + ReadableFontData* data) + : Table(header, data) { +} + +/****************************************************************************** + * HorizontalHeaderTable::Builder class + ******************************************************************************/ +HorizontalHeaderTable::Builder::Builder(Header* header, WritableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +HorizontalHeaderTable::Builder::Builder(Header* header, ReadableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +HorizontalHeaderTable::Builder::~Builder() {} + +CALLER_ATTACH FontDataTable* + HorizontalHeaderTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new HorizontalHeaderTable(header(), data); + return table.Detach(); +} + +CALLER_ATTACH HorizontalHeaderTable::Builder* + HorizontalHeaderTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new HorizontalHeaderTable::Builder(header, data); + return builder.Detach(); +} + +int32_t HorizontalHeaderTable::Builder::TableVersion() { + return InternalReadData()->ReadFixed(Offset::kVersion); +} + +void HorizontalHeaderTable::Builder::SetTableVersion(int32_t version) { + InternalWriteData()->WriteFixed(Offset::kVersion, version); +} + +int32_t HorizontalHeaderTable::Builder::Ascender() { + return InternalReadData()->ReadShort(Offset::kAscender); +} + +void HorizontalHeaderTable::Builder::SetAscender(int32_t ascender) { + InternalWriteData()->WriteShort(Offset::kVersion, ascender); +} + +int32_t HorizontalHeaderTable::Builder::Descender() { + return InternalReadData()->ReadShort(Offset::kDescender); +} + +void HorizontalHeaderTable::Builder::SetDescender(int32_t descender) { + InternalWriteData()->WriteShort(Offset::kDescender, descender); +} + +int32_t HorizontalHeaderTable::Builder::LineGap() { + return InternalReadData()->ReadShort(Offset::kLineGap); +} + +void HorizontalHeaderTable::Builder::SetLineGap(int32_t line_gap) { + InternalWriteData()->WriteShort(Offset::kLineGap, line_gap); +} + +int32_t HorizontalHeaderTable::Builder::AdvanceWidthMax() { + return InternalReadData()->ReadUShort(Offset::kAdvanceWidthMax); +} + +void HorizontalHeaderTable::Builder::SetAdvanceWidthMax(int32_t value) { + InternalWriteData()->WriteUShort(Offset::kAdvanceWidthMax, value); +} + +int32_t HorizontalHeaderTable::Builder::MinLeftSideBearing() { + return InternalReadData()->ReadShort(Offset::kMinLeftSideBearing); +} + +void HorizontalHeaderTable::Builder::SetMinLeftSideBearing(int32_t value) { + InternalWriteData()->WriteShort(Offset::kMinLeftSideBearing, value); +} + +int32_t HorizontalHeaderTable::Builder::MinRightSideBearing() { + return InternalReadData()->ReadShort(Offset::kMinRightSideBearing); +} + +void HorizontalHeaderTable::Builder::SetMinRightSideBearing(int32_t value) { + InternalWriteData()->WriteShort(Offset::kMinRightSideBearing, value); +} + +int32_t HorizontalHeaderTable::Builder::XMaxExtent() { + return InternalReadData()->ReadShort(Offset::kXMaxExtent); +} + +void HorizontalHeaderTable::Builder::SetXMaxExtent(int32_t value) { + InternalWriteData()->WriteShort(Offset::kXMaxExtent, value); +} + +int32_t HorizontalHeaderTable::Builder::CaretSlopeRise() { + return InternalReadData()->ReadUShort(Offset::kCaretSlopeRise); +} + +void HorizontalHeaderTable::Builder::SetCaretSlopeRise(int32_t value) { + InternalWriteData()->WriteUShort(Offset::kCaretSlopeRise, value); +} + +int32_t HorizontalHeaderTable::Builder::CaretSlopeRun() { + return InternalReadData()->ReadUShort(Offset::kCaretSlopeRun); +} + +void HorizontalHeaderTable::Builder::SetCaretSlopeRun(int32_t value) { + InternalWriteData()->WriteUShort(Offset::kCaretSlopeRun, value); +} + +int32_t HorizontalHeaderTable::Builder::CaretOffset() { + return InternalReadData()->ReadUShort(Offset::kCaretOffset); +} + +void HorizontalHeaderTable::Builder::SetCaretOffset(int32_t value) { + InternalWriteData()->WriteUShort(Offset::kCaretOffset, value); +} + +int32_t HorizontalHeaderTable::Builder::MetricDataFormat() { + return InternalReadData()->ReadUShort(Offset::kMetricDataFormat); +} + +void HorizontalHeaderTable::Builder::SetMetricDataFormat(int32_t value) { + InternalWriteData()->WriteUShort(Offset::kMetricDataFormat, value); +} + +int32_t HorizontalHeaderTable::Builder::NumberOfHMetrics() { + return InternalReadData()->ReadUShort(Offset::kNumberOfHMetrics); +} + +void HorizontalHeaderTable::Builder::SetNumberOfHMetrics(int32_t value) { + InternalWriteData()->WriteUShort(Offset::kNumberOfHMetrics, value); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/horizontal_header_table.h b/src/sfntly/src/sfntly/table/core/horizontal_header_table.h new file mode 100644 index 0000000000..71f30b4475 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/horizontal_header_table.h @@ -0,0 +1,111 @@ +/* + * 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_CORE_HORIZONTAL_HEADER_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_HEADER_TABLE_H_ + +#include "sfntly/table/table.h" +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +// A Horizontal Header table - 'hhea'. +class HorizontalHeaderTable : public Table, + public RefCounted { + public: + // Builder for a Horizontal Header table - 'hhea'. + class Builder : public TableBasedTableBuilder, public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + int32_t TableVersion(); + void SetTableVersion(int32_t version); + int32_t Ascender(); + void SetAscender(int32_t ascender); + int32_t Descender(); + void SetDescender(int32_t descender); + int32_t LineGap(); + void SetLineGap(int32_t line_gap); + int32_t AdvanceWidthMax(); + void SetAdvanceWidthMax(int32_t value); + int32_t MinLeftSideBearing(); + void SetMinLeftSideBearing(int32_t value); + int32_t MinRightSideBearing(); + void SetMinRightSideBearing(int32_t value); + int32_t XMaxExtent(); + void SetXMaxExtent(int32_t value); + int32_t CaretSlopeRise(); + void SetCaretSlopeRise(int32_t value); + int32_t CaretSlopeRun(); + void SetCaretSlopeRun(int32_t value); + int32_t CaretOffset(); + void SetCaretOffset(int32_t value); + int32_t MetricDataFormat(); + void SetMetricDataFormat(int32_t value); + int32_t NumberOfHMetrics(); + void SetNumberOfHMetrics(int32_t value); + }; + + virtual ~HorizontalHeaderTable(); + int32_t TableVersion(); + int32_t Ascender(); + int32_t Descender(); + int32_t LineGap(); + int32_t AdvanceWidthMax(); + int32_t MinLeftSideBearing(); + int32_t MinRightSideBearing(); + int32_t XMaxExtent(); + int32_t CaretSlopeRise(); + int32_t CaretSlopeRun(); + int32_t CaretOffset(); + int32_t MetricDataFormat(); + int32_t NumberOfHMetrics(); + + private: + struct Offset { + enum { + kVersion = 0, + kAscender = 4, + kDescender = 6, + kLineGap = 8, + kAdvanceWidthMax = 10, + kMinLeftSideBearing = 12, + kMinRightSideBearing = 14, + kXMaxExtent = 16, + kCaretSlopeRise = 18, + kCaretSlopeRun = 20, + kCaretOffset = 22, + kMetricDataFormat = 32, + kNumberOfHMetrics = 34, + }; + }; + + HorizontalHeaderTable(Header* header, ReadableFontData* data); +}; +typedef Ptr HorizontalHeaderTablePtr; +typedef Ptr HorizontalHeaderTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_HEADER_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/horizontal_metrics_table.cc b/src/sfntly/src/sfntly/table/core/horizontal_metrics_table.cc new file mode 100644 index 0000000000..156387daaf --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/horizontal_metrics_table.cc @@ -0,0 +1,138 @@ +/* + * 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/core/horizontal_metrics_table.h" +#include "sfntly/port/exception_type.h" + +namespace sfntly { +/****************************************************************************** + * HorizontalMetricsTable class + ******************************************************************************/ +HorizontalMetricsTable::~HorizontalMetricsTable() {} + +int32_t HorizontalMetricsTable::NumberOfHMetrics() { + return num_hmetrics_; +} + +int32_t HorizontalMetricsTable::NumberOfLSBs() { + return num_glyphs_ - num_hmetrics_; +} + +int32_t HorizontalMetricsTable::HMetricAdvanceWidth(int32_t entry) { + if (entry > num_hmetrics_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException(); +#endif + return 0; + } + int32_t offset = Offset::kHMetricsStart + (entry * Offset::kHMetricsSize) + + Offset::kHMetricsAdvanceWidth; + return data_->ReadUShort(offset); +} + +int32_t HorizontalMetricsTable::HMetricLSB(int32_t entry) { + if (entry > num_hmetrics_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException(); +#endif + return 0; + } + int32_t offset = Offset::kHMetricsStart + (entry * Offset::kHMetricsSize) + + Offset::kHMetricsLeftSideBearing; + return data_->ReadShort(offset); +} + +int32_t HorizontalMetricsTable::LsbTableEntry(int32_t entry) { + if (entry > num_hmetrics_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException(); +#endif + return 0; + } + int32_t offset = Offset::kHMetricsStart + (entry * Offset::kHMetricsSize) + + Offset::kLeftSideBearingSize; + return data_->ReadShort(offset); +} + +int32_t HorizontalMetricsTable::AdvanceWidth(int32_t glyph_id) { + if (glyph_id < num_hmetrics_) { + return HMetricAdvanceWidth(glyph_id); + } + return HMetricAdvanceWidth(glyph_id - num_hmetrics_); +} + +int32_t HorizontalMetricsTable::LeftSideBearing(int32_t glyph_id) { + if (glyph_id < num_hmetrics_) { + return HMetricLSB(glyph_id); + } + return LsbTableEntry(glyph_id - num_hmetrics_); +} + +HorizontalMetricsTable::HorizontalMetricsTable(Header* header, + ReadableFontData* data, + int32_t num_hmetrics, + int32_t num_glyphs) + : Table(header, data), + num_hmetrics_(num_hmetrics), + num_glyphs_(num_glyphs) { +} + +/****************************************************************************** + * HorizontalMetricsTable::Builder class + ******************************************************************************/ +HorizontalMetricsTable::Builder::Builder(Header* header, WritableFontData* data) + : TableBasedTableBuilder(header, data), num_hmetrics_(-1), num_glyphs_(-1) { +} + +HorizontalMetricsTable::Builder::Builder(Header* header, ReadableFontData* data) + : TableBasedTableBuilder(header, data), num_hmetrics_(-1), num_glyphs_(-1) { +} + +HorizontalMetricsTable::Builder::~Builder() {} + +CALLER_ATTACH FontDataTable* + HorizontalMetricsTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = + new HorizontalMetricsTable(header(), data, num_hmetrics_, num_glyphs_); + return table.Detach(); +} + +CALLER_ATTACH HorizontalMetricsTable::Builder* + HorizontalMetricsTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new HorizontalMetricsTable::Builder(header, data); + return builder.Detach(); +} + +void HorizontalMetricsTable::Builder::SetNumberOfHMetrics( + int32_t num_hmetrics) { + assert(num_hmetrics >= 0); + num_hmetrics_ = num_hmetrics; + HorizontalMetricsTable* table = + down_cast(this->GetTable()); + table->num_hmetrics_ = num_hmetrics; +} + +void HorizontalMetricsTable::Builder::SetNumGlyphs(int32_t num_glyphs) { + assert(num_glyphs >= 0); + num_glyphs_ = num_glyphs; + HorizontalMetricsTable* table = + down_cast(this->GetTable()); + table->num_glyphs_ = num_glyphs; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/horizontal_metrics_table.h b/src/sfntly/src/sfntly/table/core/horizontal_metrics_table.h new file mode 100644 index 0000000000..44b51f2d79 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/horizontal_metrics_table.h @@ -0,0 +1,87 @@ +/* + * 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_CORE_HORIZONTAL_METRICS_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_METRICS_TABLE_H_ + +#include "sfntly/table/table.h" +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +// A Horizontal Metrics table - 'hmtx'. +class HorizontalMetricsTable : public Table, + public RefCounted { + public: + // Builder for a Horizontal Metrics Table - 'hmtx'. + class Builder : public TableBasedTableBuilder, public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + void SetNumberOfHMetrics(int32_t num_hmetrics); + void SetNumGlyphs(int32_t num_glyphs); + + private: + int32_t num_hmetrics_; + int32_t num_glyphs_; + }; + + virtual ~HorizontalMetricsTable(); + int32_t NumberOfHMetrics(); + int32_t NumberOfLSBs(); + int32_t HMetricAdvanceWidth(int32_t entry); + int32_t HMetricLSB(int32_t entry); + int32_t LsbTableEntry(int32_t entry); + int32_t AdvanceWidth(int32_t glyph_id); + int32_t LeftSideBearing(int32_t glyph_id); + + private: + struct Offset { + enum { + // hMetrics + kHMetricsStart = 0, + kHMetricsSize = 4, + + // Offset within an hMetric + kHMetricsAdvanceWidth = 0, + kHMetricsLeftSideBearing = 2, + + kLeftSideBearingSize = 2 + }; + }; + + HorizontalMetricsTable(Header* header, + ReadableFontData* data, + int32_t num_hmetrics, + int32_t num_glyphs); + + int32_t num_hmetrics_; + int32_t num_glyphs_; +}; +typedef Ptr HorizontalMetricsTablePtr; +typedef Ptr HorizontalMetricsTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_METRICS_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/maximum_profile_table.cc b/src/sfntly/src/sfntly/table/core/maximum_profile_table.cc new file mode 100644 index 0000000000..a8ac3bc93a --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/maximum_profile_table.cc @@ -0,0 +1,240 @@ +/* + * 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/core/maximum_profile_table.h" + +namespace sfntly { +/****************************************************************************** + * MaximumProfileTable class + ******************************************************************************/ +MaximumProfileTable::~MaximumProfileTable() {} + +int32_t MaximumProfileTable::TableVersion() { + return data_->ReadFixed(Offset::kVersion); +} + +int32_t MaximumProfileTable::NumGlyphs() { + return data_->ReadUShort(Offset::kNumGlyphs); +} + +int32_t MaximumProfileTable::MaxPoints() { + return data_->ReadUShort(Offset::kMaxPoints); +} + +int32_t MaximumProfileTable::MaxContours() { + return data_->ReadUShort(Offset::kMaxContours); +} + +int32_t MaximumProfileTable::MaxCompositePoints() { + return data_->ReadUShort(Offset::kMaxCompositePoints); +} + +int32_t MaximumProfileTable::MaxCompositeContours() { + return data_->ReadUShort(Offset::kMaxCompositeContours); +} + +int32_t MaximumProfileTable::MaxZones() { + return data_->ReadUShort(Offset::kMaxZones); +} + +int32_t MaximumProfileTable::MaxTwilightPoints() { + return data_->ReadUShort(Offset::kMaxTwilightPoints); +} + +int32_t MaximumProfileTable::MaxStorage() { + return data_->ReadUShort(Offset::kMaxStorage); +} + +int32_t MaximumProfileTable::MaxFunctionDefs() { + return data_->ReadUShort(Offset::kMaxFunctionDefs); +} + +int32_t MaximumProfileTable::MaxStackElements() { + return data_->ReadUShort(Offset::kMaxStackElements); +} + +int32_t MaximumProfileTable::MaxSizeOfInstructions() { + return data_->ReadUShort(Offset::kMaxSizeOfInstructions); +} + +int32_t MaximumProfileTable::MaxComponentElements() { + return data_->ReadUShort(Offset::kMaxComponentElements); +} + +int32_t MaximumProfileTable::MaxComponentDepth() { + return data_->ReadUShort(Offset::kMaxComponentDepth); +} + +MaximumProfileTable::MaximumProfileTable(Header* header, + ReadableFontData* data) + : Table(header, data) { +} + +/****************************************************************************** + * MaximumProfileTable::Builder class + ******************************************************************************/ +MaximumProfileTable::Builder::Builder(Header* header, WritableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +MaximumProfileTable::Builder::Builder(Header* header, ReadableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +MaximumProfileTable::Builder::~Builder() {} + +CALLER_ATTACH FontDataTable* + MaximumProfileTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new MaximumProfileTable(header(), data); + return table.Detach(); +} + +CALLER_ATTACH MaximumProfileTable::Builder* + MaximumProfileTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new MaximumProfileTable::Builder(header, data); + return builder.Detach(); +} + +int32_t MaximumProfileTable::Builder::TableVersion() { + return InternalReadData()->ReadUShort(Offset::kVersion); +} + +void MaximumProfileTable::Builder::SetTableVersion(int32_t version) { + InternalWriteData()->WriteUShort(Offset::kVersion, version); +} + +int32_t MaximumProfileTable::Builder::NumGlyphs() { + return InternalReadData()->ReadUShort(Offset::kNumGlyphs); +} + +void MaximumProfileTable::Builder::SetNumGlyphs(int32_t num_glyphs) { + InternalWriteData()->WriteUShort(Offset::kNumGlyphs, num_glyphs); +} + +int32_t MaximumProfileTable::Builder::MaxPoints() { + return InternalReadData()->ReadUShort(Offset::kMaxPoints); +} + +void MaximumProfileTable::Builder::SetMaxPoints(int32_t max_points) { + InternalWriteData()->WriteUShort(Offset::kMaxPoints, max_points); +} + +int32_t MaximumProfileTable::Builder::MaxContours() { + return InternalReadData()->ReadUShort(Offset::kMaxContours); +} + +void MaximumProfileTable::Builder::SetMaxContours(int32_t max_contours) { + InternalWriteData()->WriteUShort(Offset::kMaxContours, max_contours); +} + +int32_t MaximumProfileTable::Builder::MaxCompositePoints() { + return InternalReadData()->ReadUShort(Offset::kMaxCompositePoints); +} + +void MaximumProfileTable::Builder::SetMaxCompositePoints( + int32_t max_composite_points) { + InternalWriteData()->WriteUShort(Offset::kMaxCompositePoints, + max_composite_points); +} + +int32_t MaximumProfileTable::Builder::MaxCompositeContours() { + return InternalReadData()->ReadUShort(Offset::kMaxCompositeContours); +} + +void MaximumProfileTable::Builder::SetMaxCompositeContours( + int32_t max_composite_contours) { + InternalWriteData()->WriteUShort(Offset::kMaxCompositeContours, + max_composite_contours); +} + +int32_t MaximumProfileTable::Builder::MaxZones() { + return InternalReadData()->ReadUShort(Offset::kMaxZones); +} + +void MaximumProfileTable::Builder::SetMaxZones(int32_t max_zones) { + InternalWriteData()->WriteUShort(Offset::kMaxZones, max_zones); +} + +int32_t MaximumProfileTable::Builder::MaxTwilightPoints() { + return InternalReadData()->ReadUShort(Offset::kMaxTwilightPoints); +} + +void MaximumProfileTable::Builder::SetMaxTwilightPoints( + int32_t max_twilight_points) { + InternalWriteData()->WriteUShort(Offset::kMaxTwilightPoints, + max_twilight_points); +} + +int32_t MaximumProfileTable::Builder::MaxStorage() { + return InternalReadData()->ReadUShort(Offset::kMaxStorage); +} + +void MaximumProfileTable::Builder::SetMaxStorage(int32_t max_storage) { + InternalWriteData()->WriteUShort(Offset::kMaxStorage, max_storage); +} + +int32_t MaximumProfileTable::Builder::MaxFunctionDefs() { + return InternalReadData()->ReadUShort(Offset::kMaxFunctionDefs); +} + +void MaximumProfileTable::Builder::SetMaxFunctionDefs( + int32_t max_function_defs) { + InternalWriteData()->WriteUShort(Offset::kMaxFunctionDefs, max_function_defs); +} + +int32_t MaximumProfileTable::Builder::MaxStackElements() { + return InternalReadData()->ReadUShort(Offset::kMaxStackElements); +} + +void MaximumProfileTable::Builder::SetMaxStackElements( + int32_t max_stack_elements) { + InternalWriteData()->WriteUShort(Offset::kMaxStackElements, + max_stack_elements); +} + +int32_t MaximumProfileTable::Builder::MaxSizeOfInstructions() { + return InternalReadData()->ReadUShort(Offset::kMaxSizeOfInstructions); +} + +void MaximumProfileTable::Builder::SetMaxSizeOfInstructions( + int32_t max_size_of_instructions) { + InternalWriteData()->WriteUShort(Offset::kMaxSizeOfInstructions, + max_size_of_instructions); +} + +int32_t MaximumProfileTable::Builder::MaxComponentElements() { + return InternalReadData()->ReadUShort(Offset::kMaxComponentElements); +} + +void MaximumProfileTable::Builder::SetMaxComponentElements( + int32_t max_component_elements) { + InternalWriteData()->WriteUShort(Offset::kMaxComponentElements, + max_component_elements); +} + +int32_t MaximumProfileTable::Builder::MaxComponentDepth() { + return InternalReadData()->ReadUShort(Offset::kMaxComponentDepth); +} + +void MaximumProfileTable::Builder::SetMaxComponentDepth( + int32_t max_component_depth) { + InternalWriteData()->WriteUShort(Offset::kMaxComponentDepth, + max_component_depth); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/maximum_profile_table.h b/src/sfntly/src/sfntly/table/core/maximum_profile_table.h new file mode 100644 index 0000000000..e7c5abb3ff --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/maximum_profile_table.h @@ -0,0 +1,120 @@ +/* + * 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_CORE_MAXIMUM_PROFILE_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_MAXIMUM_PROFILE_TABLE_H_ + +#include "sfntly/port/refcount.h" +#include "sfntly/table/table.h" +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +// A Maximum Profile table - 'maxp'. +class MaximumProfileTable : public Table, + public RefCounted { + public: + // Builder for a Maximum Profile table - 'maxp'. + class Builder : public TableBasedTableBuilder, public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + int32_t TableVersion(); + void SetTableVersion(int32_t version); + int32_t NumGlyphs(); + void SetNumGlyphs(int32_t num_glyphs); + int32_t MaxPoints(); + void SetMaxPoints(int32_t max_points); + int32_t MaxContours(); + void SetMaxContours(int32_t max_contours); + int32_t MaxCompositePoints(); + void SetMaxCompositePoints(int32_t max_composite_points); + int32_t MaxCompositeContours(); + void SetMaxCompositeContours(int32_t max_composite_contours); + int32_t MaxZones(); + void SetMaxZones(int32_t max_zones); + int32_t MaxTwilightPoints(); + void SetMaxTwilightPoints(int32_t max_twilight_points); + int32_t MaxStorage(); + void SetMaxStorage(int32_t max_storage); + int32_t MaxFunctionDefs(); + void SetMaxFunctionDefs(int32_t max_function_defs); + int32_t MaxStackElements(); + void SetMaxStackElements(int32_t max_stack_elements); + int32_t MaxSizeOfInstructions(); + void SetMaxSizeOfInstructions(int32_t max_size_of_instructions); + int32_t MaxComponentElements(); + void SetMaxComponentElements(int32_t max_component_elements); + int32_t MaxComponentDepth(); + void SetMaxComponentDepth(int32_t max_component_depth); + }; + + virtual ~MaximumProfileTable(); + int32_t TableVersion(); + int32_t NumGlyphs(); + int32_t MaxPoints(); + int32_t MaxContours(); + int32_t MaxCompositePoints(); + int32_t MaxCompositeContours(); + int32_t MaxZones(); + int32_t MaxTwilightPoints(); + int32_t MaxStorage(); + int32_t MaxFunctionDefs(); + int32_t MaxStackElements(); + int32_t MaxSizeOfInstructions(); + int32_t MaxComponentElements(); + int32_t MaxComponentDepth(); + + private: + struct Offset { + enum { + // version 0.5 and 1.0 + kVersion = 0, + kNumGlyphs = 4, + + // version 1.0 + kMaxPoints = 6, + kMaxContours = 8, + kMaxCompositePoints = 10, + kMaxCompositeContours = 12, + kMaxZones = 14, + kMaxTwilightPoints = 16, + kMaxStorage = 18, + kMaxFunctionDefs = 20, + kMaxInstructionDefs = 22, + kMaxStackElements = 24, + kMaxSizeOfInstructions = 26, + kMaxComponentElements = 28, + kMaxComponentDepth = 30, + }; + }; + + MaximumProfileTable(Header* header, ReadableFontData* data); +}; +typedef Ptr MaximumProfileTablePtr; +typedef Ptr MaximumProfileTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_MAXIMUM_PROFILE_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/name_table.cc b/src/sfntly/src/sfntly/table/core/name_table.cc new file mode 100644 index 0000000000..5f6d5a5172 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/name_table.cc @@ -0,0 +1,721 @@ +/* + * 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/core/name_table.h" + +#include +#include + +#include + +#include "sfntly/font.h" +#include "sfntly/port/exception_type.h" + +namespace sfntly { +/****************************************************************************** + * NameTable::NameEntryId class + ******************************************************************************/ +NameTable::NameEntryId::NameEntryId() + : platform_id_(0), + encoding_id_(0), + language_id_(0), + name_id_(0) { +} + +NameTable::NameEntryId::NameEntryId(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) + : platform_id_(platform_id), + encoding_id_(encoding_id), + language_id_(language_id), + name_id_(name_id) { +} + +NameTable::NameEntryId::NameEntryId(const NameTable::NameEntryId& rhs) { + *this = rhs; +} + +const NameTable::NameEntryId& + NameTable::NameEntryId::operator=(const NameTable::NameEntryId& rhs) const { + platform_id_ = rhs.platform_id_; + encoding_id_ = rhs.encoding_id_; + language_id_ = rhs.language_id_; + name_id_ = rhs.name_id_; + return *this; +} + +bool NameTable::NameEntryId::operator==(const NameEntryId& rhs) const { + return platform_id_ == rhs.platform_id_ && + encoding_id_ == rhs.encoding_id_ && + language_id_ == rhs.language_id_ && + name_id_ == rhs.name_id_; +} + +bool NameTable::NameEntryId::operator<(const NameEntryId& rhs) const { + if (platform_id_ != rhs.platform_id_) return platform_id_ < rhs.platform_id_; + if (encoding_id_ != rhs.encoding_id_) return encoding_id_ < rhs.encoding_id_; + if (language_id_ != rhs.language_id_) return language_id_ < rhs.language_id_; + return name_id_ < rhs.name_id_; +} + +/****************************************************************************** + * NameTable::NameEntry class + ******************************************************************************/ +NameTable::NameEntry::NameEntry() { + Init(0, 0, 0, 0, NULL); +} + +NameTable::NameEntry::NameEntry(const NameEntryId& name_entry_id, + const ByteVector& name_bytes) { + Init(name_entry_id.platform_id(), + name_entry_id.encoding_id(), + name_entry_id.language_id(), + name_entry_id.name_id(), + &name_bytes); +} + +NameTable::NameEntry::NameEntry(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id, + const ByteVector& name_bytes) { + Init(platform_id, encoding_id, language_id, name_id, &name_bytes); +} + +NameTable::NameEntry::~NameEntry() {} + +ByteVector* NameTable::NameEntry::NameAsBytes() { + return &name_bytes_; +} + +int32_t NameTable::NameEntry::NameBytesLength() { + return name_bytes_.size(); +} + +UChar* NameTable::NameEntry::Name() { + return NameTable::ConvertFromNameBytes(&name_bytes_, + platform_id(), + encoding_id()); +} + +bool NameTable::NameEntry::operator==(const NameEntry& rhs) const { + return (name_entry_id_ == rhs.name_entry_id_ && + name_bytes_ == rhs.name_bytes_); +} + +void NameTable::NameEntry::Init(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id, + const ByteVector* name_bytes) { + name_entry_id_ = NameEntryId(platform_id, encoding_id, language_id, name_id); + if (name_bytes) { + name_bytes_ = *name_bytes; + } else { + name_bytes_.clear(); + } +} + +/****************************************************************************** + * NameTable::NameEntryBuilder class + ******************************************************************************/ +NameTable::NameEntryBuilder::NameEntryBuilder() { + Init(0, 0, 0, 0, NULL); +} + +NameTable::NameEntryBuilder::NameEntryBuilder(const NameEntryId& name_entry_id, + const ByteVector& name_bytes) { + Init(name_entry_id.platform_id(), + name_entry_id.encoding_id(), + name_entry_id.language_id(), + name_entry_id.name_id(), + &name_bytes); +} + +NameTable::NameEntryBuilder::NameEntryBuilder( + const NameEntryId& name_entry_id) { + Init(name_entry_id.platform_id(), + name_entry_id.encoding_id(), + name_entry_id.language_id(), + name_entry_id.name_id(), + NULL); +} + +NameTable::NameEntryBuilder::NameEntryBuilder(NameEntry* b) { + Init(b->platform_id(), + b->encoding_id(), + b->language_id(), + b->name_id(), + b->NameAsBytes()); +} + +NameTable::NameEntryBuilder::~NameEntryBuilder() {} + +void NameTable::NameEntryBuilder::SetName(const UChar* name) { + if (name == NULL) { + name_entry_->name_bytes_.clear(); + return; + } + NameTable::ConvertToNameBytes(name, + name_entry_->platform_id(), + name_entry_->encoding_id(), + &name_entry_->name_bytes_); +} + +void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes) { + name_entry_->name_bytes_.clear(); + std::copy(name_bytes.begin(), + name_bytes.end(), + name_entry_->name_bytes_.begin()); +} + +void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes, + int32_t offset, + int32_t length) { + name_entry_->name_bytes_.clear(); + std::copy(name_bytes.begin() + offset, + name_bytes.begin() + offset + length, + name_entry_->name_bytes_.begin()); +} + +void NameTable::NameEntryBuilder::Init(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id, + const ByteVector* name_bytes) { + name_entry_ = new NameEntry(); + name_entry_->Init(platform_id, encoding_id, language_id, name_id, name_bytes); +} + +/****************************************************************************** + * NameTable::NameEntryFilterInPlace class (C++ port only) + ******************************************************************************/ +NameTable::NameEntryFilterInPlace::NameEntryFilterInPlace(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) + : platform_id_(platform_id), + encoding_id_(encoding_id), + language_id_(language_id), + name_id_(name_id) { +} + +bool NameTable::NameEntryFilterInPlace::Accept(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) { + return (platform_id_ == platform_id && + encoding_id_ == encoding_id && + language_id_ == language_id && + name_id_ == name_id); +} + +/****************************************************************************** + * NameTable::NameEntryIterator class + ******************************************************************************/ +NameTable::NameEntryIterator::NameEntryIterator(NameTable* table) + : RefIterator(table), + name_index_(0), + filter_(NULL) { +} + +NameTable::NameEntryIterator::NameEntryIterator(NameTable* table, + NameEntryFilter* filter) + : RefIterator(table), + name_index_(0), + filter_(filter) { +} + +bool NameTable::NameEntryIterator::HasNext() { + if (!filter_) { + if (name_index_ < container()->NameCount()) { + return true; + } + return false; + } + for (; name_index_ < container()->NameCount(); ++name_index_) { + if (filter_->Accept(container()->PlatformId(name_index_), + container()->EncodingId(name_index_), + container()->LanguageId(name_index_), + container()->NameId(name_index_))) { + return true; + } + } + return false; +} + +CALLER_ATTACH NameTable::NameEntry* NameTable::NameEntryIterator::Next() { + if (!HasNext()) + return NULL; + return container()->GetNameEntry(name_index_++); +} + +/****************************************************************************** + * NameTable::Builder class + ******************************************************************************/ +NameTable::Builder::Builder(Header* header, WritableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +NameTable::Builder::Builder(Header* header, ReadableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +CALLER_ATTACH NameTable::Builder* + NameTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new NameTable::Builder(header, data); + return builder.Detach(); +} + +void NameTable::Builder::RevertNames() { + name_entry_map_.clear(); + set_model_changed(false); +} + +int32_t NameTable::Builder::BuilderCount() { + GetNameBuilders(); // Ensure name_entry_map_ is built. + return (int32_t)name_entry_map_.size(); +} + +bool NameTable::Builder::Has(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) { + NameEntryId probe(platform_id, encoding_id, language_id, name_id); + GetNameBuilders(); // Ensure name_entry_map_ is built. + return (name_entry_map_.find(probe) != name_entry_map_.end()); +} + +CALLER_ATTACH NameTable::NameEntryBuilder* + NameTable::Builder::NameBuilder(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) { + NameEntryId probe(platform_id, encoding_id, language_id, name_id); + NameEntryBuilderMap builders; + GetNameBuilders(); // Ensure name_entry_map_ is built. + if (name_entry_map_.find(probe) != name_entry_map_.end()) { + return name_entry_map_[probe]; + } + NameEntryBuilderPtr builder = new NameEntryBuilder(probe); + name_entry_map_[probe] = builder; + return builder.Detach(); +} + +bool NameTable::Builder::Remove(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) { + NameEntryId probe(platform_id, encoding_id, language_id, name_id); + GetNameBuilders(); // Ensure name_entry_map_ is built. + NameEntryBuilderMap::iterator position = name_entry_map_.find(probe); + if (position != name_entry_map_.end()) { + name_entry_map_.erase(position); + return true; + } + return false; +} + +CALLER_ATTACH FontDataTable* + NameTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new NameTable(header(), data); + return table.Detach(); +} + +void NameTable::Builder::SubDataSet() { + name_entry_map_.clear(); + set_model_changed(false); +} + +int32_t NameTable::Builder::SubDataSizeToSerialize() { + if (name_entry_map_.empty()) { + return 0; + } + + int32_t size = NameTable::Offset::kNameRecordStart + + name_entry_map_.size() * NameTable::Offset::kNameRecordSize; + for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(), + end = name_entry_map_.end(); + b != end; ++b) { + NameEntryBuilderPtr p = b->second; + NameEntry* entry = p->name_entry(); + size += entry->NameBytesLength(); + } + return size; +} + +bool NameTable::Builder::SubReadyToSerialize() { + return !name_entry_map_.empty(); +} + +int32_t NameTable::Builder::SubSerialize(WritableFontData* new_data) { + int32_t string_table_start_offset = + NameTable::Offset::kNameRecordStart + + name_entry_map_.size() * NameTable::Offset::kNameRecordSize; + + // Header + new_data->WriteUShort(NameTable::Offset::kFormat, 0); + new_data->WriteUShort(NameTable::Offset::kCount, name_entry_map_.size()); + new_data->WriteUShort(NameTable::Offset::kStringOffset, + string_table_start_offset); + int32_t name_record_offset = NameTable::Offset::kNameRecordStart; + int32_t string_offset = 0; + // Note: we offered operator< in NameEntryId, which will be used by std::less, + // and therefore our map will act like TreeMap in Java to provide + // sorted key set. + for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(), + end = name_entry_map_.end(); + b != end; ++b) { + new_data->WriteUShort( + name_record_offset + NameTable::Offset::kNameRecordPlatformId, + b->first.platform_id()); + new_data->WriteUShort( + name_record_offset + NameTable::Offset::kNameRecordEncodingId, + b->first.encoding_id()); + new_data->WriteUShort( + name_record_offset + NameTable::Offset::kNameRecordLanguageId, + b->first.language_id()); + new_data->WriteUShort( + name_record_offset + NameTable::Offset::kNameRecordNameId, + b->first.name_id()); + NameEntry* builder_entry = b->second->name_entry(); + new_data->WriteUShort( + name_record_offset + NameTable::Offset::kNameRecordStringLength, + builder_entry->NameBytesLength()); + new_data->WriteUShort( + name_record_offset + NameTable::Offset::kNameRecordStringOffset, + string_offset); + name_record_offset += NameTable::Offset::kNameRecordSize; + string_offset += new_data->WriteBytes( + string_offset + string_table_start_offset, + builder_entry->NameAsBytes()); + } + + return string_offset + string_table_start_offset; +} + +void NameTable::Builder::Initialize(ReadableFontData* data) { + if (data) { + NameTablePtr table = new NameTable(header(), data); + Ptr name_iter; + name_iter.Attach(table->Iterator()); + while (name_iter->HasNext()) { + NameEntryPtr name_entry; + name_entry.Attach(name_iter->Next()); + NameEntryBuilderPtr name_entry_builder = new NameEntryBuilder(name_entry); + NameEntry* builder_entry = name_entry_builder->name_entry(); + NameEntryId probe = builder_entry->name_entry_id(); + name_entry_map_[probe] = name_entry_builder; + } + } +} + +NameTable::NameEntryBuilderMap* NameTable::Builder::GetNameBuilders() { + if (name_entry_map_.empty()) { + Initialize(InternalReadData()); + } + set_model_changed(); + return &name_entry_map_; +} + +/****************************************************************************** + * NameTable class + ******************************************************************************/ +NameTable::~NameTable() {} + +int32_t NameTable::Format() { + return data_->ReadUShort(Offset::kFormat); +} + +int32_t NameTable::NameCount() { + return data_->ReadUShort(Offset::kCount); +} + +int32_t NameTable::PlatformId(int32_t index) { + return data_->ReadUShort(Offset::kNameRecordPlatformId + + OffsetForNameRecord(index)); +} + +int32_t NameTable::EncodingId(int32_t index) { + return data_->ReadUShort(Offset::kNameRecordEncodingId + + OffsetForNameRecord(index)); +} + +int32_t NameTable::LanguageId(int32_t index) { + return data_->ReadUShort(Offset::kNameRecordLanguageId + + OffsetForNameRecord(index)); +} + +int32_t NameTable::NameId(int32_t index) { + return data_->ReadUShort(Offset::kNameRecordNameId + + OffsetForNameRecord(index)); +} + +void NameTable::NameAsBytes(int32_t index, ByteVector* b) { + assert(b); + int32_t length = NameLength(index); + b->clear(); + b->resize(length); + data_->ReadBytes(NameOffset(index), &((*b)[0]), 0, length); +} + +void NameTable::NameAsBytes(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id, + ByteVector* b) { + assert(b); + NameEntryPtr entry; + entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id)); + if (entry) { + ByteVector* name = entry->NameAsBytes(); + std::copy(name->begin(), name->end(), b->begin()); + } +} + +UChar* NameTable::Name(int32_t index) { + ByteVector b; + NameAsBytes(index, &b); + return ConvertFromNameBytes(&b, PlatformId(index), EncodingId(index)); +} + +UChar* NameTable::Name(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) { + NameEntryPtr entry; + entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id)); + if (entry) { + return entry->Name(); + } + return NULL; +} + +CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t index) { + ByteVector b; + NameAsBytes(index, &b); + NameEntryPtr instance = new NameEntry(PlatformId(index), + EncodingId(index), + LanguageId(index), + NameId(index), b); + return instance.Detach(); +} + +CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) { + NameTable::NameEntryFilterInPlace + filter(platform_id, encoding_id, language_id, name_id); + Ptr name_entry_iter; + name_entry_iter.Attach(Iterator(&filter)); + NameEntryPtr result; + if (name_entry_iter->HasNext()) { + result = name_entry_iter->Next(); + } + return result; +} + +CALLER_ATTACH NameTable::NameEntryIterator* NameTable::Iterator() { + Ptr output = new NameTable::NameEntryIterator(this); + return output.Detach(); +} + +CALLER_ATTACH +NameTable::NameEntryIterator* NameTable::Iterator(NameEntryFilter* filter) { + Ptr output = + new NameTable::NameEntryIterator(this, filter); + return output.Detach(); +} + +NameTable::NameTable(Header* header, ReadableFontData* data) + : SubTableContainerTable(header, data) {} + +int32_t NameTable::StringOffset() { + return data_->ReadUShort(Offset::kStringOffset); +} + +int32_t NameTable::OffsetForNameRecord(int32_t index) { + return Offset::kNameRecordStart + index * Offset::kNameRecordSize; +} + +int32_t NameTable::NameLength(int32_t index) { + return data_->ReadUShort(Offset::kNameRecordStringLength + + OffsetForNameRecord(index)); +} + +int32_t NameTable::NameOffset(int32_t index) { + return data_->ReadUShort(Offset::kNameRecordStringOffset + + OffsetForNameRecord(index)) + StringOffset(); +} + +const char* NameTable::GetEncodingName(int32_t platform_id, + int32_t encoding_id) { + switch (platform_id) { + case PlatformId::kUnicode: + return "UTF-16BE"; + case PlatformId::kMacintosh: + switch (encoding_id) { + case MacintoshEncodingId::kRoman: + return "MacRoman"; + case MacintoshEncodingId::kJapanese: + return "Shift-JIS"; + case MacintoshEncodingId::kChineseTraditional: + return "Big5"; + case MacintoshEncodingId::kKorean: + return "EUC-KR"; + case MacintoshEncodingId::kArabic: + return "MacArabic"; + case MacintoshEncodingId::kHebrew: + return "MacHebrew"; + case MacintoshEncodingId::kGreek: + return "MacGreek"; + case MacintoshEncodingId::kRussian: + return "MacCyrillic"; + case MacintoshEncodingId::kRSymbol: + return "MacSymbol"; + case MacintoshEncodingId::kThai: + return "MacThai"; + case MacintoshEncodingId::kChineseSimplified: + return "EUC-CN"; + default: // Note: unknown/unconfirmed cases are not ported. + break; + } + break; + case PlatformId::kISO: + break; + case PlatformId::kWindows: + switch (encoding_id) { + case WindowsEncodingId::kSymbol: + case WindowsEncodingId::kUnicodeUCS2: + return "UTF-16BE"; + case WindowsEncodingId::kShiftJIS: + return "windows-933"; + case WindowsEncodingId::kPRC: + return "windows-936"; + case WindowsEncodingId::kBig5: + return "windows-950"; + case WindowsEncodingId::kWansung: + return "windows-949"; + case WindowsEncodingId::kJohab: + return "ms1361"; + case WindowsEncodingId::kUnicodeUCS4: + return "UCS-4"; + } + break; + case PlatformId::kCustom: + break; + default: + break; + } + return NULL; +} + +UConverter* NameTable::GetCharset(int32_t platform_id, int32_t encoding_id) { + UErrorCode error_code = U_ZERO_ERROR; + UConverter* conv = ucnv_open(GetEncodingName(platform_id, encoding_id), + &error_code); + if (U_SUCCESS(error_code)) { + return conv; + } + + if (conv) { + ucnv_close(conv); + } + return NULL; +} + +void NameTable::ConvertToNameBytes(const UChar* name, + int32_t platform_id, + int32_t encoding_id, + ByteVector* b) { + assert(b); + assert(name); + b->clear(); + UConverter* cs = GetCharset(platform_id, encoding_id); + if (cs == NULL) { + return; + } + + // Preflight to get buffer size. + UErrorCode error_code = U_ZERO_ERROR; + int32_t length = ucnv_fromUChars(cs, NULL, 0, name, -1, &error_code); + b->resize(length + 4); // The longest termination "\0" is 4 bytes. + memset(&((*b)[0]), 0, length + 4); + error_code = U_ZERO_ERROR; + ucnv_fromUChars(cs, + reinterpret_cast(&((*b)[0])), + length + 4, + name, + -1, + &error_code); + if (!U_SUCCESS(error_code)) { + b->clear(); + } + ucnv_close(cs); +} + +UChar* NameTable::ConvertFromNameBytes(ByteVector* name_bytes, + int32_t platform_id, + int32_t encoding_id) { + if (name_bytes == NULL) { + return NULL; + } + UConverter* cs = GetCharset(platform_id, encoding_id); + UErrorCode error_code = U_ZERO_ERROR; + if (cs == NULL) { + char buffer[11] = {0}; +#if defined (WIN32) + _itoa_s(platform_id, buffer, 16); +#else + snprintf(buffer, sizeof(buffer), "%x", platform_id); +#endif + UChar* result = new UChar[12]; + memset(result, 0, sizeof(UChar) * 12); + cs = ucnv_open("utf-8", &error_code); + if (U_SUCCESS(error_code)) { + ucnv_toUChars(cs, result, 12, buffer, 11, &error_code); + ucnv_close(cs); + if (U_SUCCESS(error_code)) { + return result; + } + } + delete[] result; + return NULL; + } + + // No preflight needed here, we will be bigger. + UChar* output_buffer = new UChar[name_bytes->size() + 1]; + memset(output_buffer, 0, sizeof(UChar) * (name_bytes->size() + 1)); + int32_t length = ucnv_toUChars(cs, + output_buffer, + name_bytes->size(), + reinterpret_cast(&((*name_bytes)[0])), + name_bytes->size(), + &error_code); + ucnv_close(cs); + if (length > 0) { + return output_buffer; + } + + delete[] output_buffer; + return NULL; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/name_table.h b/src/sfntly/src/sfntly/table/core/name_table.h new file mode 100644 index 0000000000..01d3b29076 --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/name_table.h @@ -0,0 +1,744 @@ +/* + * 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_CORE_NAME_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_NAME_TABLE_H_ + +// Must include this before ICU to avoid stdint redefinition issue. +#include "sfntly/port/type.h" + +#include +#include + +#include +#include + +#include "sfntly/port/java_iterator.h" +#include "sfntly/table/subtable_container_table.h" + +namespace sfntly { + +// The following code implements the name table defined in TTF/OTF spec, which +// can be found at http://www.microsoft.com/typography/otspec/name.htm. + +// Name IDs defined in TTF/OTF spec. +struct NameId { + enum { + kUnknown = -1, + kCopyrightNotice = 0, + kFontFamilyName = 1, + kFontSubfamilyName = 2, + kUniqueFontIdentifier = 3, + kFullFontName = 4, + kVersionString = 5, + kPostscriptName = 6, + kTrademark = 7, + kManufacturerName = 8, + kDesigner = 9, + kDescription = 10, + kVendorURL = 11, + kDesignerURL = 12, + kLicenseDescription = 13, + kLicenseInfoURL = 14, + kReserved15 = 15, + kPreferredFamily = 16, + kPreferredSubfamily = 17, + kCompatibleFullName = 18, + kSampleText = 19, + kPostscriptCID = 20, + kWWSFamilyName = 21, + kWWSSubfamilyName = 22 + }; +}; + +// Unicode language IDs used in Name Records. +struct UnicodeLanguageId { + enum { + kUnknown = -1, + kAll = 0 + }; +}; + +// Macintosh Language IDs (platform ID = 1) +struct MacintoshLanguageId { + enum { + kUnknown = -1, + kEnglish = 0, + kFrench = 1, + kGerman = 2, + kItalian = 3, + kDutch = 4, + kSwedish = 5, + kSpanish = 6, + kDanish = 7, + kPortuguese = 8, + kNorwegian = 9, + kHebrew = 10, + kJapanese = 11, + kArabic = 12, + kFinnish = 13, + kGreek = 14, + kIcelandic = 15, + kMaltese = 16, + kTurkish = 17, + kCroatian = 18, + kChinese_Traditional = 19, + kUrdu = 20, + kHindi = 21, + kThai = 22, + kKorean = 23, + kLithuanian = 24, + kPolish = 25, + kHungarian = 26, + kEstonian = 27, + kLatvian = 28, + kSami = 29, + kFaroese = 30, + kFarsiPersian = 31, + kRussian = 32, + kChinese_Simplified = 33, + kFlemish = 34, + kIrishGaelic = 35, + kAlbanian = 36, + kRomanian = 37, + kCzech = 38, + kSlovak = 39, + kSlovenian = 40, + kYiddish = 41, + kSerbian = 42, + kMacedonian = 43, + kBulgarian = 44, + kUkrainian = 45, + kByelorussian = 46, + kUzbek = 47, + kKazakh = 48, + kAzerbaijani_Cyrillic = 49, + kAzerbaijani_Arabic = 50, + kArmenian = 51, + kGeorgian = 52, + kMoldavian = 53, + kKirghiz = 54, + kTajiki = 55, + kTurkmen = 56, + kMongolian_Mongolian = 57, + kMongolian_Cyrillic = 58, + kPashto = 59, + kKurdish = 60, + kKashmiri = 61, + kSindhi = 62, + kTibetan = 63, + kNepali = 64, + kSanskrit = 65, + kMarathi = 66, + kBengali = 67, + kAssamese = 68, + kGujarati = 69, + kPunjabi = 70, + kOriya = 71, + kMalayalam = 72, + kKannada = 73, + kTamil = 74, + kTelugu = 75, + kSinhalese = 76, + kBurmese = 77, + kKhmer = 78, + kLao = 79, + kVietnamese = 80, + kIndonesian = 81, + kTagalong = 82, + kMalay_Roman = 83, + kMalay_Arabic = 84, + kAmharic = 85, + kTigrinya = 86, + kGalla = 87, + kSomali = 88, + kSwahili = 89, + kKinyarwandaRuanda = 90, + kRundi = 91, + kNyanjaChewa = 92, + kMalagasy = 93, + kEsperanto = 94, + kWelsh = 128, + kBasque = 129, + kCatalan = 130, + kLatin = 131, + kQuenchua = 132, + kGuarani = 133, + kAymara = 134, + kTatar = 135, + kUighur = 136, + kDzongkha = 137, + kJavanese_Roman = 138, + kSundanese_Roman = 139, + kGalician = 140, + kAfrikaans = 141, + kBreton = 142, + kInuktitut = 143, + kScottishGaelic = 144, + kManxGaelic = 145, + kIrishGaelic_WithDotAbove = 146, + kTongan = 147, + kGreek_Polytonic = 148, + kGreenlandic = 149, + kAzerbaijani_Roman = 150 + }; +}; + +// Windows Language IDs (platformID = 3) +struct WindowsLanguageId { + enum { + kUnknown = -1, + kAfrikaans_SouthAfrica = 0x0436, + kAlbanian_Albania = 0x041C, + kAlsatian_France = 0x0484, + kAmharic_Ethiopia = 0x045E, + kArabic_Algeria = 0x1401, + kArabic_Bahrain = 0x3C01, + kArabic_Egypt = 0x0C01, + kArabic_Iraq = 0x0801, + kArabic_Jordan = 0x2C01, + kArabic_Kuwait = 0x3401, + kArabic_Lebanon = 0x3001, + kArabic_Libya = 0x1001, + kArabic_Morocco = 0x1801, + kArabic_Oman = 0x2001, + kArabic_Qatar = 0x4001, + kArabic_SaudiArabia = 0x0401, + kArabic_Syria = 0x2801, + kArabic_Tunisia = 0x1C01, + kArabic_UAE = 0x3801, + kArabic_Yemen = 0x2401, + kArmenian_Armenia = 0x042B, + kAssamese_India = 0x044D, + kAzeri_Cyrillic_Azerbaijan = 0x082C, + kAzeri_Latin_Azerbaijan = 0x042C, + kBashkir_Russia = 0x046D, + kBasque_Basque = 0x042D, + kBelarusian_Belarus = 0x0423, + kBengali_Bangladesh = 0x0845, + kBengali_India = 0x0445, + kBosnian_Cyrillic_BosniaAndHerzegovina = 0x201A, + kBosnian_Latin_BosniaAndHerzegovina = 0x141A, + kBreton_France = 0x047E, + kBulgarian_Bulgaria = 0x0402, + kCatalan_Catalan = 0x0403, + kChinese_HongKongSAR = 0x0C04, + kChinese_MacaoSAR = 0x1404, + kChinese_PeoplesRepublicOfChina = 0x0804, + kChinese_Singapore = 0x1004, + kChinese_Taiwan = 0x0404, + kCorsican_France = 0x0483, + kCroatian_Croatia = 0x041A, + kCroatian_Latin_BosniaAndHerzegovina = 0x101A, + kCzech_CzechRepublic = 0x0405, + kDanish_Denmark = 0x0406, + kDari_Afghanistan = 0x048C, + kDivehi_Maldives = 0x0465, + kDutch_Belgium = 0x0813, + kDutch_Netherlands = 0x0413, + kEnglish_Australia = 0x0C09, + kEnglish_Belize = 0x2809, + kEnglish_Canada = 0x1009, + kEnglish_Caribbean = 0x2409, + kEnglish_India = 0x4009, + kEnglish_Ireland = 0x1809, + kEnglish_Jamaica = 0x2009, + kEnglish_Malaysia = 0x4409, + kEnglish_NewZealand = 0x1409, + kEnglish_RepublicOfThePhilippines = 0x3409, + kEnglish_Singapore = 0x4809, + kEnglish_SouthAfrica = 0x1C09, + kEnglish_TrinidadAndTobago = 0x2C09, + kEnglish_UnitedKingdom = 0x0809, + kEnglish_UnitedStates = 0x0409, + kEnglish_Zimbabwe = 0x3009, + kEstonian_Estonia = 0x0425, + kFaroese_FaroeIslands = 0x0438, + kFilipino_Philippines = 0x0464, + kFinnish_Finland = 0x040B, + kFrench_Belgium = 0x080C, + kFrench_Canada = 0x0C0C, + kFrench_France = 0x040C, + kFrench_Luxembourg = 0x140c, + kFrench_PrincipalityOfMonoco = 0x180C, + kFrench_Switzerland = 0x100C, + kFrisian_Netherlands = 0x0462, + kGalician_Galician = 0x0456, + kGeorgian_Georgia = 0x0437, + kGerman_Austria = 0x0C07, + kGerman_Germany = 0x0407, + kGerman_Liechtenstein = 0x1407, + kGerman_Luxembourg = 0x1007, + kGerman_Switzerland = 0x0807, + kGreek_Greece = 0x0408, + kGreenlandic_Greenland = 0x046F, + kGujarati_India = 0x0447, + kHausa_Latin_Nigeria = 0x0468, + kHebrew_Israel = 0x040D, + kHindi_India = 0x0439, + kHungarian_Hungary = 0x040E, + kIcelandic_Iceland = 0x040F, + kIgbo_Nigeria = 0x0470, + kIndonesian_Indonesia = 0x0421, + kInuktitut_Canada = 0x045D, + kInuktitut_Latin_Canada = 0x085D, + kIrish_Ireland = 0x083C, + kisiXhosa_SouthAfrica = 0x0434, + kisiZulu_SouthAfrica = 0x0435, + kItalian_Italy = 0x0410, + kItalian_Switzerland = 0x0810, + kJapanese_Japan = 0x0411, + kKannada_India = 0x044B, + kKazakh_Kazakhstan = 0x043F, + kKhmer_Cambodia = 0x0453, + kKiche_Guatemala = 0x0486, + kKinyarwanda_Rwanda = 0x0487, + kKiswahili_Kenya = 0x0441, + kKonkani_India = 0x0457, + kKorean_Korea = 0x0412, + kKyrgyz_Kyrgyzstan = 0x0440, + kLao_LaoPDR = 0x0454, + kLatvian_Latvia = 0x0426, + kLithuanian_Lithuania = 0x0427, + kLowerSorbian_Germany = 0x082E, + kLuxembourgish_Luxembourg = 0x046E, + kMacedonian_FYROM_FormerYugoslavRepublicOfMacedonia = 0x042F, + kMalay_BruneiDarussalam = 0x083E, + kMalay_Malaysia = 0x043E, + kMalayalam_India = 0x044C, + kMaltese_Malta = 0x043A, + kMaori_NewZealand = 0x0481, + kMapudungun_Chile = 0x047A, + kMarathi_India = 0x044E, + kMohawk_Mohawk = 0x047C, + kMongolian_Cyrillic_Mongolia = 0x0450, + kMongolian_Traditional_PeoplesRepublicOfChina = 0x0850, + kNepali_Nepal = 0x0461, + kNorwegian_Bokmal_Norway = 0x0414, + kNorwegian_Nynorsk_Norway = 0x0814, + kOccitan_France = 0x0482, + kOriya_India = 0x0448, + kPashto_Afghanistan = 0x0463, + kPolish_Poland = 0x0415, + kPortuguese_Brazil = 0x0416, + kPortuguese_Portugal = 0x0816, + kPunjabi_India = 0x0446, + kQuechua_Bolivia = 0x046B, + kQuechua_Ecuador = 0x086B, + kQuechua_Peru = 0x0C6B, + kRomanian_Romania = 0x0418, + kRomansh_Switzerland = 0x0417, + kRussian_Russia = 0x0419, + kSami_Inari_Finland = 0x243B, + kSami_Lule_Norway = 0x103B, + kSami_Lule_Sweden = 0x143B, + kSami_Northern_Finland = 0x0C3B, + kSami_Northern_Norway = 0x043B, + kSami_Northern_Sweden = 0x083B, + kSami_Skolt_Finland = 0x203B, + kSami_Southern_Norway = 0x183B, + kSami_Southern_Sweden = 0x1C3B, + kSanskrit_India = 0x044F, + kSerbian_Cyrillic_BosniaAndHerzegovina = 0x1C1A, + kSerbian_Cyrillic_Serbia = 0x0C1A, + kSerbian_Latin_BosniaAndHerzegovina = 0x181A, + kSerbian_Latin_Serbia = 0x081A, + kSesothoSaLeboa_SouthAfrica = 0x046C, + kSetswana_SouthAfrica = 0x0432, + kSinhala_SriLanka = 0x045B, + kSlovak_Slovakia = 0x041B, + kSlovenian_Slovenia = 0x0424, + kSpanish_Argentina = 0x2C0A, + kSpanish_Bolivia = 0x400A, + kSpanish_Chile = 0x340A, + kSpanish_Colombia = 0x240A, + kSpanish_CostaRica = 0x140A, + kSpanish_DominicanRepublic = 0x1C0A, + kSpanish_Ecuador = 0x300A, + kSpanish_ElSalvador = 0x440A, + kSpanish_Guatemala = 0x100A, + kSpanish_Honduras = 0x480A, + kSpanish_Mexico = 0x080A, + kSpanish_Nicaragua = 0x4C0A, + kSpanish_Panama = 0x180A, + kSpanish_Paraguay = 0x3C0A, + kSpanish_Peru = 0x280A, + kSpanish_PuertoRico = 0x500A, + kSpanish_ModernSort_Spain = 0x0C0A, + kSpanish_TraditionalSort_Spain = 0x040A, + kSpanish_UnitedStates = 0x540A, + kSpanish_Uruguay = 0x380A, + kSpanish_Venezuela = 0x200A, + kSweden_Finland = 0x081D, + kSwedish_Sweden = 0x041D, + kSyriac_Syria = 0x045A, + kTajik_Cyrillic_Tajikistan = 0x0428, + kTamazight_Latin_Algeria = 0x085F, + kTamil_India = 0x0449, + kTatar_Russia = 0x0444, + kTelugu_India = 0x044A, + kThai_Thailand = 0x041E, + kTibetan_PRC = 0x0451, + kTurkish_Turkey = 0x041F, + kTurkmen_Turkmenistan = 0x0442, + kUighur_PRC = 0x0480, + kUkrainian_Ukraine = 0x0422, + kUpperSorbian_Germany = 0x042E, + kUrdu_IslamicRepublicOfPakistan = 0x0420, + kUzbek_Cyrillic_Uzbekistan = 0x0843, + kUzbek_Latin_Uzbekistan = 0x0443, + kVietnamese_Vietnam = 0x042A, + kWelsh_UnitedKingdom = 0x0452, + kWolof_Senegal = 0x0448, + kYakut_Russia = 0x0485, + kYi_PRC = 0x0478, + kYoruba_Nigeria = 0x046A + }; +}; + +class NameTable : public SubTableContainerTable, public RefCounted { + public: + // Unique identifier for a given name record. + class NameEntryId { + public: + NameEntryId(); // C++ port only, must provide default constructor. + NameEntryId(int32_t platform_id, int32_t encoding_id, int32_t language_id, + int32_t name_id); + NameEntryId(const NameEntryId&); + // Make gcc -Wnon-virtual-dtor happy. + virtual ~NameEntryId() {} + + int32_t platform_id() const { return platform_id_; } + int32_t encoding_id() const { return encoding_id_; } + int32_t language_id() const { return language_id_; } + int32_t name_id() const { return name_id_; } + + const NameEntryId& operator=(const NameEntryId& rhs) const; + bool operator==(const NameEntryId& rhs) const; + bool operator<(const NameEntryId& rhs) const; + + // UNIMPLEMENTED: int hashCode() + // String toString() + + private: + mutable int32_t platform_id_; + mutable int32_t encoding_id_; + mutable int32_t language_id_; + mutable int32_t name_id_; + }; + + class NameEntryBuilder; + + // Class to represent a name entry in the name table. + class NameEntry : public RefCounted { + public: + NameEntry(); + NameEntry(const NameEntryId& name_entry_id, const ByteVector& name_bytes); + NameEntry(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id, + const ByteVector& name_bytes); + virtual ~NameEntry(); + + NameEntryId& name_entry_id() { return name_entry_id_; } + int32_t platform_id() const { return name_entry_id_.platform_id(); } + int32_t encoding_id() const { return name_entry_id_.encoding_id(); } + int32_t language_id() const { return name_entry_id_.language_id(); } + int32_t name_id() const { return name_entry_id_.name_id(); } + + // Get the bytes for name. Returned pointer is the address of private + // member of this class, do not attempt to delete. + ByteVector* NameAsBytes(); + + // C++ port only: get the length of NameAsBytes. + int32_t NameBytesLength(); + + // Returns the name in Unicode as UChar array. + // Note: ICU UChar* convention requires caller to delete[] it. + UChar* Name(); + bool operator==(const NameEntry& rhs) const; + + // UNIMPLEMENTED: String toString() + // int hashCode() + + private: + void Init(int32_t platform_id, int32_t encoding_id, int32_t language_id, + int32_t name_id, const ByteVector* name_bytes); + + NameEntryId name_entry_id_; + int32_t length_; + ByteVector name_bytes_; + + friend class NameEntryBuilder; + }; + + // Builder of a name entry. + // C++ port: original Java hierarchy inherits from NameEntry. In C++ port, we + // opted not doing so to avoid ref count issues and nasty protected members. + class NameEntryBuilder : public RefCounted { + public: + NameEntryBuilder(); + NameEntryBuilder(const NameEntryId& name_entry_id, + const ByteVector& name_bytes); + explicit NameEntryBuilder(const NameEntryId& name_entry_id); + explicit NameEntryBuilder(NameEntry* entry); + virtual ~NameEntryBuilder(); + + virtual void SetName(const UChar* name); + virtual void SetName(const ByteVector& name_bytes); + virtual void SetName(const ByteVector& name_bytes, + int32_t offset, + int32_t length); + + // C++ port only. CALLER_ATTACH is not added because the lifetime shall be + // controlled by this class, therefore the caller shall not increase the ref + // count. + NameEntry* name_entry() { return name_entry_; } + + private: + void Init(int32_t platform_id, int32_t encoding_id, int32_t language_id, + int32_t name_id, const ByteVector* name_bytes); + + Ptr name_entry_; + }; + typedef std::map > NameEntryBuilderMap; + + // An interface for a filter to use with the name entry iterator. This allows + // name entries to be iterated and only those acceptable to the filter will be + // returned. + class NameEntryFilter { + public: + virtual bool Accept(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id) = 0; + // Make gcc -Wnon-virtual-dtor happy. + virtual ~NameEntryFilter() {} + }; + + // C++ port only: an in-place filter to mimic Java Iterator's filtering. + class NameEntryFilterInPlace : public NameEntryFilter { + public: + NameEntryFilterInPlace(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id); + // Make gcc -Wnon-virtual-dtor happy. + virtual ~NameEntryFilterInPlace() {} + + virtual bool Accept(int32_t platform_id, + int32_t encoding_id, + int32_t language_id, + int32_t name_id); + + private: + int32_t platform_id_; + int32_t encoding_id_; + int32_t language_id_; + int32_t name_id_; + }; + + class NameEntryIterator : public RefIterator { + public: + // If filter is NULL, filter through all tables. + explicit NameEntryIterator(NameTable* table); + NameEntryIterator(NameTable* table, NameEntryFilter* filter); + virtual ~NameEntryIterator() {} + + virtual bool HasNext(); + virtual CALLER_ATTACH NameEntry* Next(); + + private: + int32_t name_index_; + NameEntryFilter* filter_; + }; + + // The builder to construct name table for outputting. + class Builder : public SubTableContainerTable::Builder, + public RefCounted { + public: + // Constructor scope altered to public because C++ does not allow base + // class to instantiate derived class with protected constructors. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + // Revert the name builders for the name table to the last version that came + // from data. + void RevertNames(); + + // Number of name entry builders contained. + int32_t BuilderCount(); + + // Note: For C++ port, clear() is not implemented. The clear() function + // implies completely remove name entry builders, which is easy in + // Java but will take a lot of efforts in C++ to release the builders + // nicely and correctly. + // TODO(arthurhsu): IMPLEMENT + // Clear the name builders for the name table. + // void clear(); + + // Check the existance of a name entry builder by key. + bool Has(int32_t platform_id, int32_t encoding_id, int32_t language_id, + int32_t name_id); + + // Get name entry builder by key. + CALLER_ATTACH NameEntryBuilder* NameBuilder(int32_t platform_id, + int32_t encoding_id, int32_t language_id, int32_t name_id); + + // Remove name entry builder by key. + bool Remove(int32_t platform_id, int32_t encoding_id, int32_t language_id, + int32_t name_id); + + // FontDataTable::Builder API implementation + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + private: + void Initialize(ReadableFontData* data); + NameEntryBuilderMap* GetNameBuilders(); + + // Note: callers should use the getter funtion provided above to ensure that + // this is lazily initialized instead of accessing directly. + NameEntryBuilderMap name_entry_map_; + }; + + /**************************************************************************** + * public methods of NameTable class + ****************************************************************************/ + virtual ~NameTable(); + + // Get the format used in the name table. + virtual int32_t Format(); + + // Get the number of names in the name table. + virtual int32_t NameCount(); + + // Get the platform id for the given name record. + virtual int32_t PlatformId(int32_t index); + + // Get the encoding id for the given name record. + // see MacintoshEncodingId, WindowsEncodingId, UnicodeEncodingId + virtual int32_t EncodingId(int32_t index); + + // Get the language id for the given name record. + virtual int32_t LanguageId(int32_t index); + + // Get the name id for given name record. + virtual int32_t NameId(int32_t index); + + // Get the name as bytes for the specified name. If there is no entry for the + // requested name, then empty vector is returned. + virtual void NameAsBytes(int32_t index, ByteVector* b); + virtual void NameAsBytes(int32_t platform_id, int32_t encoding_id, + int32_t language_id, int32_t name_id, + ByteVector* b); + + // Get the name as a UChar* for the given name record. If there is no + // encoding conversion available for the name record then a best attempt + // UChar* will be returned. + // Note: ICU UChar* convention requires caller to delete[] it. + virtual UChar* Name(int32_t index); + + // Get the name as a UChar* for the specified name. If there is no entry for + // the requested name then NULL is returned. If there is no encoding + // conversion available for the name then a best attempt UChar* will be + // returned. + // Note: ICU UChar* convention requires caller to delete[] it. + virtual UChar* Name(int32_t platform_id, int32_t encoding_id, + int32_t language_id, int32_t name_id); + + // Note: These functions are renamed in C++ port. Their original Java name is + // nameEntry(). + virtual CALLER_ATTACH NameEntry* GetNameEntry(int32_t index); + virtual CALLER_ATTACH NameEntry* GetNameEntry(int32_t platform_id, + int32_t encoding_id, int32_t language_id, int32_t name_id); + + // Note: Not implemented in C++ port due to complexity and low usage. + // virtual void names(std::set*); + + // Get the iterator to iterate through all name entries. + virtual CALLER_ATTACH NameEntryIterator* Iterator(); + virtual CALLER_ATTACH NameEntryIterator* Iterator(NameEntryFilter* filter); + + private: + struct Offset { + enum { + kFormat = 0, + kCount = 2, + kStringOffset = 4, + kNameRecordStart = 6, + + // Format 1 - offset from the end of the name records + kLangTagCount = 0, + kLangTagRecord = 2, + + kNameRecordSize = 12, + // Name Records + kNameRecordPlatformId = 0, + kNameRecordEncodingId = 2, + kNameRecordLanguageId = 4, + kNameRecordNameId = 6, + kNameRecordStringLength = 8, + kNameRecordStringOffset = 10 + }; + }; + + // The table shall be constructed using Builder, no direct instantiation. + NameTable(Header* header, ReadableFontData* data); + + // Get the offset to the string data in the name table. + int32_t StringOffset(); + + // Get the offset for the given name record. + int32_t OffsetForNameRecord(int32_t index); + + // Get the length of the string data for the given name record. + int32_t NameLength(int32_t index); + + // Get the offset of the string data for the given name record. + int32_t NameOffset(int32_t index); + + // Note: string literals are returned. Caller shall not attempt to manipulate + // the returned pointer. + static const char* GetEncodingName(int32_t platform_id, int32_t encoding_id); + + // Note: ICU UConverter* convention requires caller to ucnv_close() it. + static UConverter* GetCharset(int32_t platform_id, int32_t encoding_id); + + // Note: Output will be stored in ByteVector* b. Original data in b will be + // erased and replaced with converted name bytes. + static void ConvertToNameBytes(const UChar* name, int32_t platform_id, + int32_t encoding_id, ByteVector* b); + + // Note: ICU UChar* convention requires caller to delete[] it. + static UChar* ConvertFromNameBytes(ByteVector* name_bytes, + int32_t platform_id, int32_t encoding_id); +}; // class NameTable +typedef Ptr NameTablePtr; +typedef Ptr NameEntryPtr; +typedef Ptr NameTableBuilderPtr; +typedef Ptr NameEntryBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_NAME_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/core/os2_table.cc b/src/sfntly/src/sfntly/table/core/os2_table.cc new file mode 100644 index 0000000000..7ca9d9a4fd --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/os2_table.cc @@ -0,0 +1,608 @@ +/* + * 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/core/os2_table.h" + +namespace sfntly { +/****************************************************************************** + * Constants + ******************************************************************************/ +const int64_t CodePageRange::kLatin1_1252 = (int64_t)1 << 0; +const int64_t CodePageRange::kLatin2_1250 = (int64_t)1 << (int64_t)1; +const int64_t CodePageRange::kCyrillic_1251 = (int64_t)1 << 2; +const int64_t CodePageRange::kGreek_1253 = (int64_t)1 << 3; +const int64_t CodePageRange::kTurkish_1254 = (int64_t)1 << 4; +const int64_t CodePageRange::kHebrew_1255 = (int64_t)1 << 5; +const int64_t CodePageRange::kArabic_1256 = (int64_t)1 << 6; +const int64_t CodePageRange::kWindowsBaltic_1257 = (int64_t)1 << 7; +const int64_t CodePageRange::kVietnamese_1258 = (int64_t)1 << 8; +const int64_t CodePageRange::kAlternateANSI9 = (int64_t)1 << 9; +const int64_t CodePageRange::kAlternateANSI10 = (int64_t)1 << 10; +const int64_t CodePageRange::kAlternateANSI11 = (int64_t)1 << 11; +const int64_t CodePageRange::kAlternateANSI12 = (int64_t)1 << 12; +const int64_t CodePageRange::kAlternateANSI13 = (int64_t)1 << 13; +const int64_t CodePageRange::kAlternateANSI14 = (int64_t)1 << 14; +const int64_t CodePageRange::kAlternateANSI15 = (int64_t)1 << 15; +const int64_t CodePageRange::kThai_874 = (int64_t)1 << 16; +const int64_t CodePageRange::kJapanJIS_932 = (int64_t)1 << 17; +const int64_t CodePageRange::kChineseSimplified_936 = (int64_t)1 << 18; +const int64_t CodePageRange::kKoreanWansung_949 = (int64_t)1 << 19; +const int64_t CodePageRange::kChineseTraditional_950 = (int64_t)1 << 20; +const int64_t CodePageRange::kKoreanJohab_1361 = (int64_t)1 << 21; +const int64_t CodePageRange::kAlternateANSI22 = (int64_t)1 << 22; +const int64_t CodePageRange::kAlternateANSI23 = (int64_t)1 << 23; +const int64_t CodePageRange::kAlternateANSI24 = (int64_t)1 << 24; +const int64_t CodePageRange::kAlternateANSI25 = (int64_t)1 << 25; +const int64_t CodePageRange::kAlternateANSI26 = (int64_t)1 << 26; +const int64_t CodePageRange::kAlternateANSI27 = (int64_t)1 << 27; +const int64_t CodePageRange::kAlternateANSI28 = (int64_t)1 << 28; +const int64_t CodePageRange::kMacintoshCharacterSet = (int64_t)1 << 29; +const int64_t CodePageRange::kOEMCharacterSet = (int64_t)1 << 30; +const int64_t CodePageRange::kSymbolCharacterSet = (int64_t)1 << 31; +const int64_t CodePageRange::kReservedForOEM32 = (int64_t)1 << 32; +const int64_t CodePageRange::kReservedForOEM33 = (int64_t)1 << 33; +const int64_t CodePageRange::kReservedForOEM34 = (int64_t)1 << 34; +const int64_t CodePageRange::kReservedForOEM35 = (int64_t)1 << 35; +const int64_t CodePageRange::kReservedForOEM36 = (int64_t)1 << 36; +const int64_t CodePageRange::kReservedForOEM37 = (int64_t)1 << 37; +const int64_t CodePageRange::kReservedForOEM38 = (int64_t)1 << 38; +const int64_t CodePageRange::kReservedForOEM39 = (int64_t)1 << 39; +const int64_t CodePageRange::kReservedForOEM40 = (int64_t)1 << 40; +const int64_t CodePageRange::kReservedForOEM41 = (int64_t)1 << 41; +const int64_t CodePageRange::kReservedForOEM42 = (int64_t)1 << 42; +const int64_t CodePageRange::kReservedForOEM43 = (int64_t)1 << 43; +const int64_t CodePageRange::kReservedForOEM44 = (int64_t)1 << 44; +const int64_t CodePageRange::kReservedForOEM45 = (int64_t)1 << 45; +const int64_t CodePageRange::kReservedForOEM46 = (int64_t)1 << 46; +const int64_t CodePageRange::kReservedForOEM47 = (int64_t)1 << 47; +const int64_t CodePageRange::kIBMGreek_869 = (int64_t)1 << 48; +const int64_t CodePageRange::kMSDOSRussion_866 = (int64_t)1 << 49; +const int64_t CodePageRange::kMSDOSNordic_865 = (int64_t)1 << 50; +const int64_t CodePageRange::kArabic_864 = (int64_t)1 << 51; +const int64_t CodePageRange::kMSDOSCanadianFrench_863 = (int64_t)1 << 52; +const int64_t CodePageRange::kHebrew_862 = (int64_t)1 << 53; +const int64_t CodePageRange::kMSDOSIcelandic_861 = (int64_t)1 << 54; +const int64_t CodePageRange::kMSDOSPortugese_860 = (int64_t)1 << 55; +const int64_t CodePageRange::kIBMTurkish_857 = (int64_t)1 << 56; +const int64_t CodePageRange::kIBMCyrillic_855 = (int64_t)1 << 57; +const int64_t CodePageRange::kLatin2_852 = (int64_t)1 << 58; +const int64_t CodePageRange::kMSDOSBaltic_775 = (int64_t)1 << 59; +const int64_t CodePageRange::kGreek_737 = (int64_t)1 << 60; +const int64_t CodePageRange::kArabic_708 = (int64_t)1 << 61; +const int64_t CodePageRange::kLatin1_850 = (int64_t)1 << 62; +const int64_t CodePageRange::kUS_437 = (int64_t)1 << 63; + +/****************************************************************************** + * struct UnicodeRange + ******************************************************************************/ +int32_t UnicodeRange::range(int32_t bit) { + if (bit < 0 || bit > kLast) { + return -1; + } + return bit; +} + +/****************************************************************************** + * class OS2Table + ******************************************************************************/ +OS2Table::~OS2Table() {} + +int32_t OS2Table::TableVersion() { + return data_->ReadUShort(Offset::kVersion); +} + +int32_t OS2Table::XAvgCharWidth() { + return data_->ReadShort(Offset::kXAvgCharWidth); +} + +int32_t OS2Table::UsWeightClass() { + return data_->ReadUShort(Offset::kUsWeightClass); +} + +int32_t OS2Table::UsWidthClass() { + return data_->ReadUShort(Offset::kUsWidthClass); +} + +int32_t OS2Table::FsType() { + return data_->ReadUShort(Offset::kFsType); +} + +int32_t OS2Table::YSubscriptXSize() { + return data_->ReadShort(Offset::kYSubscriptXSize); +} + +int32_t OS2Table::YSubscriptYSize() { + return data_->ReadShort(Offset::kYSubscriptYSize); +} + +int32_t OS2Table::YSubscriptXOffset() { + return data_->ReadShort(Offset::kYSubscriptXOffset); +} + +int32_t OS2Table::YSubscriptYOffset() { + return data_->ReadShort(Offset::kYSubscriptYOffset); +} + +int32_t OS2Table::YSuperscriptXSize() { + return data_->ReadShort(Offset::kYSuperscriptXSize); +} + +int32_t OS2Table::YSuperscriptYSize() { + return data_->ReadShort(Offset::kYSuperscriptYSize); +} + +int32_t OS2Table::YSuperscriptXOffset() { + return data_->ReadShort(Offset::kYSuperscriptXOffset); +} + +int32_t OS2Table::YSuperscriptYOffset() { + return data_->ReadShort(Offset::kYSuperscriptYOffset); +} + +int32_t OS2Table::YStrikeoutSize() { + return data_->ReadShort(Offset::kYStrikeoutSize); +} + +int32_t OS2Table::YStrikeoutPosition() { + return data_->ReadShort(Offset::kYStrikeoutPosition); +} + +int32_t OS2Table::SFamilyClass() { + return data_->ReadShort(Offset::kSFamilyClass); +} + +void OS2Table::Panose(ByteVector* value) { + assert(value); + value->clear(); + value->resize(10); + data_->ReadBytes(Offset::kPanose, &((*value)[0]), 0, 10); +} + +int64_t OS2Table::UlUnicodeRange1() { + return data_->ReadULong(Offset::kUlUnicodeRange1); +} + +int64_t OS2Table::UlUnicodeRange2() { + return data_->ReadULong(Offset::kUlUnicodeRange2); +} + +int64_t OS2Table::UlUnicodeRange3() { + return data_->ReadULong(Offset::kUlUnicodeRange3); +} + +int64_t OS2Table::UlUnicodeRange4() { + return data_->ReadULong(Offset::kUlUnicodeRange4); +} + +void OS2Table::AchVendId(ByteVector* b) { + assert(b); + b->clear(); + b->resize(4); + data_->ReadBytes(Offset::kAchVendId, &((*b)[0]), 0, 4); +} + +int32_t OS2Table::FsSelection() { + return data_->ReadUShort(Offset::kFsSelection); +} + +int32_t OS2Table::UsFirstCharIndex() { + return data_->ReadUShort(Offset::kUsFirstCharIndex); +} + +int32_t OS2Table::UsLastCharIndex() { + return data_->ReadUShort(Offset::kUsLastCharIndex); +} + +int32_t OS2Table::STypoAscender() { + return data_->ReadShort(Offset::kSTypoAscender); +} + +int32_t OS2Table::STypoDescender() { + return data_->ReadShort(Offset::kSTypoDescender); +} + +int32_t OS2Table::STypoLineGap() { + return data_->ReadShort(Offset::kSTypoLineGap); +} + +int32_t OS2Table::UsWinAscent() { + return data_->ReadUShort(Offset::kUsWinAscent); +} + +int32_t OS2Table::UsWinDescent() { + return data_->ReadUShort(Offset::kUsWinDescent); +} + +int64_t OS2Table::UlCodePageRange1() { + return data_->ReadULong(Offset::kUlCodePageRange1); +} + +int64_t OS2Table::UlCodePageRange2() { + return data_->ReadULong(Offset::kUlCodePageRange2); +} + +int32_t OS2Table::SxHeight() { + return data_->ReadShort(Offset::kSxHeight); +} + +int32_t OS2Table::SCapHeight() { + return data_->ReadShort(Offset::kSCapHeight); +} + +int32_t OS2Table::UsDefaultChar() { + return data_->ReadUShort(Offset::kUsDefaultChar); +} + +int32_t OS2Table::UsBreakChar() { + return data_->ReadUShort(Offset::kUsBreakChar); +} + +int32_t OS2Table::UsMaxContext() { + return data_->ReadUShort(Offset::kUsMaxContext); +} + +OS2Table::OS2Table(Header* header, ReadableFontData* data) + : Table(header, data) { +} + +/****************************************************************************** + * class OS2Table::Builder + ******************************************************************************/ +OS2Table::Builder::Builder(Header* header, WritableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +OS2Table::Builder::Builder(Header* header, ReadableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +OS2Table::Builder::~Builder() {} + +CALLER_ATTACH FontDataTable* OS2Table::Builder::SubBuildTable( + ReadableFontData* data) { + FontDataTablePtr table = new OS2Table(header(), data); + return table.Detach(); +} + +CALLER_ATTACH OS2Table::Builder* + OS2Table::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new OS2Table::Builder(header, data); + return builder.Detach(); +} + +int32_t OS2Table::Builder::TableVersion() { + return InternalReadData()->ReadUShort(Offset::kVersion); +} + +void OS2Table::Builder::SetTableVersion(int32_t version) { + InternalWriteData()->WriteUShort(Offset::kVersion, version); +} + +int32_t OS2Table::Builder::XAvgCharWidth() { + return InternalReadData()->ReadShort(Offset::kXAvgCharWidth); +} + +void OS2Table::Builder::SetXAvgCharWidth(int32_t width) { + InternalWriteData()->WriteShort(Offset::kXAvgCharWidth, width); +} + +int32_t OS2Table::Builder::UsWeightClass() { + return InternalReadData()->ReadUShort(Offset::kUsWeightClass); +} + +void OS2Table::Builder::SetUsWeightClass(int32_t weight) { + InternalWriteData()->WriteUShort(Offset::kUsWeightClass, weight); +} + +int32_t OS2Table::Builder::UsWidthClass() { + return InternalReadData()->ReadUShort(Offset::kUsWidthClass); +} + +void OS2Table::Builder::SetUsWidthClass(int32_t width) { + InternalWriteData()->WriteUShort(Offset::kUsWidthClass, width); +} + +int32_t OS2Table::Builder::FsType() { + return InternalReadData()->ReadUShort(Offset::kFsType); +} + +void OS2Table::Builder::SetFsType(int32_t fs_type) { + InternalWriteData()->WriteUShort(Offset::kFsType, fs_type); +} + +int32_t OS2Table::Builder::YSubscriptXSize() { + return InternalReadData()->ReadShort(Offset::kYSubscriptXSize); +} + +void OS2Table::Builder::SetYSubscriptXSize(int32_t size) { + InternalWriteData()->WriteShort(Offset::kYSubscriptXSize, size); +} + +int32_t OS2Table::Builder::YSubscriptYSize() { + return InternalReadData()->ReadShort(Offset::kYSubscriptYSize); +} + +void OS2Table::Builder::SetYSubscriptYSize(int32_t size) { + InternalWriteData()->WriteShort(Offset::kYSubscriptYSize, size); +} + +int32_t OS2Table::Builder::YSubscriptXOffset() { + return InternalReadData()->ReadShort(Offset::kYSubscriptXOffset); +} + +void OS2Table::Builder::SetYSubscriptXOffset(int32_t offset) { + InternalWriteData()->WriteShort(Offset::kYSubscriptXOffset, offset); +} + +int32_t OS2Table::Builder::YSubscriptYOffset() { + return InternalReadData()->ReadShort(Offset::kYSubscriptYOffset); +} + +void OS2Table::Builder::SetYSubscriptYOffset(int32_t offset) { + InternalWriteData()->WriteShort(Offset::kYSubscriptYOffset, offset); +} + +int32_t OS2Table::Builder::YSuperscriptXSize() { + return InternalReadData()->ReadShort(Offset::kYSuperscriptXSize); +} + +void OS2Table::Builder::SetYSuperscriptXSize(int32_t size) { + InternalWriteData()->WriteShort(Offset::kYSuperscriptXSize, size); +} + +int32_t OS2Table::Builder::YSuperscriptYSize() { + return InternalReadData()->ReadShort(Offset::kYSuperscriptYSize); +} + +void OS2Table::Builder::SetYSuperscriptYSize(int32_t size) { + InternalWriteData()->WriteShort(Offset::kYSuperscriptYSize, size); +} + +int32_t OS2Table::Builder::YSuperscriptXOffset() { + return InternalReadData()->ReadShort(Offset::kYSuperscriptXOffset); +} + +void OS2Table::Builder::SetYSuperscriptXOffset(int32_t offset) { + InternalWriteData()->WriteShort(Offset::kYSuperscriptXOffset, offset); +} + +int32_t OS2Table::Builder::YSuperscriptYOffset() { + return InternalReadData()->ReadShort(Offset::kYSuperscriptYOffset); +} + +void OS2Table::Builder::SetYSuperscriptYOffset(int32_t offset) { + InternalWriteData()->WriteShort(Offset::kYSuperscriptYOffset, offset); +} + +int32_t OS2Table::Builder::YStrikeoutSize() { + return InternalReadData()->ReadShort(Offset::kYStrikeoutSize); +} + +void OS2Table::Builder::SetYStrikeoutSize(int32_t size) { + InternalWriteData()->WriteShort(Offset::kYStrikeoutSize, size); +} + +int32_t OS2Table::Builder::YStrikeoutPosition() { + return InternalReadData()->ReadShort(Offset::kYStrikeoutPosition); +} + +void OS2Table::Builder::SetYStrikeoutPosition(int32_t position) { + InternalWriteData()->WriteShort(Offset::kYStrikeoutPosition, position); +} + +int32_t OS2Table::Builder::SFamilyClass() { + return InternalReadData()->ReadShort(Offset::kSFamilyClass); +} + +void OS2Table::Builder::SetSFamilyClass(int32_t family) { + InternalWriteData()->WriteShort(Offset::kSFamilyClass, family); +} + +void OS2Table::Builder::Panose(ByteVector* value) { + assert(value); + value->clear(); + value->resize(Offset::kPanoseLength); + InternalReadData()->ReadBytes(Offset::kPanose, + &((*value)[0]), + 0, + Offset::kPanoseLength); +} + +void OS2Table::Builder::SetPanose(ByteVector* panose) { + assert(panose); + if (panose->size() != Offset::kPanoseLength) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalArgumentException("Panose bytes must be exactly 10 in length"); +#endif + return; + } + InternalWriteData()->WriteBytes(Offset::kPanose, panose); +} + +int64_t OS2Table::Builder::UlUnicodeRange1() { + return InternalReadData()->ReadULong(Offset::kUlUnicodeRange1); +} + +void OS2Table::Builder::SetUlUnicodeRange1(int64_t range) { + InternalWriteData()->WriteULong(Offset::kUlUnicodeRange1, range); +} + +int64_t OS2Table::Builder::UlUnicodeRange2() { + return InternalReadData()->ReadULong(Offset::kUlUnicodeRange2); +} + +void OS2Table::Builder::SetUlUnicodeRange2(int64_t range) { + InternalWriteData()->WriteULong(Offset::kUlUnicodeRange2, range); +} + +int64_t OS2Table::Builder::UlUnicodeRange3() { + return InternalReadData()->ReadULong(Offset::kUlUnicodeRange3); +} + +void OS2Table::Builder::SetUlUnicodeRange3(int64_t range) { + InternalWriteData()->WriteULong(Offset::kUlUnicodeRange3, range); +} + +int64_t OS2Table::Builder::UlUnicodeRange4() { + return InternalReadData()->ReadULong(Offset::kUlUnicodeRange4); +} + +void OS2Table::Builder::SetUlUnicodeRange4(int64_t range) { + InternalWriteData()->WriteULong(Offset::kUlUnicodeRange4, range); +} + +void OS2Table::Builder::AchVendId(ByteVector* b) { + assert(b); + b->clear(); + b->resize(4); + InternalReadData()->ReadBytes(Offset::kAchVendId, &((*b)[0]), 0, 4); +} + +void OS2Table::Builder::SetAchVendId(ByteVector* b) { + assert(b); + assert(b->size()); + InternalWriteData()->WriteBytesPad(Offset::kAchVendId, + b, + 0, + std::min( + (size_t)Offset::kAchVendIdLength, + b->size()), + static_cast(' ')); +} + +int32_t OS2Table::Builder::FsSelection() { + return InternalReadData()->ReadUShort(Offset::kFsSelection); +} + +void OS2Table::Builder::SetFsSelection(int32_t fs_selection) { + InternalWriteData()->WriteUShort(Offset::kFsSelection, fs_selection); +} + +int32_t OS2Table::Builder::UsFirstCharIndex() { + return InternalReadData()->ReadUShort(Offset::kUsFirstCharIndex); +} + +void OS2Table::Builder::SetUsFirstCharIndex(int32_t first_index) { + InternalWriteData()->WriteUShort(Offset::kUsFirstCharIndex, first_index); +} + +int32_t OS2Table::Builder::UsLastCharIndex() { + return InternalReadData()->ReadUShort(Offset::kUsLastCharIndex); +} + +void OS2Table::Builder::SetUsLastCharIndex(int32_t last_index) { + InternalWriteData()->WriteUShort(Offset::kUsLastCharIndex, last_index); +} + +int32_t OS2Table::Builder::STypoAscender() { + return InternalReadData()->ReadShort(Offset::kSTypoAscender); +} + +void OS2Table::Builder::SetSTypoAscender(int32_t ascender) { + InternalWriteData()->WriteShort(Offset::kSTypoAscender, ascender); +} + +int32_t OS2Table::Builder::STypoDescender() { + return InternalReadData()->ReadShort(Offset::kSTypoDescender); +} + +void OS2Table::Builder::SetSTypoDescender(int32_t descender) { + InternalWriteData()->WriteShort(Offset::kSTypoDescender, descender); +} + +int32_t OS2Table::Builder::STypoLineGap() { + return InternalReadData()->ReadShort(Offset::kSTypoLineGap); +} + +void OS2Table::Builder::SetSTypoLineGap(int32_t line_gap) { + InternalWriteData()->WriteShort(Offset::kSTypoLineGap, line_gap); +} + +int32_t OS2Table::Builder::UsWinAscent() { + return InternalReadData()->ReadUShort(Offset::kUsWinAscent); +} + +void OS2Table::Builder::SetUsWinAscent(int32_t ascent) { + InternalWriteData()->WriteUShort(Offset::kUsWinAscent, ascent); +} + +int32_t OS2Table::Builder::UsWinDescent() { + return InternalReadData()->ReadUShort(Offset::kUsWinDescent); +} + +void OS2Table::Builder::SetUsWinDescent(int32_t descent) { + InternalWriteData()->WriteUShort(Offset::kUsWinDescent, descent); +} + +int64_t OS2Table::Builder::UlCodePageRange1() { + return InternalReadData()->ReadULong(Offset::kUlCodePageRange1); +} + +void OS2Table::Builder::SetUlCodePageRange1(int64_t range) { + InternalWriteData()->WriteULong(Offset::kUlCodePageRange1, range); +} + +int64_t OS2Table::Builder::UlCodePageRange2() { + return InternalReadData()->ReadULong(Offset::kUlCodePageRange2); +} + +void OS2Table::Builder::SetUlCodePageRange2(int64_t range) { + InternalWriteData()->WriteULong(Offset::kUlCodePageRange2, range); +} + +int32_t OS2Table::Builder::SxHeight() { + return InternalReadData()->ReadShort(Offset::kSxHeight); +} + +void OS2Table::Builder::SetSxHeight(int32_t height) { + InternalWriteData()->WriteShort(Offset::kSxHeight, height); +} + +int32_t OS2Table::Builder::SCapHeight() { + return InternalReadData()->ReadShort(Offset::kSCapHeight); +} + +void OS2Table::Builder::SetSCapHeight(int32_t height) { + InternalWriteData()->WriteShort(Offset::kSCapHeight, height); +} + +int32_t OS2Table::Builder::UsDefaultChar() { + return InternalReadData()->ReadUShort(Offset::kUsDefaultChar); +} + +void OS2Table::Builder::SetUsDefaultChar(int32_t default_char) { + InternalWriteData()->WriteUShort(Offset::kUsDefaultChar, default_char); +} + +int32_t OS2Table::Builder::UsBreakChar() { + return InternalReadData()->ReadUShort(Offset::kUsBreakChar); +} + +void OS2Table::Builder::SetUsBreakChar(int32_t break_char) { + InternalWriteData()->WriteUShort(Offset::kUsBreakChar, break_char); +} + +int32_t OS2Table::Builder::UsMaxContext() { + return InternalReadData()->ReadUShort(Offset::kUsMaxContext); +} + +void OS2Table::Builder::SetUsMaxContext(int32_t max_context) { + InternalWriteData()->WriteUShort(Offset::kUsMaxContext, max_context); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/core/os2_table.h b/src/sfntly/src/sfntly/table/core/os2_table.h new file mode 100644 index 0000000000..00d26d2a3b --- /dev/null +++ b/src/sfntly/src/sfntly/table/core/os2_table.h @@ -0,0 +1,508 @@ +/* + * 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_CORE_OS2_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_OS2_TABLE_H_ + +#include "sfntly/port/refcount.h" +#include "sfntly/table/table.h" +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +struct WeightClass { + enum { + kThin = 100, + kExtraLight = 200, + kUltraLight = 200, + kLight = 300, + kNormal = 400, + kRegular = 400, + kMedium = 500, + kSemiBold = 600, + kDemiBold = 600, + kBold = 700, + kExtraBold = 800, + kUltraBold = 800, + kBlack = 900, + kHeavy = 900 + }; +}; + +struct WidthClass { + enum { + kUltraCondensed = 1, + kExtraCondensed = 2, + kCondensed = 3, + kSemiCondensed = 4, + kMedium = 5, + kNormal = 5, + kSemiExpanded = 6, + kExpanded = 7, + kExtraExpanded = 8, + kUltraExpanded = 9 + }; +}; + +// Flags to indicate the embedding licensing rights for a font. +struct EmbeddingFlags { + enum { + kReserved0 = 1 << 0, + kRestrictedLicenseEmbedding = 1 << 1, + kPreviewAndPrintEmbedding = 1 << 2, + kEditableEmbedding = 1 << 3, + kReserved4 = 1 << 4, + kReserved5 = 1 << 5, + kReserved6 = 1 << 6, + kReserved7 = 1 << 7, + kNoSubsetting = 1 << 8, + kBitmapEmbeddingOnly = 1 << 9, + kReserved10 = 1 << 10, + kReserved11 = 1 << 11, + kReserved12 = 1 << 12, + kReserved13 = 1 << 13, + kReserved14 = 1 << 14, + kReserved15 = 1 << 15 + }; +}; + +struct UnicodeRange { + enum { + // Do NOT reorder. This enum relies on the ordering of the data matching the + // ordinal numbers of the properties. + kBasicLatin, + kLatin1Supplement, + kLatinExtendedA, + kLatinExtendedB, + kIPAExtensions, + kSpacingModifierLetters, + kCombiningDiacriticalMarks, + kGreekAndCoptic, + kCoptic, + kCyrillic, + kArmenian, + kHebrew, + kVai, + kArabic, + kNKo, + kDevanagari, + kBengali, + kGurmukhi, + kGujarati, + kOriya, + kTamil, + kTelugu, + kKannada, + kMalayalam, + kThai, + kLao, + kGeorgian, + kBalinese, + kHangulJamo, + kLatinExtendedAdditional, + kGreekExtended, + kGeneralPunctuation, + kSuperscriptsAndSubscripts, + kCurrencySymbols, + kNumberForms, + kArrows, + kMathematicalOperators, + kMiscTechnical, + kControlPictures, + kOCR, + kEnclosedAlphanumerics, + kBoxDrawing, + kBlockElements, + kGeometricShapes, + kMiscSymbols, + kDingbats, + kCJKSymbolsAndPunctuation, + kHiragana, + kKatakana, + kBopomofo, + kHangulCompatibilityJamo, + kPhagspa, + kEnclosedCJKLettersAndMonths, + kCJKCompatibility, + kHangulSyllables, + kNonPlane0, + kPhoenician, + kCJKUnifiedIdeographs, + kPrivateUseAreaPlane0, + kCJKStrokes, + kAlphabeticPresentationForms, + kArabicPresentationFormsA, + kCombiningHalfMarks, + kVerticalForms, + kSmallFormVariants, + kArabicPresentationFormsB, + kHalfwidthAndFullwidthForms, + kSpecials, + kTibetan, + kSyriac, + kThaana, + kSinhala, + kMyanmar, + kEthiopic, + kCherokee, + kUnifiedCanadianAboriginalSyllabics, + kOgham, + kRunic, + kKhmer, + kMongolian, + kBraillePatterns, + kYiSyllables, + kTagalog, + kOldItalic, + kGothic, + kDeseret, + kMusicalSymbols, + kMathematicalAlphanumericSymbols, + kPrivateUsePlane15And16, + kVariationSelectors, + kTags, + kLimbu, + kTaiLe, + kNewTaiLue, + kBuginese, + kGlagolitic, + kTifnagh, + kYijingHexagramSymbols, + kSylotiNagari, + kLinearB, + kAncientGreekNumbers, + kUgaritic, + kOldPersian, + kShavian, + kOsmanya, + kCypriotSyllabary, + kKharoshthi, + kTaiXuanJingSymbols, + kCuneiform, + kCountingRodNumerals, + kSudanese, + kLepcha, + kOlChiki, + kSaurashtra, + kKayahLi, + kRejang, + kCharm, + kAncientSymbols, + kPhaistosDisc, + kCarian, + kDominoTiles, + kReserved123, + kReserved124, + kReserved125, + kReserved126, + kReserved127, + kLast = kReserved127 + }; + + int32_t range(int32_t bit); + // UNIMPLEMENTED: EnumSet asSet(long range1, long range2, + // long range3, long range4) + // long[] asArray(EnumSet rangeSet) +}; + +struct FsSelection { + enum { + kITALIC = 1 << 0, + kUNDERSCORE = 1 << 1, + kNEGATIVE = 1 << 2, + kOUTLINED = 1 << 3, + kSTRIKEOUT = 1 << 4, + kBOLD = 1 << 5, + kREGULAR = 1 << 6, + kUSE_TYPO_METRICS = 1 << 7, + kWWS = 1 << 8, + kOBLIQUE = 1 << 9 + }; + // UNIMPLEMENTED: EnumSet asSet(long range1, long range2, + // long range3, long range4) + // long[] asArray(EnumSet rangeSet) +}; + +// C++ port only: C++ does not support 64-bit enums until C++0x. For better +// portability, we need to use static const int64_t instead. +struct CodePageRange { + static const int64_t kLatin1_1252; + static const int64_t kLatin2_1250; + static const int64_t kCyrillic_1251; + static const int64_t kGreek_1253; + static const int64_t kTurkish_1254; + static const int64_t kHebrew_1255; + static const int64_t kArabic_1256; + static const int64_t kWindowsBaltic_1257; + static const int64_t kVietnamese_1258; + static const int64_t kAlternateANSI9; + static const int64_t kAlternateANSI10; + static const int64_t kAlternateANSI11; + static const int64_t kAlternateANSI12; + static const int64_t kAlternateANSI13; + static const int64_t kAlternateANSI14; + static const int64_t kAlternateANSI15; + static const int64_t kThai_874; + static const int64_t kJapanJIS_932; + static const int64_t kChineseSimplified_936; + static const int64_t kKoreanWansung_949; + static const int64_t kChineseTraditional_950; + static const int64_t kKoreanJohab_1361; + static const int64_t kAlternateANSI22; + static const int64_t kAlternateANSI23; + static const int64_t kAlternateANSI24; + static const int64_t kAlternateANSI25; + static const int64_t kAlternateANSI26; + static const int64_t kAlternateANSI27; + static const int64_t kAlternateANSI28; + static const int64_t kMacintoshCharacterSet; + static const int64_t kOEMCharacterSet; + static const int64_t kSymbolCharacterSet; + static const int64_t kReservedForOEM32; + static const int64_t kReservedForOEM33; + static const int64_t kReservedForOEM34; + static const int64_t kReservedForOEM35; + static const int64_t kReservedForOEM36; + static const int64_t kReservedForOEM37; + static const int64_t kReservedForOEM38; + static const int64_t kReservedForOEM39; + static const int64_t kReservedForOEM40; + static const int64_t kReservedForOEM41; + static const int64_t kReservedForOEM42; + static const int64_t kReservedForOEM43; + static const int64_t kReservedForOEM44; + static const int64_t kReservedForOEM45; + static const int64_t kReservedForOEM46; + static const int64_t kReservedForOEM47; + static const int64_t kIBMGreek_869; + static const int64_t kMSDOSRussion_866; + static const int64_t kMSDOSNordic_865; + static const int64_t kArabic_864; + static const int64_t kMSDOSCanadianFrench_863; + static const int64_t kHebrew_862; + static const int64_t kMSDOSIcelandic_861; + static const int64_t kMSDOSPortugese_860; + static const int64_t kIBMTurkish_857; + static const int64_t kIBMCyrillic_855; + static const int64_t kLatin2_852; + static const int64_t kMSDOSBaltic_775; + static const int64_t kGreek_737; + static const int64_t kArabic_708; + static const int64_t kLatin1_850; + static const int64_t kUS_437; + + // UNIMPLEMENTED: EnumSet asSet(long range1, long range2, + // long range3, long range4) + // long[] asArray(EnumSet rangeSet) +}; + +// An OS/2 table - 'OS/2'. +class OS2Table : public Table, public RefCounted { + public: + // A builder for the OS/2 table = 'OS/2'. + class Builder : public TableBasedTableBuilder, public RefCounted { + public: + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + int32_t TableVersion(); + void SetTableVersion(int32_t version); + int32_t XAvgCharWidth(); + void SetXAvgCharWidth(int32_t width); + int32_t UsWeightClass(); + void SetUsWeightClass(int32_t weight); + int32_t UsWidthClass(); + void SetUsWidthClass(int32_t width); + // UNIMPLEMENTED: EnumSet fsType() + // void setFsType(EnumSeT flagSet) + int32_t FsType(); + void SetFsType(int32_t fs_type); + int32_t YSubscriptXSize(); + void SetYSubscriptXSize(int32_t size); + int32_t YSubscriptYSize(); + void SetYSubscriptYSize(int32_t size); + int32_t YSubscriptXOffset(); + void SetYSubscriptXOffset(int32_t offset); + int32_t YSubscriptYOffset(); + void SetYSubscriptYOffset(int32_t offset); + int32_t YSuperscriptXSize(); + void SetYSuperscriptXSize(int32_t size); + int32_t YSuperscriptYSize(); + void SetYSuperscriptYSize(int32_t size); + int32_t YSuperscriptXOffset(); + void SetYSuperscriptXOffset(int32_t offset); + int32_t YSuperscriptYOffset(); + void SetYSuperscriptYOffset(int32_t offset); + int32_t YStrikeoutSize(); + void SetYStrikeoutSize(int32_t size); + int32_t YStrikeoutPosition(); + void SetYStrikeoutPosition(int32_t position); + int32_t SFamilyClass(); + void SetSFamilyClass(int32_t family); + void Panose(ByteVector* value); + void SetPanose(ByteVector* panose); + int64_t UlUnicodeRange1(); + void SetUlUnicodeRange1(int64_t range); + int64_t UlUnicodeRange2(); + void SetUlUnicodeRange2(int64_t range); + int64_t UlUnicodeRange3(); + void SetUlUnicodeRange3(int64_t range); + int64_t UlUnicodeRange4(); + void SetUlUnicodeRange4(int64_t range); + // UNIMPLEMENTED: EnumSet UlUnicodeRange() + // setUlUnicodeRange(EnumSet rangeSet) + void AchVendId(ByteVector* b); + // This field is 4 bytes in length and only the first 4 bytes of the byte + // array will be written. If the byte array is less than 4 bytes it will be + // padded out with space characters (0x20). + // @param b ach Vendor Id + void SetAchVendId(ByteVector* b); + // UNIMPLEMENTED: public EnumSet fsSelection() + int32_t FsSelection(); + void SetFsSelection(int32_t fs_selection); + int32_t UsFirstCharIndex(); + void SetUsFirstCharIndex(int32_t first_index); + int32_t UsLastCharIndex(); + void SetUsLastCharIndex(int32_t last_index); + int32_t STypoAscender(); + void SetSTypoAscender(int32_t ascender); + int32_t STypoDescender(); + void SetSTypoDescender(int32_t descender); + int32_t STypoLineGap(); + void SetSTypoLineGap(int32_t line_gap); + int32_t UsWinAscent(); + void SetUsWinAscent(int32_t ascent); + int32_t UsWinDescent(); + void SetUsWinDescent(int32_t descent); + int64_t UlCodePageRange1(); + void SetUlCodePageRange1(int64_t range); + int64_t UlCodePageRange2(); + void SetUlCodePageRange2(int64_t range); + // UNIMPLEMENTED: EnumSet ulCodePageRange() + // void setUlCodePageRange(EnumSet rangeSet) + int32_t SxHeight(); + void SetSxHeight(int32_t height); + int32_t SCapHeight(); + void SetSCapHeight(int32_t height); + int32_t UsDefaultChar(); + void SetUsDefaultChar(int32_t default_char); + int32_t UsBreakChar(); + void SetUsBreakChar(int32_t break_char); + int32_t UsMaxContext(); + void SetUsMaxContext(int32_t max_context); + }; + + ~OS2Table(); + + int32_t TableVersion(); + int32_t XAvgCharWidth(); + int32_t UsWeightClass(); + int32_t UsWidthClass(); + // UNIMPLEMENTED: public EnumSet fsType() + int32_t FsType(); + int32_t YSubscriptXSize(); + int32_t YSubscriptYSize(); + int32_t YSubscriptXOffset(); + int32_t YSubscriptYOffset(); + int32_t YSuperscriptXSize(); + int32_t YSuperscriptYSize(); + int32_t YSuperscriptXOffset(); + int32_t YSuperscriptYOffset(); + int32_t YStrikeoutSize(); + int32_t YStrikeoutPosition(); + int32_t SFamilyClass(); + void Panose(ByteVector* value); + int64_t UlUnicodeRange1(); + int64_t UlUnicodeRange2(); + int64_t UlUnicodeRange3(); + int64_t UlUnicodeRange4(); + // UNIMPLEMENTED: public EnumSet UlUnicodeRange() + void AchVendId(ByteVector* b); + // UNIMPLEMENTED: public EnumSet fsSelection() + int32_t FsSelection(); + int32_t UsFirstCharIndex(); + int32_t UsLastCharIndex(); + int32_t STypoAscender(); + int32_t STypoDescender(); + int32_t STypoLineGap(); + int32_t UsWinAscent(); + int32_t UsWinDescent(); + int64_t UlCodePageRange1(); + int64_t UlCodePageRange2(); + // UNIMPLEMENTED: public EnumSet ulCodePageRange() + int32_t SxHeight(); + int32_t SCapHeight(); + int32_t UsDefaultChar(); + int32_t UsBreakChar(); + int32_t UsMaxContext(); + + private: + struct Offset { + enum { + kVersion = 0, + kXAvgCharWidth = 2, + kUsWeightClass = 4, + kUsWidthClass = 6, + kFsType = 8, + kYSubscriptXSize = 10, + kYSubscriptYSize = 12, + kYSubscriptXOffset = 14, + kYSubscriptYOffset = 16, + kYSuperscriptXSize = 18, + kYSuperscriptYSize = 20, + kYSuperscriptXOffset = 22, + kYSuperscriptYOffset = 24, + kYStrikeoutSize = 26, + kYStrikeoutPosition = 28, + kSFamilyClass = 30, + kPanose = 32, + kPanoseLength = 10, // Length of panose bytes. + kUlUnicodeRange1 = 42, + kUlUnicodeRange2 = 46, + kUlUnicodeRange3 = 50, + kUlUnicodeRange4 = 54, + kAchVendId = 58, + kAchVendIdLength = 4, // Length of ach vend id bytes. + kFsSelection = 62, + kUsFirstCharIndex = 64, + kUsLastCharIndex = 66, + kSTypoAscender = 68, + kSTypoDescender = 70, + kSTypoLineGap = 72, + kUsWinAscent = 74, + kUsWinDescent = 76, + kUlCodePageRange1 = 78, + kUlCodePageRange2 = 82, + kSxHeight = 86, + kSCapHeight = 88, + kUsDefaultChar = 90, + kUsBreakChar = 92, + kUsMaxContext = 94 + }; + }; + + OS2Table(Header* header, ReadableFontData* data); +}; +typedef Ptr OS2TablePtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_OS2_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/font_data_table.cc b/src/sfntly/src/sfntly/table/font_data_table.cc new file mode 100644 index 0000000000..0e27f7a771 --- /dev/null +++ b/src/sfntly/src/sfntly/table/font_data_table.cc @@ -0,0 +1,193 @@ +/* + * 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/font_data_table.h" + +#include "sfntly/data/font_output_stream.h" + +namespace sfntly { + +/****************************************************************************** + * FontDataTable class + ******************************************************************************/ + +FontDataTable::FontDataTable(ReadableFontData* data) { + data_ = data; +} + +FontDataTable::~FontDataTable() {} + +ReadableFontData* FontDataTable::ReadFontData() { + return data_; +} + +int32_t FontDataTable::DataLength() { + return data_->Length(); +} + +int32_t FontDataTable::Serialize(OutputStream* os) { + return data_->CopyTo(os); +} + +int32_t FontDataTable::Serialize(WritableFontData* data) { + return data_->CopyTo(data); +} + +/****************************************************************************** + * FontDataTable::Builder class + ******************************************************************************/ +CALLER_ATTACH WritableFontData* FontDataTable::Builder::Data() { + WritableFontDataPtr new_data; + if (model_changed_) { + if (!SubReadyToSerialize()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IOException("Table not ready to build."); +#endif + return NULL; + } + int32_t size = SubDataSizeToSerialize(); + new_data.Attach(WritableFontData::CreateWritableFontData(size)); + SubSerialize(new_data); + } else { + ReadableFontDataPtr data = InternalReadData(); + new_data.Attach(WritableFontData::CreateWritableFontData( + data != NULL ? data->Length() : 0)); + if (data != NULL) { + data->CopyTo(new_data); + } + } + return new_data.Detach(); +} + +void FontDataTable::Builder::SetData(ReadableFontData* data) { + InternalSetData(data, true); +} + + +CALLER_ATTACH FontDataTable* FontDataTable::Builder::Build() { + FontDataTablePtr table; // NULL default table + ReadableFontDataPtr data = InternalReadData(); + if (model_changed_) { + // Let subclass serialize from model. + if (!SubReadyToSerialize()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IOException("Table not ready to build."); +#endif + return NULL; + } + int32_t size = SubDataSizeToSerialize(); + WritableFontDataPtr new_data; + new_data.Attach(WritableFontData::CreateWritableFontData(size)); + SubSerialize(new_data); + data = new_data; + } + + if (data != NULL) { + table = SubBuildTable(data); + NotifyPostTableBuild(table); + } + + r_data_.Release(); + w_data_.Release(); + return table; +} + +bool FontDataTable::Builder::ReadyToBuild() { + return true; +} + +ReadableFontData* FontDataTable::Builder::InternalReadData() { + return (r_data_ != NULL) ? r_data_.p_ : + static_cast(w_data_.p_); +} + +WritableFontData* FontDataTable::Builder::InternalWriteData() { + if (w_data_ == NULL) { + WritableFontDataPtr new_data; + new_data.Attach(WritableFontData::CreateWritableFontData( + r_data_ == NULL ? 0 : r_data_->Length())); +#if !defined (SFNTLY_NO_EXCEPTION) + try { +#endif + if (r_data_) { + r_data_->CopyTo(new_data); + } +#if !defined (SFNTLY_NO_EXCEPTION) + } catch (IOException& e) { + // TODO(stuartg): fix when IOExceptions are cleaned up + } +#endif + InternalSetData(new_data, false); + } + return w_data_.p_; +} + +FontDataTable::Builder::Builder() + : model_changed_(false), + contained_model_changed_(false), + data_changed_(false) { +} + +FontDataTable::Builder::Builder(int32_t data_size) + : model_changed_(false), + contained_model_changed_(false), + data_changed_(false) { + w_data_.Attach(WritableFontData::CreateWritableFontData(data_size)); +} + +FontDataTable::Builder::Builder(WritableFontData* data) + : model_changed_(false), + contained_model_changed_(false), + data_changed_(false) { + w_data_ = data; +} + +FontDataTable::Builder::Builder(ReadableFontData* data) + : model_changed_(false), + contained_model_changed_(false), + data_changed_(false) { + r_data_ = data; +} + +FontDataTable::Builder::~Builder() { +} + +void FontDataTable::Builder::NotifyPostTableBuild(FontDataTable* table) { + // Default: NOP. + UNREFERENCED_PARAMETER(table); +} + +void FontDataTable::Builder::InternalSetData(WritableFontData* data, + bool data_changed) { + w_data_ = data; + r_data_ = NULL; + if (data_changed) { + data_changed_ = true; + SubDataSet(); + } +} + +void FontDataTable::Builder::InternalSetData(ReadableFontData* data, + bool data_changed) { + w_data_ = NULL; + r_data_ = data; + if (data_changed) { + data_changed_ = true; + SubDataSet(); + } +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/font_data_table.h b/src/sfntly/src/sfntly/table/font_data_table.h new file mode 100644 index 0000000000..5e437e2f34 --- /dev/null +++ b/src/sfntly/src/sfntly/table/font_data_table.h @@ -0,0 +1,123 @@ +/* + * 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_FONT_DATA_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_FONT_DATA_TABLE_H_ + +#include "sfntly/data/readable_font_data.h" +#include "sfntly/data/writable_font_data.h" +#include "sfntly/port/refcount.h" + +namespace sfntly { + +// An abstract base for any table that contains a FontData. This is the root of +// the table class hierarchy. +class FontDataTable : virtual public RefCount { + public: + // Note: original version is abstract Builder + // C++ template is not designed that way so plain class is chosen. + class Builder : virtual public RefCount { + public: + // Get a snapshot copy of the internal data of the builder. + // This causes any internal data structures to be serialized to a new data + // object. This data object belongs to the caller and must be properly + // disposed of. No changes are made to the builder and any changes to the + // data directly do not affect the internal state. To do that a subsequent + // call must be made to {@link #SetData(WritableFontData)}. + // @return a copy of the internal data of the builder + CALLER_ATTACH WritableFontData* Data(); + virtual void SetData(ReadableFontData* data); + + // Note: changed from protected to avoid accessibility error in C++ + virtual CALLER_ATTACH FontDataTable* Build(); + virtual bool ReadyToBuild(); + + ReadableFontData* InternalReadData(); + WritableFontData* InternalWriteData(); + + bool data_changed() { return data_changed_; } + bool model_changed() { + return current_model_changed() || contained_model_changed(); + } + bool current_model_changed() { return model_changed_; } + bool contained_model_changed() { return contained_model_changed_; } + + bool set_model_changed() { return set_model_changed(true); } + bool set_model_changed(bool changed) { + bool old = model_changed_; + model_changed_ = changed; + return old; + } + + protected: + explicit Builder(); + + // Construct a FontDataTable.Builder with a WritableFontData backing store + // of size given. A positive size will create a fixed size backing store and + // a 0 or less size is an estimate for a growable backing store with the + // estimate being the absolute of the size. + // @param dataSize if positive then a fixed size; if 0 or less then an + // estimate for a growable size + Builder(int32_t data_size); + Builder(WritableFontData* data); + Builder(ReadableFontData* data); + virtual ~Builder(); + + // subclass API + virtual void NotifyPostTableBuild(FontDataTable* table); + virtual int32_t SubSerialize(WritableFontData* new_data) = 0; + virtual bool SubReadyToSerialize() = 0; + virtual int32_t SubDataSizeToSerialize() = 0; + virtual void SubDataSet() = 0; + virtual CALLER_ATTACH FontDataTable* + SubBuildTable(ReadableFontData* data) = 0; + + private: + void InternalSetData(WritableFontData* data, bool data_changed); + void InternalSetData(ReadableFontData* data, bool data_changed); + + WritableFontDataPtr w_data_; + ReadableFontDataPtr r_data_; + bool model_changed_; + bool contained_model_changed_; // may expand to list of submodel states + bool data_changed_; + }; + + explicit FontDataTable(ReadableFontData* data); + virtual ~FontDataTable(); + + // Get the readable font data for this table. + ReadableFontData* ReadFontData(); + + // Get the length of the data for this table in bytes. This is the full + // allocated length of the data underlying the table and may or may not + // include any padding. + virtual int32_t DataLength(); + + virtual int32_t Serialize(OutputStream* os); + + protected: + virtual int32_t Serialize(WritableFontData* data); + + // TODO(arthurhsu): style guide violation: protected member, need refactoring + ReadableFontDataPtr data_; +}; +typedef Ptr FontDataTablePtr; +typedef Ptr FontDataTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_FONT_DATA_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/generic_table_builder.cc b/src/sfntly/src/sfntly/table/generic_table_builder.cc new file mode 100644 index 0000000000..78e679772c --- /dev/null +++ b/src/sfntly/src/sfntly/table/generic_table_builder.cc @@ -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. + */ + +#include "sfntly/table/generic_table_builder.h" + +namespace sfntly { + +GenericTableBuilder::~GenericTableBuilder() {} + +CALLER_ATTACH +FontDataTable* GenericTableBuilder::SubBuildTable(ReadableFontData* data) { + // Note: In C++ port, we use GenericTable, the ref-counted version of Table + UNREFERENCED_PARAMETER(data); + Ptr table = new GenericTable(header(), InternalReadData()); + return table.Detach(); +} + +// static +CALLER_ATTACH GenericTableBuilder* + GenericTableBuilder::CreateBuilder(Header* header, WritableFontData* data) { + Ptr builder = + new GenericTableBuilder(header, data); + return builder.Detach(); +} + +GenericTableBuilder::GenericTableBuilder(Header* header, + WritableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +GenericTableBuilder::GenericTableBuilder(Header* header, + ReadableFontData* data) + : TableBasedTableBuilder(header, data) { +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/generic_table_builder.h b/src/sfntly/src/sfntly/table/generic_table_builder.h new file mode 100644 index 0000000000..a100ea072c --- /dev/null +++ b/src/sfntly/src/sfntly/table/generic_table_builder.h @@ -0,0 +1,42 @@ +/* + * 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_GENERIC_TABLE_BUILDER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_GENERIC_TABLE_BUILDER_H_ + +#include "sfntly/table/table_based_table_builder.h" + +namespace sfntly { + +// A table builder to do the minimal table building for an unknown table type. +class GenericTableBuilder : public TableBasedTableBuilder, + public RefCounted { + public: + virtual ~GenericTableBuilder(); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + + static CALLER_ATTACH GenericTableBuilder* + CreateBuilder(Header* header, WritableFontData* data); + + private: + GenericTableBuilder(Header* header, WritableFontData* data); + GenericTableBuilder(Header* header, ReadableFontData* data); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_ diff --git a/src/sfntly/src/sfntly/table/header.cc b/src/sfntly/src/sfntly/table/header.cc new file mode 100644 index 0000000000..672ace5749 --- /dev/null +++ b/src/sfntly/src/sfntly/table/header.cc @@ -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. + */ + +#include "sfntly/table/header.h" + +namespace sfntly { + +/****************************************************************************** + * Header class + ******************************************************************************/ +Header::Header(int32_t tag) + : tag_(tag), + offset_(0), + offset_valid_(false), + length_(0), + length_valid_(false), + checksum_(0), + checksum_valid_(false) { +} + +Header::Header(int32_t tag, int32_t length) + : tag_(tag), + offset_(0), + offset_valid_(false), + length_(length), + length_valid_(true), + checksum_(0), + checksum_valid_(false) { +} + +Header::Header(int32_t tag, int64_t checksum, int32_t offset, int32_t length) + : tag_(tag), + offset_(offset), + offset_valid_(true), + length_(length), + length_valid_(true), + checksum_(checksum), + checksum_valid_(true) { +} + +Header::~Header() {} + +bool HeaderComparatorByOffset::operator() (const HeaderPtr lhs, + const HeaderPtr rhs) { + return lhs->offset_ > rhs->offset_; +} + +bool HeaderComparatorByTag::operator() (const HeaderPtr lhs, + const HeaderPtr rhs) { + return lhs->tag_ > rhs->tag_; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/header.h b/src/sfntly/src/sfntly/table/header.h new file mode 100644 index 0000000000..280e556c47 --- /dev/null +++ b/src/sfntly/src/sfntly/table/header.h @@ -0,0 +1,114 @@ +/* + * 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_HEADER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_HEADER_H_ + +#include "sfntly/port/refcount.h" + +namespace sfntly { + +class Header : public RefCounted
{ + public: + // Make a partial header with only the basic info for an empty new table. + explicit Header(int32_t tag); + + // Make a partial header with only the basic info for a new table. + Header(int32_t tag, int32_t length); + + // Make a full header as read from an existing font. + Header(int32_t tag, int64_t checksum, int32_t offset, int32_t length); + virtual ~Header(); + + // Get the table tag. + int32_t tag() { return tag_; } + + // Get the table offset. The offset is from the start of the font file. This + // offset value is what was read from the font file during construction of the + // font. It may not be meaningful if the font was maninpulated through the + // builders. + int32_t offset() { return offset_; } + + // Is the offset in the header valid. The offset will not be valid if the + // table was constructed during building and has no physical location in a + // font file. + bool offset_valid() { return offset_valid_; } + + // Get the length of the table as recorded in the table record header. During + // building the header length will reflect the length that was initially read + // from the font file. This may not be consistent with the current state of + // the data. + int32_t length() { return length_; } + + // Is the length in the header valid. The length will not be valid if the + // table was constructed during building and has no physical location in a + // font file until the table is built from the builder. + bool length_valid() { return length_valid_; } + + // Get the checksum for the table as recorded in the table record header. + int64_t checksum() { return checksum_; } + + // Is the checksum valid. The checksum will not be valid if the table was + // constructed during building and has no physical location in a font file. + // Note that this does *NOT* check the validity of the checksum against + // the calculated checksum for the table data. + bool checksum_valid() { return checksum_valid_; } + + // UNIMPLEMENTED: boolean equals(Object obj) + // int hashCode() + // string toString() + + private: + int32_t tag_; + int32_t offset_; + bool offset_valid_; + int32_t length_; + bool length_valid_; + int64_t checksum_; + bool checksum_valid_; + + friend class HeaderComparatorByOffset; + friend class HeaderComparatorByTag; +}; +typedef Ptr
HeaderPtr; + +class HeaderComparator { + public: + virtual ~HeaderComparator() {} + virtual bool operator()(const HeaderPtr h1, + const HeaderPtr h2) = 0; +}; + +class HeaderComparatorByOffset : public HeaderComparator { + public: + virtual ~HeaderComparatorByOffset() {} + virtual bool operator()(const HeaderPtr h1, + const HeaderPtr h2); +}; + +class HeaderComparatorByTag : public HeaderComparator { + public: + virtual ~HeaderComparatorByTag() {} + virtual bool operator()(const HeaderPtr h1, + const HeaderPtr h2); +}; + +typedef std::set HeaderOffsetSortedSet; +typedef std::set HeaderTagSortedSet; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_HEADER_H_ diff --git a/src/sfntly/src/sfntly/table/subtable.cc b/src/sfntly/src/sfntly/table/subtable.cc new file mode 100644 index 0000000000..e5b906fd37 --- /dev/null +++ b/src/sfntly/src/sfntly/table/subtable.cc @@ -0,0 +1,64 @@ +/* + * 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/subtable.h" + +namespace sfntly { +/****************************************************************************** + * SubTable class + ******************************************************************************/ +SubTable::~SubTable() {} + +SubTable::SubTable(ReadableFontData* data, ReadableFontData* master_data) + : FontDataTable(data), padding_(0) { + master_data_ = master_data; +} + +SubTable::SubTable(ReadableFontData* data) + : FontDataTable(data), padding_(0) { +} + +/****************************************************************************** + * SubTable::Builder class + ******************************************************************************/ +SubTable::Builder::~Builder() { +} + +SubTable::Builder::Builder(int32_t data_size) + : FontDataTable::Builder(data_size) { +} + +SubTable::Builder::Builder(WritableFontData* data, + ReadableFontData* master_data) + : FontDataTable::Builder(data) { + master_data_ = master_data; +} + +SubTable::Builder::Builder(ReadableFontData* data, + ReadableFontData* master_data) + : FontDataTable::Builder(data) { + master_data_ = master_data; +} + +SubTable::Builder::Builder(WritableFontData* data) + : FontDataTable::Builder(data) { +} + +SubTable::Builder::Builder(ReadableFontData* data) + : FontDataTable::Builder(data) { +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/subtable.h b/src/sfntly/src/sfntly/table/subtable.h new file mode 100644 index 0000000000..fa6f4c6bcd --- /dev/null +++ b/src/sfntly/src/sfntly/table/subtable.h @@ -0,0 +1,73 @@ +/* + * 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_SUBTABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_SUBTABLE_H_ + +#include "sfntly/table/font_data_table.h" + +namespace sfntly { + +// An abstract base class for subtables. Subtables are smaller tables nested +// within other tables and don't have an entry in the main font index. Examples +// of these are the CMap subtables within CMap table (cmap) or a glyph within +// the glyph table (glyf). +class SubTable : public FontDataTable { + public: + class Builder : public FontDataTable::Builder { + public: + virtual ~Builder(); + + protected: + // @param data the data for the subtable being built + // @param master_data the data for the full table + Builder(int32_t data_size); + Builder(WritableFontData* data, ReadableFontData* master_data); + Builder(ReadableFontData* data, ReadableFontData* master_data); + explicit Builder(WritableFontData* data); + explicit Builder(ReadableFontData* data); + + ReadableFontData* master_read_data() { return master_data_; } + + private: + ReadableFontDataPtr master_data_; + }; + + virtual ~SubTable(); + virtual int32_t Padding() { return padding_; } + + // Sets the amount of padding that is part of the data being used by this + // subtable. + void set_padding(int32_t padding) { padding_ = padding; } + + protected: + SubTable(ReadableFontData* data, ReadableFontData* master_data); + + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + explicit SubTable(ReadableFontData* data); + + ReadableFontData* master_read_data() { return master_data_; } + + private: + // The data for the whole table in which this subtable is contained. + ReadableFontDataPtr master_data_; + int32_t padding_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_SUBTABLE_H_ diff --git a/src/sfntly/src/sfntly/table/subtable_container_table.h b/src/sfntly/src/sfntly/table/subtable_container_table.h new file mode 100644 index 0000000000..0f099debb4 --- /dev/null +++ b/src/sfntly/src/sfntly/table/subtable_container_table.h @@ -0,0 +1,48 @@ +/* + * 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 TYPOGRAPHY_FONT_SFNTLY_SRC_SFNTLY_TABLE_SUBTABLE_CONTAINER_TABLE_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SFNTLY_TABLE_SUBTABLE_CONTAINER_TABLE_H_ + +#include "sfntly/table/table.h" + +namespace sfntly { + +class SubTableContainerTable : public Table { + public: + class Builder : public Table::Builder { + public: + Builder(Header* header, WritableFontData* data) + : Table::Builder(header, data) { + } + + Builder(Header* header, ReadableFontData* data) + : Table::Builder(header, data) { + } + + virtual ~Builder() {} + }; + + SubTableContainerTable(Header* header, ReadableFontData* data) + : Table(header, data) { + } + + virtual ~SubTableContainerTable() {} +}; + +} // namespace sfntly + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SFNTLY_TABLE_SUBTABLE_CONTAINER_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/table.cc b/src/sfntly/src/sfntly/table/table.cc new file mode 100644 index 0000000000..cf574b838b --- /dev/null +++ b/src/sfntly/src/sfntly/table/table.cc @@ -0,0 +1,162 @@ +/* + * 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. + */ + +// type.h needs to be included first because of building issues on Windows +// Type aliases we delcare are defined in other headers and make the build +// fail otherwise. +#include "sfntly/port/type.h" +#include "sfntly/table/table.h" + +#include "sfntly/font.h" +#include "sfntly/tag.h" +#include "sfntly/table/bitmap/ebdt_table.h" +#include "sfntly/table/bitmap/eblc_table.h" +#include "sfntly/table/bitmap/ebsc_table.h" +#include "sfntly/table/core/cmap_table.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/core/name_table.h" +#include "sfntly/table/core/os2_table.h" +#include "sfntly/table/generic_table_builder.h" +#include "sfntly/table/table_based_table_builder.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/truetype/loca_table.h" + +namespace sfntly { + +/****************************************************************************** + * Table class + ******************************************************************************/ +Table::~Table() {} + +int64_t Table::CalculatedChecksum() { + return data_->Checksum(); +} + +void Table::SetFont(Font* font) { + font_ = font; +} + +Table::Table(Header* header, ReadableFontData* data) + : FontDataTable(data) { + header_ = header; +} + +/****************************************************************************** + * Table::Builder class + ******************************************************************************/ +Table::Builder::~Builder() { + header_.Release(); +} + +void Table::Builder::NotifyPostTableBuild(FontDataTable* table) { + if (model_changed() || data_changed()) { + Table* derived_table = down_cast(table); + derived_table->header_ = new Header(header()->tag(), + derived_table->DataLength()); + } +} + +CALLER_ATTACH +Table::Builder* Table::Builder::GetBuilder(Header* header, + WritableFontData* table_data) { + int32_t tag = header->tag(); + Table::Builder* builder_raw = NULL; + + // Note: Tables are commented out when they are not used/ported. + // TODO(arthurhsu): IMPLEMENT: finish tables that are not ported. + if (tag == Tag::head) { + builder_raw = static_cast( + FontHeaderTable::Builder::CreateBuilder(header, table_data)); +#if defined (SFNTLY_EXPERIMENTAL) + } else if (tag == Tag::cmap) { + builder_raw = static_cast( + CMapTable::Builder::CreateBuilder(header, table_data)); +#endif // SFNTLY_EXPERIMENTAL + } else if (tag == Tag::hhea) { + builder_raw = static_cast( + HorizontalHeaderTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::hmtx) { + builder_raw = static_cast( + HorizontalMetricsTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::maxp) { + builder_raw = static_cast( + MaximumProfileTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::name) { + builder_raw = static_cast( + NameTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::OS_2) { + builder_raw = static_cast( + OS2Table::Builder::CreateBuilder(header, table_data)); + }/* else if (tag == Tag::PostScript) { + builder_raw = static_cast( + PostScriptTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::cvt) { + builder_raw = static_cast( + ControlValueTable::Builder::CreateBuilder(header, table_data)); + }*/ else if (tag == Tag::glyf) { + builder_raw = static_cast( + GlyphTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::loca) { + builder_raw = static_cast( + LocaTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::EBDT || tag == Tag::bdat) { + builder_raw = static_cast( + EbdtTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::EBLC || tag == Tag::bloc) { + builder_raw = static_cast( + EblcTable::Builder::CreateBuilder(header, table_data)); + } else if (tag == Tag::EBSC) { + builder_raw = static_cast( + EbscTable::Builder::CreateBuilder(header, table_data)); + } /* else if (tag == Tag::prep) { + builder_raw = static_cast( + ControlProgramTable::Builder::CreateBuilder(header, table_data)); + }*/ else if (tag == Tag::bhed) { + builder_raw = static_cast( + FontHeaderTable::Builder::CreateBuilder(header, table_data)); +#if defined (SFNTLY_EXPERIMENTAL) + } else if (tag == Tag::hdmx) { + builder_raw = static_cast( + HorizontalDeviceMetricsTable::Builder::CreateBuilder(header, + table_data)); +#endif // SFNTLY_EXPERIMENTAL + } else { + builder_raw = static_cast( + GenericTableBuilder::CreateBuilder(header, table_data)); + } + + return builder_raw; +} + +Table::Builder::Builder(Header* header, WritableFontData* data) + : FontDataTable::Builder(data) { + header_ = header; +} + +Table::Builder::Builder(Header* header, ReadableFontData* data) + : FontDataTable::Builder(data) { + header_ = header; +} + +Table::Builder::Builder(Header* header) { + header_ = header; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/table.h b/src/sfntly/src/sfntly/table/table.h new file mode 100644 index 0000000000..6ebc22df8a --- /dev/null +++ b/src/sfntly/src/sfntly/table/table.h @@ -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_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_H_ + +#include +#include +#include +#include + +#include "sfntly/port/type.h" +#include "sfntly/table/font_data_table.h" +#include "sfntly/table/header.h" + +namespace sfntly { +class Font; + +// A concrete implementation of a root level table in the font. This is the base +// class used for all specific table implementations and is used as the generic +// table for all tables which have no specific implementations. +class Table : public FontDataTable { + public: + // Note: original version is Builder + // C++ template is not designed that way so plain old inheritance is + // chosen. + class Builder : public FontDataTable::Builder { + public: + virtual ~Builder(); + virtual Header* header() { return header_; } + virtual void NotifyPostTableBuild(FontDataTable* table); + + // Get a builder for the table type specified by the data in the header. + // @param header the header for the table + // @param tableData the data to be used to build the table from + // @return builder for the table specified + static CALLER_ATTACH Builder* GetBuilder(Header* header, + WritableFontData* table_data); + + // UNIMPLEMENTED: toString() + + protected: + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + Builder(Header* header); + + private: + Ptr
header_; + }; + + // Note: GenericTableBuilder moved to table_based_table_builder.h to avoid + // circular inclusion. + + virtual ~Table(); + + // Get the calculated checksum for the data in the table. + virtual int64_t CalculatedChecksum(); + + // Get the header for the table. + virtual Header* header() { return header_; } + + // Get the tag for the table from the record header. + virtual int32_t header_tag() { return header_->tag(); } + + // Get the offset for the table from the record header. + virtual int32_t header_offset() { return header_->offset(); } + + // Get the length of the table from the record header. + virtual int32_t header_length() { return header_->length(); } + + // Get the checksum for the table from the record header. + virtual int64_t header_checksum() { return header_->checksum(); } + + // UNIMPLEMENTED: toString() + + virtual void SetFont(Font* font); + + protected: + Table(Header* header, ReadableFontData* data); + + private: + Ptr
header_; + Ptr font_; +}; + +// C++ port only +class GenericTable : public Table, public RefCounted { + public: + GenericTable(Header* header, ReadableFontData* data) : Table(header, data) {} + virtual ~GenericTable() {} +}; + +typedef Ptr TablePtr; +typedef std::vector TableHeaderList; +typedef Ptr TableBuilderPtr; +typedef std::map TableMap; +typedef std::pair TableMapEntry; + +typedef std::map DataBlockMap; +typedef std::pair DataBlockEntry; +typedef std::map TableBuilderMap; +typedef std::pair TableBuilderEntry; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/table_based_table_builder.cc b/src/sfntly/src/sfntly/table/table_based_table_builder.cc new file mode 100644 index 0000000000..b505704638 --- /dev/null +++ b/src/sfntly/src/sfntly/table/table_based_table_builder.cc @@ -0,0 +1,69 @@ +/* + * 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/table_based_table_builder.h" + +namespace sfntly { + +/****************************************************************************** + * TableBasedTableBuilder class + ******************************************************************************/ +TableBasedTableBuilder::~TableBasedTableBuilder() {} + +int32_t TableBasedTableBuilder::SubSerialize(WritableFontData* data) { + UNREFERENCED_PARAMETER(data); + return 0; +} + +bool TableBasedTableBuilder::SubReadyToSerialize() { + return false; +} + +int32_t TableBasedTableBuilder::SubDataSizeToSerialize() { + return 0; +} + +void TableBasedTableBuilder::SubDataSet() { + table_ = NULL; +} + +CALLER_ATTACH FontDataTable* TableBasedTableBuilder::Build() { + FontDataTablePtr table = static_cast(GetTable()); + return table.Detach(); +} + +TableBasedTableBuilder::TableBasedTableBuilder(Header* header, + WritableFontData* data) + : Table::Builder(header, data) { +} + +TableBasedTableBuilder::TableBasedTableBuilder(Header* header, + ReadableFontData* data) + : Table::Builder(header, data) { +} + +TableBasedTableBuilder::TableBasedTableBuilder(Header* header) + : Table::Builder(header) { +} + +Table* TableBasedTableBuilder::GetTable() { + if (table_ == NULL) { + table_.Attach(down_cast(SubBuildTable(InternalReadData()))); + } + return table_; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/table_based_table_builder.h b/src/sfntly/src/sfntly/table/table_based_table_builder.h new file mode 100644 index 0000000000..d88eefd11e --- /dev/null +++ b/src/sfntly/src/sfntly/table/table_based_table_builder.h @@ -0,0 +1,48 @@ +/* + * 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_TABLE_BASED_TABLE_BUILDER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_BASED_TABLE_BUILDER_H_ + +#include "sfntly/table/table.h" + +namespace sfntly { + +class TableBasedTableBuilder : public Table::Builder { + public: + virtual ~TableBasedTableBuilder(); + + virtual int32_t SubSerialize(WritableFontData* new_data); + virtual bool SubReadyToSerialize(); + virtual int32_t SubDataSizeToSerialize(); + virtual void SubDataSet(); + virtual CALLER_ATTACH FontDataTable* Build(); + + protected: + TableBasedTableBuilder(Header* header, WritableFontData* data); + TableBasedTableBuilder(Header* header, ReadableFontData* data); + explicit TableBasedTableBuilder(Header* header); + + // C++ port: renamed table() to GetTable() + virtual Table* GetTable(); + + // TODO(arthurhsu): style guide violation: protected member, need refactor + TablePtr table_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_BASED_TABLE_BUILDER_H_ diff --git a/src/sfntly/src/sfntly/table/truetype/glyph_table.cc b/src/sfntly/src/sfntly/table/truetype/glyph_table.cc new file mode 100644 index 0000000000..f38fac5c5c --- /dev/null +++ b/src/sfntly/src/sfntly/table/truetype/glyph_table.cc @@ -0,0 +1,679 @@ +/* + * 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/truetype/glyph_table.h" + +#include + +#include "sfntly/port/exception_type.h" + +namespace sfntly { +/****************************************************************************** + * Constants + ******************************************************************************/ +const int32_t GlyphTable::SimpleGlyph::kFLAG_ONCURVE = 1; +const int32_t GlyphTable::SimpleGlyph::kFLAG_XSHORT = 1 << 1; +const int32_t GlyphTable::SimpleGlyph::kFLAG_YSHORT = 1 << 2; +const int32_t GlyphTable::SimpleGlyph::kFLAG_REPEAT = 1 << 3; +const int32_t GlyphTable::SimpleGlyph::kFLAG_XREPEATSIGN = 1 << 4; +const int32_t GlyphTable::SimpleGlyph::kFLAG_YREPEATSIGN = 1 << 5; + +const int32_t GlyphTable::CompositeGlyph::kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; +const int32_t GlyphTable::CompositeGlyph::kFLAG_ARGS_ARE_XY_VALUES = 1 << 1; +const int32_t GlyphTable::CompositeGlyph::kFLAG_ROUND_XY_TO_GRID = 1 << 2; +const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_A_SCALE = 1 << 3; +const int32_t GlyphTable::CompositeGlyph::kFLAG_RESERVED = 1 << 4; +const int32_t GlyphTable::CompositeGlyph::kFLAG_MORE_COMPONENTS = 1 << 5; +const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; +const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; +const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; +const int32_t GlyphTable::CompositeGlyph::kFLAG_USE_MY_METRICS = 1 << 9; +const int32_t GlyphTable::CompositeGlyph::kFLAG_OVERLAP_COMPOUND = 1 << 10; +const int32_t GlyphTable::CompositeGlyph::kFLAG_SCALED_COMPONENT_OFFSET = 1 << 11; +const int32_t GlyphTable::CompositeGlyph::kFLAG_UNSCALED_COMPONENT_OFFSET = 1 << 12; + +/****************************************************************************** + * GlyphTable class + ******************************************************************************/ +GlyphTable::~GlyphTable() { +} + +GlyphTable::Glyph* GlyphTable::GetGlyph(int32_t offset, int32_t length) { + return GlyphTable::Glyph::GetGlyph(this, this->data_, offset, length); +} + +GlyphTable::GlyphTable(Header* header, ReadableFontData* data) + : SubTableContainerTable(header, data) { +} + +/****************************************************************************** + * GlyphTable::Builder class + ******************************************************************************/ +GlyphTable::Builder::Builder(Header* header, ReadableFontData* data) + : SubTableContainerTable::Builder(header, data) { +} + +GlyphTable::Builder::~Builder() { +} + +void GlyphTable::Builder::SetLoca(const IntegerList& loca) { + loca_ = loca; + set_model_changed(false); + glyph_builders_.clear(); +} + +void GlyphTable::Builder::GenerateLocaList(IntegerList* locas) { + assert(locas); + GlyphBuilderList* glyph_builders = GetGlyphBuilders(); + locas->push_back(0); + if (glyph_builders->size() == 0) { + locas->push_back(0); + } else { + int32_t total = 0; + for (GlyphBuilderList::iterator b = glyph_builders->begin(), + b_end = glyph_builders->end(); + b != b_end; ++b) { + int32_t size = (*b)->SubDataSizeToSerialize(); + locas->push_back(total + size); + total += size; + } + } +} + +CALLER_ATTACH GlyphTable::Builder* + GlyphTable::Builder::CreateBuilder(Header* header, WritableFontData* data) { + Ptr builder; + builder = new GlyphTable::Builder(header, data); + return builder.Detach(); +} + +GlyphTable::GlyphBuilderList* GlyphTable::Builder::GlyphBuilders() { + return GetGlyphBuilders(); +} + +void GlyphTable::Builder::SetGlyphBuilders(GlyphBuilderList* glyph_builders) { + glyph_builders_ = *glyph_builders; + set_model_changed(); +} + +CALLER_ATTACH GlyphTable::Glyph::Builder* + GlyphTable::Builder::GlyphBuilder(ReadableFontData* data) { + return Glyph::Builder::GetBuilder(this, data); +} + +CALLER_ATTACH FontDataTable* + GlyphTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = new GlyphTable(header(), data); + return table.Detach(); +} + +void GlyphTable::Builder::SubDataSet() { + glyph_builders_.clear(); + set_model_changed(false); +} + +int32_t GlyphTable::Builder::SubDataSizeToSerialize() { + if (glyph_builders_.empty()) + return 0; + + bool variable = false; + int32_t size = 0; + + // Calculate size of each table. + for (GlyphBuilderList::iterator b = glyph_builders_.begin(), + end = glyph_builders_.end(); b != end; ++b) { + int32_t glyph_size = (*b)->SubDataSizeToSerialize(); + size += abs(glyph_size); + variable |= glyph_size <= 0; + } + return variable ? -size : size; +} + +bool GlyphTable::Builder::SubReadyToSerialize() { + return !glyph_builders_.empty(); +} + +int32_t GlyphTable::Builder::SubSerialize(WritableFontData* new_data) { + int32_t size = 0; + for (GlyphBuilderList::iterator b = glyph_builders_.begin(), + end = glyph_builders_.end(); b != end; ++b) { + FontDataPtr data; + data.Attach(new_data->Slice(size)); + size += (*b)->SubSerialize(down_cast(data.p_)); + } + return size; +} + +void GlyphTable::Builder::Initialize(ReadableFontData* data, + const IntegerList& loca) { + if (data != NULL) { + if (loca_.empty()) { + return; + } + int32_t loca_value; + int32_t last_loca_value = loca[0]; + for (size_t i = 1; i < loca.size(); ++i) { + loca_value = loca[i]; + GlyphBuilderPtr builder; + builder.Attach( + Glyph::Builder::GetBuilder(this, + data, + last_loca_value /*offset*/, + loca_value - last_loca_value /*length*/)); + glyph_builders_.push_back(builder); + last_loca_value = loca_value; + } + } +} + +GlyphTable::GlyphBuilderList* GlyphTable::Builder::GetGlyphBuilders() { + if (glyph_builders_.empty()) { + if (InternalReadData() && !loca_.empty()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalStateException( + "Loca values not set - unable to parse glyph data."); +#endif + return NULL; + } + Initialize(InternalReadData(), loca_); + set_model_changed(); + } + return &glyph_builders_; +} + +void GlyphTable::Builder::Revert() { + glyph_builders_.clear(); + set_model_changed(false); +} + +/****************************************************************************** + * GlyphTable::Glyph class + ******************************************************************************/ +GlyphTable::Glyph::~Glyph() {} + +CALLER_ATTACH GlyphTable::Glyph* + GlyphTable::Glyph::GetGlyph(GlyphTable* table, + ReadableFontData* data, + int32_t offset, + int32_t length) { + UNREFERENCED_PARAMETER(table); + int32_t type = GlyphType(data, offset, length); + GlyphPtr glyph; + + ReadableFontDataPtr sliced_data; + sliced_data.Attach(down_cast(data->Slice(offset, length))); + if (type == GlyphType::kSimple) { + glyph = new SimpleGlyph(sliced_data); + } else { + glyph = new CompositeGlyph(sliced_data); + } + return glyph.Detach(); +} + +int32_t GlyphTable::Glyph::Padding() { + Initialize(); + return SubTable::Padding(); +} + +int32_t GlyphTable::Glyph::GlyphType() { + return glyph_type_; +} + +int32_t GlyphTable::Glyph::NumberOfContours() { + return number_of_contours_; +} + +int32_t GlyphTable::Glyph::XMin() { + return data_->ReadShort(Offset::kXMin); +} + +int32_t GlyphTable::Glyph::XMax() { + return data_->ReadShort(Offset::kXMax); +} + +int32_t GlyphTable::Glyph::YMin() { + return data_->ReadShort(Offset::kYMin); +} + +int32_t GlyphTable::Glyph::YMax() { + return data_->ReadShort(Offset::kYMax); +} + +GlyphTable::Glyph::Glyph(ReadableFontData* data, int32_t glyph_type) + : SubTable(data), + glyph_type_(glyph_type) { + if (data_->Length() == 0) { + number_of_contours_ = 0; + } else { + // -1 if composite + number_of_contours_ = data_->ReadShort(Offset::kNumberOfContours); + } +} + +int32_t GlyphTable::Glyph::GlyphType(ReadableFontData* data, + int32_t offset, + int32_t length) { + if (length == 0) { + return GlyphType::kSimple; + } + int32_t number_of_contours = data->ReadShort(offset); + if (number_of_contours >= 0) { + return GlyphType::kSimple; + } + return GlyphType::kComposite; +} + +/****************************************************************************** + * GlyphTable::Glyph::Builder class + ******************************************************************************/ +GlyphTable::Glyph::Builder::~Builder() { +} + +GlyphTable::Glyph::Builder::Builder(WritableFontData* data) + : SubTable::Builder(data) { +} + +GlyphTable::Glyph::Builder::Builder(ReadableFontData* data) + : SubTable::Builder(data) { +} + +CALLER_ATTACH GlyphTable::Glyph::Builder* + GlyphTable::Glyph::Builder::GetBuilder( + GlyphTable::Builder* table_builder, + ReadableFontData* data) { + return GetBuilder(table_builder, data, 0, data->Length()); +} + +CALLER_ATTACH GlyphTable::Glyph::Builder* + GlyphTable::Glyph::Builder::GetBuilder( + GlyphTable::Builder* table_builder, + ReadableFontData* data, + int32_t offset, + int32_t length) { + UNREFERENCED_PARAMETER(table_builder); + int32_t type = Glyph::GlyphType(data, offset, length); + GlyphBuilderPtr builder; + ReadableFontDataPtr sliced_data; + sliced_data.Attach(down_cast(data->Slice(offset, length))); + if (type == GlyphType::kSimple) { + builder = new SimpleGlyph::SimpleGlyphBuilder(sliced_data); + } else { + builder = new CompositeGlyph::CompositeGlyphBuilder(sliced_data); + } + return builder.Detach(); +} + +void GlyphTable::Glyph::Builder::SubDataSet() { + // NOP +} + +int32_t GlyphTable::Glyph::Builder::SubDataSizeToSerialize() { + return InternalReadData()->Length(); +} + +bool GlyphTable::Glyph::Builder::SubReadyToSerialize() { + return true; +} + +int32_t GlyphTable::Glyph::Builder::SubSerialize(WritableFontData* new_data) { + return InternalReadData()->CopyTo(new_data); +} + +/****************************************************************************** + * GlyphTable::SimpleGlyph + ******************************************************************************/ +GlyphTable::SimpleGlyph::SimpleGlyph(ReadableFontData* data) + : GlyphTable::Glyph(data, GlyphType::kSimple), initialized_(false) { +} + +GlyphTable::SimpleGlyph::~SimpleGlyph() { +} + +int32_t GlyphTable::SimpleGlyph::InstructionSize() { + Initialize(); + return instruction_size_; +} + +CALLER_ATTACH ReadableFontData* GlyphTable::SimpleGlyph::Instructions() { + Initialize(); + return down_cast( + data_->Slice(instructions_offset_, InstructionSize())); +} + +int32_t GlyphTable::SimpleGlyph::NumberOfPoints(int32_t contour) { + Initialize(); + if (contour >= NumberOfContours()) { + return 0; + } + return contour_index_[contour + 1] - contour_index_[contour]; +} + +int32_t GlyphTable::SimpleGlyph::XCoordinate(int32_t contour, int32_t point) { + Initialize(); + return x_coordinates_[contour_index_[contour] + point]; +} + +int32_t GlyphTable::SimpleGlyph::YCoordinate(int32_t contour, int32_t point) { + Initialize(); + return y_coordinates_[contour_index_[contour] + point]; +} + +bool GlyphTable::SimpleGlyph::OnCurve(int32_t contour, int32_t point) { + Initialize(); + return on_curve_[contour_index_[contour] + point]; +} + +void GlyphTable::SimpleGlyph::Initialize() { + AutoLock lock(initialization_lock_); + if (initialized_) { + return; + } + + if (ReadFontData()->Length() == 0) { + instruction_size_ = 0; + number_of_points_ = 0; + instructions_offset_ = 0; + flags_offset_ = 0; + x_coordinates_offset_ = 0; + y_coordinates_offset_ = 0; + return; + } + + instruction_size_ = data_->ReadUShort(Offset::kSimpleEndPtsOfCountours + + NumberOfContours() * DataSize::kUSHORT); + instructions_offset_ = Offset::kSimpleEndPtsOfCountours + + (NumberOfContours() + 1) * DataSize::kUSHORT; + flags_offset_ = instructions_offset_ + instruction_size_ * DataSize::kBYTE; + number_of_points_ = ContourEndPoint(NumberOfContours() - 1) + 1; + x_coordinates_.resize(number_of_points_); + y_coordinates_.resize(number_of_points_); + on_curve_.resize(number_of_points_); + ParseData(false); + x_coordinates_offset_ = flags_offset_ + flag_byte_count_ * DataSize::kBYTE; + y_coordinates_offset_ = x_coordinates_offset_ + x_byte_count_ * + DataSize::kBYTE; + contour_index_.resize(NumberOfContours() + 1); + contour_index_[0] = 0; + for (uint32_t contour = 0; contour < contour_index_.size() - 1; ++contour) { + contour_index_[contour + 1] = ContourEndPoint(contour) + 1; + } + ParseData(true); + int32_t non_padded_data_length = + 5 * DataSize::kSHORT + + (NumberOfContours() * DataSize::kUSHORT) + + DataSize::kUSHORT + + (instruction_size_ * DataSize::kBYTE) + + (flag_byte_count_ * DataSize::kBYTE) + + (x_byte_count_ * DataSize::kBYTE) + + (y_byte_count_ * DataSize::kBYTE); + set_padding(DataLength() - non_padded_data_length); + initialized_ = true; +} + +void GlyphTable::SimpleGlyph::ParseData(bool fill_arrays) { + int32_t flag = 0; + int32_t flag_repeat = 0; + int32_t flag_index = 0; + int32_t x_byte_index = 0; + int32_t y_byte_index = 0; + + for (int32_t point_index = 0; point_index < number_of_points_; + ++point_index) { + // get the flag for the current point + if (flag_repeat == 0) { + flag = FlagAsInt(flag_index++); + if ((flag & kFLAG_REPEAT) == kFLAG_REPEAT) { + flag_repeat = FlagAsInt(flag_index++); + } + } else { + flag_repeat--; + } + + // on the curve? + if (fill_arrays) { + on_curve_[point_index] = ((flag & kFLAG_ONCURVE) == kFLAG_ONCURVE); + } + // get the x coordinate + if ((flag & kFLAG_XSHORT) == kFLAG_XSHORT) { + // single byte x coord value + if (fill_arrays) { + x_coordinates_[point_index] = + data_->ReadUByte(x_coordinates_offset_ + x_byte_index); + x_coordinates_[point_index] *= + ((flag & kFLAG_XREPEATSIGN) == kFLAG_XREPEATSIGN) ? 1 : -1; + } + x_byte_index++; + } else { + // double byte coord value + if (!((flag & kFLAG_XREPEATSIGN) == kFLAG_XREPEATSIGN)) { + if (fill_arrays) { + x_coordinates_[point_index] = + data_->ReadShort(x_coordinates_offset_ + x_byte_index); + } + x_byte_index += 2; + } + } + if (fill_arrays && point_index > 0) { + x_coordinates_[point_index] += x_coordinates_[point_index - 1]; + } + + // get the y coordinate + if ((flag & kFLAG_YSHORT) == kFLAG_YSHORT) { + if (fill_arrays) { + y_coordinates_[point_index] = + data_->ReadUByte(y_coordinates_offset_ + y_byte_index); + y_coordinates_[point_index] *= + ((flag & kFLAG_YREPEATSIGN) == kFLAG_YREPEATSIGN) ? 1 : -1; + } + y_byte_index++; + } else { + if (!((flag & kFLAG_YREPEATSIGN) == kFLAG_YREPEATSIGN)) { + if (fill_arrays) { + y_coordinates_[point_index] = + data_->ReadShort(y_coordinates_offset_ + y_byte_index); + } + y_byte_index += 2; + } + } + if (fill_arrays && point_index > 0) { + y_coordinates_[point_index] += y_coordinates_[point_index - 1]; + } + } + flag_byte_count_ = flag_index; + x_byte_count_ = x_byte_index; + y_byte_count_ = y_byte_index; +} + +int32_t GlyphTable::SimpleGlyph::FlagAsInt(int32_t index) { + return data_->ReadUByte(flags_offset_ + index * DataSize::kBYTE); +} + +int32_t GlyphTable::SimpleGlyph::ContourEndPoint(int32_t contour) { + return data_->ReadUShort(contour * DataSize::kUSHORT + + Offset::kSimpleEndPtsOfCountours); +} + +/****************************************************************************** + * GlyphTable::SimpleGlyph::Builder + ******************************************************************************/ +GlyphTable::SimpleGlyph::SimpleGlyphBuilder::~SimpleGlyphBuilder() { +} + +GlyphTable::SimpleGlyph::SimpleGlyphBuilder::SimpleGlyphBuilder( + WritableFontData* data) + : Glyph::Builder(data) { +} + +GlyphTable::SimpleGlyph::SimpleGlyphBuilder::SimpleGlyphBuilder( + ReadableFontData* data) + : Glyph::Builder(data) { +} + +CALLER_ATTACH FontDataTable* + GlyphTable::SimpleGlyph::SimpleGlyphBuilder::SubBuildTable( + ReadableFontData* data) { + FontDataTablePtr table = new SimpleGlyph(data); + return table.Detach(); +} + +/****************************************************************************** + * GlyphTable::CompositeGlyph + ******************************************************************************/ +GlyphTable::CompositeGlyph::CompositeGlyph(ReadableFontData* data) + : GlyphTable::Glyph(data, GlyphType::kComposite), + instruction_size_(0), + instructions_offset_(0), + initialized_(false) { + Initialize(); +} + +GlyphTable::CompositeGlyph::~CompositeGlyph() { +} + +int32_t GlyphTable::CompositeGlyph::Flags(int32_t contour) { + return data_->ReadUShort(contour_index_[contour]); +} + +int32_t GlyphTable::CompositeGlyph::NumGlyphs() { + return contour_index_.size(); +} + +int32_t GlyphTable::CompositeGlyph::GlyphIndex(int32_t contour) { + return data_->ReadUShort(DataSize::kUSHORT + contour_index_[contour]); +} + +int32_t GlyphTable::CompositeGlyph::Argument1(int32_t contour) { + int32_t index = 2 * DataSize::kUSHORT + contour_index_[contour]; + int32_t contour_flags = Flags(contour); + if ((contour_flags & kFLAG_ARG_1_AND_2_ARE_WORDS) == + kFLAG_ARG_1_AND_2_ARE_WORDS) { + return data_->ReadUShort(index); + } + return data_->ReadByte(index); +} + +int32_t GlyphTable::CompositeGlyph::Argument2(int32_t contour) { + int32_t index = 2 * DataSize::kUSHORT + contour_index_[contour]; + int32_t contour_flags = Flags(contour); + if ((contour_flags & kFLAG_ARG_1_AND_2_ARE_WORDS) == + kFLAG_ARG_1_AND_2_ARE_WORDS) { + return data_->ReadUShort(index + DataSize::kUSHORT); + } + return data_->ReadByte(index + DataSize::kUSHORT); +} + +int32_t GlyphTable::CompositeGlyph::TransformationSize(int32_t contour) { + int32_t contour_flags = Flags(contour); + if ((contour_flags & kFLAG_WE_HAVE_A_SCALE) == kFLAG_WE_HAVE_A_SCALE) { + return DataSize::kF2DOT14; + } else if ((contour_flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) == + kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) { + return 2 * DataSize::kF2DOT14; + } else if ((contour_flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) == + kFLAG_WE_HAVE_A_TWO_BY_TWO) { + return 4 * DataSize::kF2DOT14; + } + return 0; +} + +void GlyphTable::CompositeGlyph::Transformation(int32_t contour, + ByteVector* transformation) { + int32_t contour_flags = Flags(contour); + int32_t index = contour_index_[contour] + 2 * DataSize::kUSHORT; + if ((contour_flags & kFLAG_ARG_1_AND_2_ARE_WORDS) == + kFLAG_ARG_1_AND_2_ARE_WORDS) { + index += 2 * DataSize::kSHORT; + } else { + index += 2 * DataSize::kBYTE; + } + int32_t tsize = TransformationSize(contour); + transformation->resize(tsize); + data_->ReadBytes(index, &((*transformation)[0]), 0, tsize); +} + +int32_t GlyphTable::CompositeGlyph::InstructionSize() { + return instruction_size_; +} + +CALLER_ATTACH ReadableFontData* GlyphTable::CompositeGlyph::Instructions() { + return down_cast( + data_->Slice(instructions_offset_, InstructionSize())); +} + +void GlyphTable::CompositeGlyph::Initialize() { + AutoLock lock(initialization_lock_); + if (initialized_) { + return; + } + + int32_t index = 5 * DataSize::kUSHORT; + int32_t flags = kFLAG_MORE_COMPONENTS; + + while ((flags & kFLAG_MORE_COMPONENTS) == kFLAG_MORE_COMPONENTS) { + contour_index_.push_back(index); + flags = data_->ReadUShort(index); + index += 2 * DataSize::kUSHORT; // flags and glyphIndex + if ((flags & kFLAG_ARG_1_AND_2_ARE_WORDS) == kFLAG_ARG_1_AND_2_ARE_WORDS) { + index += 2 * DataSize::kSHORT; + } else { + index += 2 * DataSize::kBYTE; + } + if ((flags & kFLAG_WE_HAVE_A_SCALE) == kFLAG_WE_HAVE_A_SCALE) { + index += DataSize::kF2DOT14; + } else if ((flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) == + kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) { + index += 2 * DataSize::kF2DOT14; + } else if ((flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) == + kFLAG_WE_HAVE_A_TWO_BY_TWO) { + index += 4 * DataSize::kF2DOT14; + } + int32_t non_padded_data_length = index; + if ((flags & kFLAG_WE_HAVE_INSTRUCTIONS) == kFLAG_WE_HAVE_INSTRUCTIONS) { + instruction_size_ = data_->ReadUShort(index); + index += DataSize::kUSHORT; + instructions_offset_ = index; + non_padded_data_length = index + (instruction_size_ * DataSize::kBYTE); + } + set_padding(DataLength() - non_padded_data_length); + } + + initialized_ = true; +} + +/****************************************************************************** + * GlyphTable::CompositeGlyph::Builder + ******************************************************************************/ +GlyphTable::CompositeGlyph::CompositeGlyphBuilder::~CompositeGlyphBuilder() { +} + +GlyphTable::CompositeGlyph::CompositeGlyphBuilder::CompositeGlyphBuilder( + WritableFontData* data) + : Glyph::Builder(data) { +} + +GlyphTable::CompositeGlyph::CompositeGlyphBuilder::CompositeGlyphBuilder( + ReadableFontData* data) + : Glyph::Builder(data) { +} + +CALLER_ATTACH FontDataTable* + GlyphTable::CompositeGlyph::CompositeGlyphBuilder::SubBuildTable( + ReadableFontData* data) { + FontDataTablePtr table = new CompositeGlyph(data); + return table.Detach(); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/truetype/glyph_table.h b/src/sfntly/src/sfntly/table/truetype/glyph_table.h new file mode 100644 index 0000000000..0836971894 --- /dev/null +++ b/src/sfntly/src/sfntly/table/truetype/glyph_table.h @@ -0,0 +1,335 @@ +/* + * 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_TRUETYPE_GLYPH_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_GLYPH_TABLE_H_ + +#include + +#include "sfntly/table/table.h" +#include "sfntly/table/subtable.h" +#include "sfntly/table/subtable_container_table.h" + +namespace sfntly { + +struct GlyphType { + enum { + kSimple = 0, + kComposite = 1 + }; +}; + +class GlyphTable : public SubTableContainerTable, + public RefCounted { + public: + class Builder; + class Glyph : public SubTable { + public: + // Note: Contour is an empty class for the version ported + class Contour { + protected: + Contour() {} + virtual ~Contour() {} + }; + + class Builder : public SubTable::Builder { + public: + virtual ~Builder(); + + protected: + // Incoming table_builder is GlyphTable::Builder*. + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + explicit Builder(WritableFontData* data); + explicit Builder(ReadableFontData* data); + + static CALLER_ATTACH Builder* + GetBuilder(GlyphTable::Builder* table_builder, + ReadableFontData* data); + static CALLER_ATTACH Builder* + GetBuilder(GlyphTable::Builder* table_builder, + ReadableFontData* data, + int32_t offset, + int32_t length); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + private: + int32_t format_; + friend class GlyphTable::Builder; + }; + + virtual ~Glyph(); + static CALLER_ATTACH Glyph* GetGlyph(GlyphTable* table, + ReadableFontData* data, + int32_t offset, + int32_t length); + + virtual int32_t Padding(); + virtual int32_t GlyphType(); + virtual int32_t NumberOfContours(); + virtual int32_t XMin(); + virtual int32_t XMax(); + virtual int32_t YMin(); + virtual int32_t YMax(); + + virtual int32_t InstructionSize() = 0; + virtual ReadableFontData* Instructions() = 0; + + protected: + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + Glyph(ReadableFontData* data, int32_t glyph_type); + virtual void Initialize() = 0; + // Note: Derived class to define initialization_lock_. + + private: + static int32_t GlyphType(ReadableFontData* data, + int32_t offset, + int32_t length); + + int32_t glyph_type_; + int32_t number_of_contours_; + }; // class GlyphTable::Glyph + typedef Ptr GlyphBuilderPtr; + typedef std::vector GlyphBuilderList; + + class Builder : public SubTableContainerTable::Builder, + public RefCounted { + public: + // Note: Constructor scope altered to public for base class to instantiate. + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + virtual void SetLoca(const IntegerList& loca); + virtual void GenerateLocaList(IntegerList* locas); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + // Gets the List of glyph builders for the glyph table builder. These may be + // manipulated in any way by the caller and the changes will be reflected in + // the final glyph table produced. + // If there is no current data for the glyph builder or the glyph builders + // have not been previously set then this will return an empty glyph builder + // List. If there is current data (i.e. data read from an existing font) and + // the loca list has not been set or is null, empty, or + // invalid, then an empty glyph builder List will be returned. + GlyphBuilderList* GlyphBuilders(); + + // Replace the internal glyph builders with the one provided. The provided + // list and all contained objects belong to this builder. + // This call is only required if the entire set of glyphs in the glyph + // table builder are being replaced. If the glyph builder list provided from + // the GlyphTable.Builder::GlyphBuilders() is being used and modified + // then those changes will already be reflected in the glyph table builder. + void SetGlyphBuilders(GlyphBuilderList* glyph_builders); + + // Glyph builder factories + CALLER_ATTACH Glyph::Builder* GlyphBuilder(ReadableFontData* data); + + protected: // internal API for building + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + private: + void Initialize(ReadableFontData* data, const IntegerList& loca); + GlyphBuilderList* GetGlyphBuilders(); + void Revert(); + + GlyphBuilderList glyph_builders_; + IntegerList loca_; + }; + + class SimpleGlyph : public Glyph, public RefCounted { + public: + static const int32_t kFLAG_ONCURVE; + static const int32_t kFLAG_XSHORT; + static const int32_t kFLAG_YSHORT; + static const int32_t kFLAG_REPEAT; + static const int32_t kFLAG_XREPEATSIGN; + static const int32_t kFLAG_YREPEATSIGN; + + class SimpleContour : public Glyph::Contour { + protected: + SimpleContour() {} + virtual ~SimpleContour() {} + }; + + class SimpleGlyphBuilder : public Glyph::Builder, + public RefCounted { + public: + virtual ~SimpleGlyphBuilder(); + + protected: + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + explicit SimpleGlyphBuilder(WritableFontData* data); + explicit SimpleGlyphBuilder(ReadableFontData* data); + virtual CALLER_ATTACH FontDataTable* + SubBuildTable(ReadableFontData* data); + + private: + friend class Glyph::Builder; + }; + + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + explicit SimpleGlyph(ReadableFontData* data); + virtual ~SimpleGlyph(); + + virtual int32_t InstructionSize(); + virtual CALLER_ATTACH ReadableFontData* Instructions(); + virtual void Initialize(); + + int32_t NumberOfPoints(int32_t contour); + int32_t XCoordinate(int32_t contour, int32_t point); + int32_t YCoordinate(int32_t contour, int32_t point); + bool OnCurve(int32_t contour, int32_t point); + + private: + void ParseData(bool fill_arrays); + int32_t FlagAsInt(int32_t index); + int32_t ContourEndPoint(int32_t contour); + + bool initialized_; + Lock initialization_lock_; + int32_t instruction_size_; + int32_t number_of_points_; + + // start offsets of the arrays + int32_t instructions_offset_; + int32_t flags_offset_; + int32_t x_coordinates_offset_; + int32_t y_coordinates_offset_; + + int32_t flag_byte_count_; + int32_t x_byte_count_; + int32_t y_byte_count_; + + IntegerList x_coordinates_; + IntegerList y_coordinates_; + std::vector on_curve_; + IntegerList contour_index_; + }; + + class CompositeGlyph : public Glyph, public RefCounted { + public: + static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS; + static const int32_t kFLAG_ARGS_ARE_XY_VALUES; + static const int32_t kFLAG_ROUND_XY_TO_GRID; + static const int32_t kFLAG_WE_HAVE_A_SCALE; + static const int32_t kFLAG_RESERVED; + static const int32_t kFLAG_MORE_COMPONENTS; + static const int32_t kFLAG_WE_HAVE_AN_X_AND_Y_SCALE; + static const int32_t kFLAG_WE_HAVE_A_TWO_BY_TWO; + static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS; + static const int32_t kFLAG_USE_MY_METRICS; + static const int32_t kFLAG_OVERLAP_COMPOUND; + static const int32_t kFLAG_SCALED_COMPONENT_OFFSET; + static const int32_t kFLAG_UNSCALED_COMPONENT_OFFSET; + + class CompositeGlyphBuilder : public Glyph::Builder, + public RefCounted { + public: + virtual ~CompositeGlyphBuilder(); + + protected: + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + explicit CompositeGlyphBuilder(WritableFontData* data); + explicit CompositeGlyphBuilder(ReadableFontData* data); + + virtual CALLER_ATTACH FontDataTable* + SubBuildTable(ReadableFontData* data); + + private: + friend class Glyph::Builder; + }; + + // Note: constructor refactored in C++ to avoid heavy lifting. + // caller need to do data->Slice(offset, length) beforehand. + explicit CompositeGlyph(ReadableFontData* data); + virtual ~CompositeGlyph(); + + int32_t Flags(int32_t contour); + int32_t NumGlyphs(); + int32_t GlyphIndex(int32_t contour); + int32_t Argument1(int32_t contour); + int32_t Argument2(int32_t contour); + int32_t TransformationSize(int32_t contour); + void Transformation(int32_t contour, ByteVector* transformation); + virtual int32_t InstructionSize(); + virtual CALLER_ATTACH ReadableFontData* Instructions(); + + protected: + virtual void Initialize(); + + private: + IntegerList contour_index_; + int32_t instruction_size_; + int32_t instructions_offset_; + bool initialized_; + Lock initialization_lock_; + }; + + virtual ~GlyphTable(); + + // C++ port: rename glyph() to GetGlyph(). + Glyph* GetGlyph(int32_t offset, int32_t length); + + private: + struct Offset { + enum { + // header + kNumberOfContours = 0, + kXMin = 2, + kYMin = 4, + kXMax = 6, + kYMax = 8, + + // Simple Glyph Description + kSimpleEndPtsOfCountours = 10, + // offset from the end of the contours array + kSimpleInstructionLength = 0, + kSimpleInstructions = 2, + // flags + // xCoordinates + // yCoordinates + + // Composite Glyph Description + kCompositeFlags = 0, + kCompositeGyphIndexWithoutFlag = 0, + kCompositeGlyphIndexWithFlag = 2, + }; + }; + + GlyphTable(Header* header, ReadableFontData* data); +}; +typedef Ptr GlyphTablePtr; +typedef Ptr GlyphTableBuilderPtr; +typedef std::vector GlyphTableBuilderList; +typedef Ptr GlyphPtr; +typedef Ptr GlyphBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_GLYPH_TABLE_H_ diff --git a/src/sfntly/src/sfntly/table/truetype/loca_table.cc b/src/sfntly/src/sfntly/table/truetype/loca_table.cc new file mode 100644 index 0000000000..c692155da8 --- /dev/null +++ b/src/sfntly/src/sfntly/table/truetype/loca_table.cc @@ -0,0 +1,246 @@ +/* + * 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/truetype/loca_table.h" +#include "sfntly/port/exception_type.h" + +namespace sfntly { +/****************************************************************************** + * LocaTable class + ******************************************************************************/ +LocaTable::~LocaTable() {} + +int32_t LocaTable::GlyphOffset(int32_t glyph_id) { + if (glyph_id < 0 || glyph_id >= num_glyphs_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException("Glyph ID is out of bounds."); +#endif + return 0; + } + return Loca(glyph_id); +} + +int32_t LocaTable::GlyphLength(int32_t glyph_id) { + if (glyph_id < 0 || glyph_id >= num_glyphs_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException("Glyph ID is out of bounds."); +#endif + return 0; + } + return Loca(glyph_id + 1) - Loca(glyph_id); +} + +int32_t LocaTable::NumLocas() { + return num_glyphs_ + 1; +} + +int32_t LocaTable::Loca(int32_t index) { + if (index > num_glyphs_) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundException(); +#endif + return 0; + } + if (format_version_ == IndexToLocFormat::kShortOffset) { + return 2 * data_->ReadUShort(index * DataSize::kUSHORT); + } + return data_->ReadULongAsInt(index * DataSize::kULONG); +} + +LocaTable::LocaTable(Header* header, + ReadableFontData* data, + int32_t format_version, + int32_t num_glyphs) + : Table(header, data), + format_version_(format_version), + num_glyphs_(num_glyphs) { +} + +/****************************************************************************** + * LocaTable::Iterator class + ******************************************************************************/ +LocaTable::LocaIterator::LocaIterator(LocaTable* table) + : PODIterator(table), index_(-1) { +} + +bool LocaTable::LocaIterator::HasNext() { + return index_ <= container()->num_glyphs_; +} + +int32_t LocaTable::LocaIterator::Next() { + return container()->Loca(index_++); +} + +/****************************************************************************** + * LocaTable::Builder class + ******************************************************************************/ +LocaTable::Builder::Builder(Header* header, WritableFontData* data) + : Table::Builder(header, data), + format_version_(IndexToLocFormat::kLongOffset), + num_glyphs_(-1) { +} + +LocaTable::Builder::Builder(Header* header, ReadableFontData* data) + : Table::Builder(header, data), + format_version_(IndexToLocFormat::kLongOffset), + num_glyphs_(-1) { +} + +LocaTable::Builder::~Builder() {} + +CALLER_ATTACH +LocaTable::Builder* LocaTable::Builder::CreateBuilder(Header* header, + WritableFontData* data) { + Ptr builder; + builder = new LocaTable::Builder(header, data); + return builder.Detach(); +} + +IntegerList* LocaTable::Builder::LocaList() { + return GetLocaList(); +} + +void LocaTable::Builder::SetLocaList(IntegerList* list) { + loca_.clear(); + if (list) { + loca_ = *list; + set_model_changed(); + } +} + +int32_t LocaTable::Builder::GlyphOffset(int32_t glyph_id) { + if (CheckGlyphRange(glyph_id) == -1) { + return 0; + } + return GetLocaList()->at(glyph_id); +} + +int32_t LocaTable::Builder::GlyphLength(int32_t glyph_id) { + if (CheckGlyphRange(glyph_id) == -1) { + return 0; + } + return GetLocaList()->at(glyph_id + 1) - GetLocaList()->at(glyph_id); +} + +void LocaTable::Builder::SetNumGlyphs(int32_t num_glyphs) { + num_glyphs_ = num_glyphs; +} + +int32_t LocaTable::Builder::NumGlyphs() { + return LastGlyphIndex() - 1; +} + +void LocaTable::Builder::Revert() { + loca_.clear(); + set_model_changed(false); +} + +int32_t LocaTable::Builder::NumLocas() { + return GetLocaList()->size(); +} + +int32_t LocaTable::Builder::Loca(int32_t index) { + return GetLocaList()->at(index); +} + +CALLER_ATTACH +FontDataTable* LocaTable::Builder::SubBuildTable(ReadableFontData* data) { + FontDataTablePtr table = + new LocaTable(header(), data, format_version_, num_glyphs_); + return table.Detach(); +} + +void LocaTable::Builder::SubDataSet() { + Initialize(InternalReadData()); +} + +int32_t LocaTable::Builder::SubDataSizeToSerialize() { + if (loca_.empty()) { + return 0; + } + if (format_version_ == IndexToLocFormat::kLongOffset) { + return loca_.size() * DataSize::kULONG; + } + return loca_.size() * DataSize::kUSHORT; +} + +bool LocaTable::Builder::SubReadyToSerialize() { + return !loca_.empty(); +} + +int32_t LocaTable::Builder::SubSerialize(WritableFontData* new_data) { + int32_t size = 0; + for (IntegerList::iterator l = loca_.begin(), end = loca_.end(); + l != end; ++l) { + if (format_version_ == IndexToLocFormat::kLongOffset) { + size += new_data->WriteULong(size, *l); + } else { + size += new_data->WriteUShort(size, *l / 2); + } + } + num_glyphs_ = loca_.size() - 1; + return size; +} + +void LocaTable::Builder::Initialize(ReadableFontData* data) { + ClearLoca(false); + if (data) { + if (NumGlyphs() < 0) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IllegalStateException("numglyphs not set on LocaTable Builder."); +#endif + return; + } + LocaTablePtr table = + new LocaTable(header(), data, format_version_, num_glyphs_); + Ptr loca_iter = + new LocaTable::LocaIterator(table); + while (loca_iter->HasNext()) { + loca_.push_back(loca_iter->Next()); + } + } +} + +int32_t LocaTable::Builder::CheckGlyphRange(int32_t glyph_id) { + if (glyph_id < 0 || glyph_id > LastGlyphIndex()) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw IndexOutOfBoundsException("Glyph ID is outside of the allowed range"); +#endif + return -1; + } + return glyph_id; +} + +int32_t LocaTable::Builder::LastGlyphIndex() { + return !loca_.empty() ? loca_.size() - 2 : num_glyphs_ - 1; +} + +IntegerList* LocaTable::Builder::GetLocaList() { + if (loca_.empty()) { + Initialize(InternalReadData()); + set_model_changed(); + } + return &loca_; +} + +void LocaTable::Builder::ClearLoca(bool nullify) { + // Note: in C++ port, nullify is not used at all. + UNREFERENCED_PARAMETER(nullify); + loca_.clear(); + set_model_changed(false); +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/table/truetype/loca_table.h b/src/sfntly/src/sfntly/table/truetype/loca_table.h new file mode 100644 index 0000000000..67c5749b05 --- /dev/null +++ b/src/sfntly/src/sfntly/table/truetype/loca_table.h @@ -0,0 +1,183 @@ +/* + * 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_TRUETYPE_LOCA_TABLE_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_LOCA_TABLE_H_ + +#include "sfntly/port/java_iterator.h" +#include "sfntly/table/table.h" +#include "sfntly/table/core/font_header_table.h" + +namespace sfntly { + +// A Loca table - 'loca'. +class LocaTable : public Table, public RefCounted { + public: + class LocaIterator : public PODIterator { + public: + explicit LocaIterator(LocaTable* table); + virtual ~LocaIterator() {} + + virtual bool HasNext(); + virtual int32_t Next(); + + private: + int32_t index_; + }; + + class Builder : public Table::Builder, public RefCounted { + public: + // Constructor scope altered to public for base class to instantiate. + Builder(Header* header, WritableFontData* data); + Builder(Header* header, ReadableFontData* data); + virtual ~Builder(); + + static CALLER_ATTACH Builder* CreateBuilder(Header* header, + WritableFontData* data); + + // Get the format version that will be used when the loca table is + // generated. + // @return the loca table format version + int32_t format_version() { return format_version_; } + void set_format_version(int32_t value) { format_version_ = value; } + + // Gets the List of locas for loca table builder. These may be manipulated + // in any way by the caller and the changes will be reflected in the final + // loca table produced as long as no subsequent call is made to the + // SetLocaList(List) method. + // If there is no current data for the loca table builder or the loca list + // have not been previously set then this will return an empty List. + IntegerList* LocaList(); + + // Set the list of locas to be used for building this table. If any existing + // list was already retrieved with the LocaList() method then the + // connection of that previous list to this builder will be broken. + void SetLocaList(IntegerList* list); + + // Return the offset for the given glyph id. Valid glyph ids are from 0 to + // one less than the number of glyphs. The zero entry is the special entry + // for the notdef glyph. The final entry beyond the last glyph id is used to + // calculate the size of the last glyph. + // @param glyphId the glyph id to get the offset for; must be less than or + // equal to one more than the number of glyph ids + // @return the offset in the glyph table to the specified glyph id + int32_t GlyphOffset(int32_t glyph_id); + + // Get the length of the data in the glyph table for the specified glyph id. + int32_t GlyphLength(int32_t glyph_id); + + // Set the number of glyphs. + // This method sets the number of glyphs that the builder will attempt to + // parse location data for from the raw binary data. This method only needs + // to be called (and must be) when the raw data for this builder has + // been changed. It does not by itself reset the data or clear any set loca + // list. + void SetNumGlyphs(int32_t num_glyphs); + + // Get the number of glyphs that this builder has support for. + int NumGlyphs(); + + // Revert the loca table builder to the state contained in the last raw data + // set on the builder. That raw data may be that read from a font file when + // the font builder was created, that set by a user of the loca table + // builder, or null data if this builder was created as a new empty builder. + void Revert(); + + // Get the number of locations or locas. This will be one more than the + // number of glyphs for this table since the last loca position is used to + // indicate the size of the final glyph. + int32_t NumLocas(); + + // Get the value from the loca table for the index specified. These are the + // raw values from the table that are used to compute the offset and size of + // a glyph in the glyph table. Valid index values run from 0 to the number + // of glyphs in the font. + int32_t Loca(int32_t index); + + virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); + virtual void SubDataSet(); + virtual int32_t SubDataSizeToSerialize(); + virtual bool SubReadyToSerialize(); + virtual int32_t SubSerialize(WritableFontData* new_data); + + private: + // Initialize the internal state from the data. Done lazily since in many + // cases the builder will be just creating a table object with no parsing + // required. + // @param data the data to initialize from + void Initialize(ReadableFontData* data); + + // Checks that the glyph id is within the correct range. + // @return glyph_id if correct, -1 otherwise. + int32_t CheckGlyphRange(int32_t glyph_id); + + int32_t LastGlyphIndex(); + + // Internal method to get the loca list if already generated and if not to + // initialize the state of the builder. + // @return the loca list + IntegerList* GetLocaList(); + + void ClearLoca(bool nullify); + + int32_t format_version_; // Note: IndexToLocFormat + int32_t num_glyphs_; + IntegerList loca_; + }; + + virtual ~LocaTable(); + + int32_t format_version() { return format_version_; } + int32_t num_glyphs() { return num_glyphs_; } + + // Return the offset for the given glyph id. Valid glyph ids are from 0 to the + // one less than the number of glyphs. The zero entry is the special entry for + // the notdef glyph. The final entry beyond the last glyph id is used to + // calculate the size of the last glyph. + // @param glyphId the glyph id to get the offset for; must be less than or + // equal to one more than the number of glyph ids + // @return the offset in the glyph table to the specified glyph id + int32_t GlyphOffset(int32_t glyph_id); + + // Get the length of the data in the glyph table for the specified glyph id. + int32_t GlyphLength(int32_t glyph_id); + + // Get the number of locations or locas. This will be one more than the number + // of glyphs for this table since the last loca position is used to indicate + // the size of the final glyph. + int32_t NumLocas(); + + // Get the value from the loca table for the index specified. Valid index + // values run from 0 to the number of glyphs in the font. + int32_t Loca(int32_t index); + + private: + LocaTable(Header* header, + ReadableFontData* data, + int32_t format_version, + int32_t num_glyphs); + + int32_t format_version_; // Note: Java's version, renamed to format_version_ + int32_t num_glyphs_; + + friend class LocaIterator; +}; +typedef Ptr LocaTablePtr; +typedef Ptr LocaTableBuilderPtr; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_LOCA_TABLE_H_ diff --git a/src/sfntly/src/sfntly/tag.cc b/src/sfntly/src/sfntly/tag.cc new file mode 100644 index 0000000000..c9d8c29878 --- /dev/null +++ b/src/sfntly/src/sfntly/tag.cc @@ -0,0 +1,110 @@ +/* + * 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/tag.h" +#include "sfntly/port/endian.h" + +// Use a macro instead of GenerateTag() because gcc 4.4.3 creates static +// initializers in that case. +#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d); + +namespace sfntly { + +const int32_t Tag::ttcf = TAG('t', 't', 'c', 'f'); +const int32_t Tag::cmap = TAG('c', 'm', 'a', 'p'); +const int32_t Tag::head = TAG('h', 'e', 'a', 'd'); +const int32_t Tag::hhea = TAG('h', 'h', 'e', 'a'); +const int32_t Tag::hmtx = TAG('h', 'm', 't', 'x'); +const int32_t Tag::maxp = TAG('m', 'a', 'x', 'p'); +const int32_t Tag::name = TAG('n', 'a', 'm', 'e'); +const int32_t Tag::OS_2 = TAG('O', 'S', '/', '2'); +const int32_t Tag::post = TAG('p', 'o', 's', 't'); +const int32_t Tag::cvt = TAG('c', 'v', 't', ' '); +const int32_t Tag::fpgm = TAG('f', 'p', 'g', 'm'); +const int32_t Tag::glyf = TAG('g', 'l', 'y', 'f'); +const int32_t Tag::loca = TAG('l', 'o', 'c', 'a'); +const int32_t Tag::prep = TAG('p', 'r', 'e', 'p'); +const int32_t Tag::CFF = TAG('C', 'F', 'F', ' '); +const int32_t Tag::VORG = TAG('V', 'O', 'R', 'G'); +const int32_t Tag::EBDT = TAG('E', 'B', 'D', 'T'); +const int32_t Tag::EBLC = TAG('E', 'B', 'L', 'C'); +const int32_t Tag::EBSC = TAG('E', 'B', 'S', 'C'); +const int32_t Tag::BASE = TAG('B', 'A', 'S', 'E'); +const int32_t Tag::GDEF = TAG('G', 'D', 'E', 'F'); +const int32_t Tag::GPOS = TAG('G', 'P', 'O', 'S'); +const int32_t Tag::GSUB = TAG('G', 'S', 'U', 'B'); +const int32_t Tag::JSTF = TAG('J', 'S', 'T', 'F'); +const int32_t Tag::DSIG = TAG('D', 'S', 'I', 'G'); +const int32_t Tag::gasp = TAG('g', 'a', 's', 'p'); +const int32_t Tag::hdmx = TAG('h', 'd', 'm', 'x'); +const int32_t Tag::kern = TAG('k', 'e', 'r', 'n'); +const int32_t Tag::LTSH = TAG('L', 'T', 'S', 'H'); +const int32_t Tag::PCLT = TAG('P', 'C', 'L', 'T'); +const int32_t Tag::VDMX = TAG('V', 'D', 'M', 'X'); +const int32_t Tag::vhea = TAG('v', 'h', 'e', 'a'); +const int32_t Tag::vmtx = TAG('v', 'm', 't', 'x'); +const int32_t Tag::bsln = TAG('b', 's', 'l', 'n'); +const int32_t Tag::feat = TAG('f', 'e', 'a', 't'); +const int32_t Tag::lcar = TAG('l', 'c', 'a', 'r'); +const int32_t Tag::morx = TAG('m', 'o', 'r', 'x'); +const int32_t Tag::opbd = TAG('o', 'p', 'b', 'd'); +const int32_t Tag::prop = TAG('p', 'r', 'o', 'p'); +const int32_t Tag::Feat = TAG('F', 'e', 'a', 't'); +const int32_t Tag::Glat = TAG('G', 'l', 'a', 't'); +const int32_t Tag::Gloc = TAG('G', 'l', 'o', 'c'); +const int32_t Tag::Sile = TAG('S', 'i', 'l', 'e'); +const int32_t Tag::Silf = TAG('S', 'i', 'l', 'f'); +const int32_t Tag::bhed = TAG('b', 'h', 'e', 'd'); +const int32_t Tag::bdat = TAG('b', 'd', 'a', 't'); +const int32_t Tag::bloc = TAG('b', 'l', 'o', 'c'); + +const int32_t CFF_TABLE_ORDERING[] = { + Tag::head, + Tag::hhea, + Tag::maxp, + Tag::OS_2, + Tag::name, + Tag::cmap, + Tag::post, + Tag::CFF }; +const size_t CFF_TABLE_ORDERING_SIZE = + sizeof(CFF_TABLE_ORDERING) / sizeof(int32_t); + +const int32_t TRUE_TYPE_TABLE_ORDERING[] = { + Tag::head, + Tag::hhea, + Tag::maxp, + Tag::OS_2, + Tag::hmtx, + Tag::LTSH, + Tag::VDMX, + Tag::hdmx, + Tag::cmap, + Tag::fpgm, + Tag::prep, + Tag::cvt, + Tag::loca, + Tag::glyf, + Tag::kern, + Tag::name, + Tag::post, + Tag::gasp, + Tag::PCLT, + Tag::DSIG }; +const size_t TRUE_TYPE_TABLE_ORDERING_SIZE = + sizeof(TRUE_TYPE_TABLE_ORDERING) / sizeof(int32_t); + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/tag.h b/src/sfntly/src/sfntly/tag.h new file mode 100644 index 0000000000..0ecbab85b4 --- /dev/null +++ b/src/sfntly/src/sfntly/tag.h @@ -0,0 +1,123 @@ +/* + * 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_TAG_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TAG_H_ + +#include + +#include "sfntly/port/type.h" + +namespace sfntly { + +// Font identification tags used for tables, features, etc. +// Tag names are consistent with the OpenType and sfnt specs. +struct Tag { + static const int32_t ttcf; + + // Table Type Tags + // required tables + static const int32_t cmap; + static const int32_t head; + static const int32_t hhea; + static const int32_t hmtx; + static const int32_t maxp; + static const int32_t name; + static const int32_t OS_2; + static const int32_t post; + + // TrueType outline tables + static const int32_t cvt; + static const int32_t fpgm; + static const int32_t glyf; + static const int32_t loca; + static const int32_t prep; + + // PostScript outline tables + static const int32_t CFF; + static const int32_t VORG; + + // opentype bitmap glyph outlines + static const int32_t EBDT; + static const int32_t EBLC; + static const int32_t EBSC; + + // advanced typographic features + static const int32_t BASE; + static const int32_t GDEF; + static const int32_t GPOS; + static const int32_t GSUB; + static const int32_t JSTF; + + // other + static const int32_t DSIG; + static const int32_t gasp; + static const int32_t hdmx; + static const int32_t kern; + static const int32_t LTSH; + static const int32_t PCLT; + static const int32_t VDMX; + static const int32_t vhea; + static const int32_t vmtx; + + // AAT tables + static const int32_t bsln; + static const int32_t feat; + static const int32_t lcar; + static const int32_t morx; + static const int32_t opbd; + static const int32_t prop; + + // Graphite tables + static const int32_t Feat; + static const int32_t Glat; + static const int32_t Gloc; + static const int32_t Sile; + static const int32_t Silf; + + // truetype bitmap font tables + static const int32_t bhed; + static const int32_t bdat; + static const int32_t bloc; +}; + +// Create integer tag value for human readable tag name. +inline int32_t GenerateTag(int32_t a, int32_t b, int32_t c, int32_t d) { + return (a << 24) | (b << 16) | (c << 8) | d; +} + +// Translate tag to human readable string. +// The Caller must delete[] the returned value. +inline char* TagToString(int32_t tag) { + char *name = new char[5]; + name[0] = static_cast((tag & 0xff000000) >> 24); + name[1] = static_cast((tag & 0x00ff0000) >> 16); + name[2] = static_cast((tag & 0x0000ff00) >> 8); + name[3] = static_cast(tag & 0x000000ff); + name[4] = 0; + return name; +} + +// Note: For Java, these two orderings are in Font class. Moved here to avoid +// VC++ bug of not populating correct values. +extern const int32_t CFF_TABLE_ORDERING[]; +extern const size_t CFF_TABLE_ORDERING_SIZE; +extern const int32_t TRUE_TYPE_TABLE_ORDERING[]; +extern const size_t TRUE_TYPE_TABLE_ORDERING_SIZE; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TAG_H_ diff --git a/src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.cc b/src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.cc new file mode 100644 index 0000000000..b3d6b07e44 --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.cc @@ -0,0 +1,90 @@ +/* + * 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/tools/subsetter/glyph_table_subsetter.h" + +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/truetype/loca_table.h" +#include "sfntly/tag.h" +#include "sfntly/tools/subsetter/subsetter.h" +#include "sfntly/port/exception_type.h" + +namespace sfntly { + +const int32_t kGlyphTableSubsetterTags[2] = {Tag::glyf, Tag::loca}; + +GlyphTableSubsetter::GlyphTableSubsetter() + : TableSubsetterImpl(kGlyphTableSubsetterTags, 2) { +} + +GlyphTableSubsetter::~GlyphTableSubsetter() {} + +bool GlyphTableSubsetter::Subset(Subsetter* subsetter, + Font* font, + Font::Builder* font_builder) { + assert(font); + assert(subsetter); + assert(font_builder); + + IntegerList* permutation_table = subsetter->GlyphPermutationTable(); + if (!permutation_table || permutation_table->empty()) + return false; + + GlyphTablePtr glyph_table = down_cast(font->GetTable(Tag::glyf)); + LocaTablePtr loca_table = down_cast(font->GetTable(Tag::loca)); + if (glyph_table == NULL || loca_table == NULL) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw RuntimeException("Font to subset is not valid."); +#endif + return false; + } + + GlyphTableBuilderPtr glyph_table_builder = + down_cast + (font_builder->NewTableBuilder(Tag::glyf)); + LocaTableBuilderPtr loca_table_builder = + down_cast + (font_builder->NewTableBuilder(Tag::loca)); + if (glyph_table_builder == NULL || loca_table_builder == NULL) { +#if !defined (SFNTLY_NO_EXCEPTION) + throw RuntimeException("Builder for subset is not valid."); +#endif + return false; + } + GlyphTable::GlyphBuilderList* glyph_builders = + glyph_table_builder->GlyphBuilders(); + for (IntegerList::iterator old_glyph_id = permutation_table->begin(), + old_glyph_id_end = permutation_table->end(); + old_glyph_id != old_glyph_id_end; ++old_glyph_id) { + int old_offset = loca_table->GlyphOffset(*old_glyph_id); + int old_length = loca_table->GlyphLength(*old_glyph_id); + GlyphPtr glyph; + glyph.Attach(glyph_table->GetGlyph(old_offset, old_length)); + ReadableFontDataPtr data = glyph->ReadFontData(); + WritableFontDataPtr 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); + } + IntegerList loca_list; + glyph_table_builder->GenerateLocaList(&loca_list); + loca_table_builder->SetLocaList(&loca_list); + return true; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.h b/src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.h new file mode 100644 index 0000000000..88c704443f --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/glyph_table_subsetter.h @@ -0,0 +1,37 @@ +/* + * 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_TOOLS_SUBSETTER_GLYPH_TABLE_SUBSETTER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_GLYPH_TABLE_SUBSETTER_H_ + +#include "sfntly/tools/subsetter/table_subsetter_impl.h" + +namespace sfntly { + +class GlyphTableSubsetter : public TableSubsetterImpl, + public RefCounted { + public: + GlyphTableSubsetter(); + virtual ~GlyphTableSubsetter(); + + virtual bool Subset(Subsetter* subsetter, + Font* font, + Font::Builder* font_builder); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_GLYPH_TABLE_SUBSETTER_H_ diff --git a/src/sfntly/src/sfntly/tools/subsetter/subsetter.cc b/src/sfntly/src/sfntly/tools/subsetter/subsetter.cc new file mode 100644 index 0000000000..7d987796b9 --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/subsetter.cc @@ -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. + */ + +#include "sfntly/tools/subsetter/subsetter.h" + +#include +#include + +#include "sfntly/tools/subsetter/glyph_table_subsetter.h" + +namespace sfntly { + +Subsetter::Subsetter(Font* font, FontFactory* font_factory) { + font_ = font; + font_factory_ = font_factory; + TableSubsetterPtr subsetter = new GlyphTableSubsetter(); + // TODO(arthurhsu): IMPLEMENT: CMap table subsetter + table_subsetters_.push_back(subsetter); +} + +Subsetter::~Subsetter() { + font_factory_.Release(); + font_.Release(); + table_subsetters_.clear(); +} + +void Subsetter::SetGlyphs(IntegerList* glyphs) { + new_to_old_glyphs_ = *glyphs; +} + +void Subsetter::SetCMaps(CMapIdList* cmap_ids, int32_t number) { + UNREFERENCED_PARAMETER(cmap_ids); + UNREFERENCED_PARAMETER(number); + // TODO(arthurhsu): IMPLEMENT +} + +void Subsetter::SetRemoveTables(IntegerSet* remove_tables) { + remove_tables_ = *remove_tables; +} + +CALLER_ATTACH Font::Builder* Subsetter::Subset() { + FontBuilderPtr font_builder; + font_builder.Attach(font_factory_->NewFontBuilder()); + + IntegerSet table_tags; + for (TableMap::const_iterator i = font_->GetTableMap()->begin(), + e = font_->GetTableMap()->end(); i != e; ++i) { + table_tags.insert(i->first); + } + if (!remove_tables_.empty()) { + IntegerSet result; + std::set_difference(table_tags.begin(), table_tags.end(), + remove_tables_.begin(), remove_tables_.end(), + std::inserter(result, result.end())); + table_tags = result; + } + for (TableSubsetterList::iterator + table_subsetter = table_subsetters_.begin(), + table_subsetter_end = table_subsetters_.end(); + table_subsetter != table_subsetter_end; ++table_subsetter) { + bool handled = (*table_subsetter)->Subset(this, font_, font_builder); + if (handled) { + IntegerSet* handled_tags = (*table_subsetter)->TagsHandled(); + IntegerSet result; + std::set_difference(table_tags.begin(), table_tags.end(), + handled_tags->begin(), handled_tags->end(), + std::inserter(result, result.end())); + table_tags = result; + } + } + for (IntegerSet::iterator tag = table_tags.begin(), + tag_end = table_tags.end(); tag != tag_end; ++tag) { + Table* table = font_->GetTable(*tag); + if (table) { + font_builder->NewTableBuilder(*tag, table->ReadFontData()); + } + } + return font_builder.Detach(); +} + +IntegerList* Subsetter::GlyphPermutationTable() { + return &new_to_old_glyphs_; +} + +CMapIdList* Subsetter::CMapId() { + return &cmap_ids_; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/tools/subsetter/subsetter.h b/src/sfntly/src/sfntly/tools/subsetter/subsetter.h new file mode 100644 index 0000000000..85940a7928 --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/subsetter.h @@ -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. + */ + +#ifndef SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_SUBSETTER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_SUBSETTER_H_ + +#include + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/tools/subsetter/table_subsetter.h" + +namespace sfntly { + +class Subsetter : public RefCounted { + public: + Subsetter(Font* font, FontFactory* font_factory); + virtual ~Subsetter(); + + virtual void SetGlyphs(IntegerList* glyphs); + + // Set the cmaps to be used in the subsetted font. The cmaps are listed in + // order of priority and the number parameter gives a count of how many of the + // list should be put into the subsetted font. If there are no matches in the + // font for any of the provided cmap ids which would lead to a font with no + // cmap then an error will be thrown during subsetting. + // The two most common cases would be: + // * a list of one or more cmap ids with a count setting of 1 + // This will use the list of cmap ids as an ordered priority and look for + // an available cmap in the font that matches the requests. Only the first + // such match will be placed in the subsetted font. + // * a list of one or more cmap ids with a count setting equal to the list + // length + // This will use the list of cmap ids and try to place each one specified + // into the subsetted font. + // @param cmapIds the cmap ids to use for the subsetted font + // @param number the maximum number of cmaps to place in the subsetted font + virtual void SetCMaps(CMapIdList* cmap_ids, int32_t number); + + virtual void SetRemoveTables(IntegerSet* remove_tables); + virtual CALLER_ATTACH Font::Builder* Subset(); + virtual IntegerList* GlyphPermutationTable(); + virtual CMapIdList* CMapId(); + + private: + FontPtr font_; + FontFactoryPtr font_factory_; + TableSubsetterList table_subsetters_; + + // Settings from user + IntegerSet remove_tables_; + IntegerList new_to_old_glyphs_; + CMapIdList cmap_ids_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_SUBSETTER_H_ diff --git a/src/sfntly/src/sfntly/tools/subsetter/table_subsetter.h b/src/sfntly/src/sfntly/tools/subsetter/table_subsetter.h new file mode 100644 index 0000000000..1336615b07 --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/table_subsetter.h @@ -0,0 +1,39 @@ +/* + * 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_TOOLS_SUBSETTER_TABLE_SUBSETTER_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_H_ + +#include + +#include "sfntly/font.h" + +namespace sfntly { + +class Subsetter; +class TableSubsetter : virtual public RefCount { + public: + virtual IntegerSet* TagsHandled() = 0; + virtual bool TagHandled(int32_t tag) = 0; + virtual bool Subset(Subsetter* subsetter, Font* font, + Font::Builder* font_builder) = 0; +}; +typedef Ptr TableSubsetterPtr; +typedef std::vector TableSubsetterList; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_H_ diff --git a/src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.cc b/src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.cc new file mode 100644 index 0000000000..f239c78e3c --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.cc @@ -0,0 +1,38 @@ +/* + * 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/tools/subsetter/table_subsetter_impl.h" + +namespace sfntly { + +TableSubsetterImpl::TableSubsetterImpl(const int32_t* tags, + size_t tags_length) { + for (size_t i = 0; i < tags_length; ++i) { + tags_.insert(tags[i]); + } +} + +TableSubsetterImpl::~TableSubsetterImpl() {} + +bool TableSubsetterImpl::TagHandled(int32_t tag) { + return tags_.find(tag) != tags_.end(); +} + +IntegerSet* TableSubsetterImpl::TagsHandled() { + return &tags_; +} + +} // namespace sfntly diff --git a/src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.h b/src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.h new file mode 100644 index 0000000000..de0d9a98ca --- /dev/null +++ b/src/sfntly/src/sfntly/tools/subsetter/table_subsetter_impl.h @@ -0,0 +1,37 @@ +/* + * 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_TOOLS_SUBSETTER_TABLE_SUBSETTER_IMPL_H_ +#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_IMPL_H_ + +#include "sfntly/tools/subsetter/table_subsetter.h" + +namespace sfntly { + +class TableSubsetterImpl : public TableSubsetter { + public: + TableSubsetterImpl(const int32_t* tags, size_t tags_length); + virtual ~TableSubsetterImpl(); + virtual bool TagHandled(int32_t tag); + virtual IntegerSet* TagsHandled(); + + protected: + IntegerSet tags_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_IMPL_H_ From 929ad31af970b7f6b39f3c18d4b0883bfd2f814f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 11:09:42 +0530 Subject: [PATCH 19/40] ... --- COPYRIGHT | 12 ++++++++++++ session.vim | 1 + 2 files changed, 13 insertions(+) diff --git a/COPYRIGHT b/COPYRIGHT index f6eeffc5cd..1a2c305ad2 100644 --- a/COPYRIGHT +++ b/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 diff --git a/session.vim b/session.vim index 079975f8d4..71c4313365 100644 --- a/session.vim +++ b/session.vim @@ -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 From d51de87c617ab8666e7b3a070ae88f6c1b7beb34 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 11:33:46 +0530 Subject: [PATCH 20/40] Fixes to sfntly to get it to compile correctly with -fPIC and g++ (remove inline method definitions) --- .../src/sfntly/table/truetype/loca_table.cc | 18 ++++++++++++++++++ .../src/sfntly/table/truetype/loca_table.h | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/sfntly/src/sfntly/table/truetype/loca_table.cc b/src/sfntly/src/sfntly/table/truetype/loca_table.cc index c692155da8..793f9a90ff 100644 --- a/src/sfntly/src/sfntly/table/truetype/loca_table.cc +++ b/src/sfntly/src/sfntly/table/truetype/loca_table.cc @@ -47,6 +47,17 @@ int32_t LocaTable::NumLocas() { return num_glyphs_ + 1; } +// Changed by Kovid: The following two methods must not have inline +// definitions, otherwise they give incorrect results when compiled with gcc +// and -fPIC, leading to corrupted font generation. +int32_t LocaTable::num_glyphs() { + return num_glyphs_; +} + +int32_t LocaTable::format_version() { + return format_version_; +} + int32_t LocaTable::Loca(int32_t index) { if (index > num_glyphs_) { #if !defined (SFNTLY_NO_EXCEPTION) @@ -101,6 +112,13 @@ LocaTable::Builder::Builder(Header* header, ReadableFontData* data) LocaTable::Builder::~Builder() {} +// Changed by Kovid: The following two methods must not have inline +// definitions, otherwise they give incorrect results when compiled with gcc +// and -fPIC, leading to corrupted font generation. +int32_t LocaTable::Builder::format_version() { return format_version_; } + +void LocaTable::Builder::set_format_version(int32_t value) { format_version_ = value; } + CALLER_ATTACH LocaTable::Builder* LocaTable::Builder::CreateBuilder(Header* header, WritableFontData* data) { diff --git a/src/sfntly/src/sfntly/table/truetype/loca_table.h b/src/sfntly/src/sfntly/table/truetype/loca_table.h index 67c5749b05..b4e1d3ceab 100644 --- a/src/sfntly/src/sfntly/table/truetype/loca_table.h +++ b/src/sfntly/src/sfntly/table/truetype/loca_table.h @@ -51,8 +51,8 @@ class LocaTable : public Table, public RefCounted { // Get the format version that will be used when the loca table is // generated. // @return the loca table format version - int32_t format_version() { return format_version_; } - void set_format_version(int32_t value) { format_version_ = value; } + int32_t format_version(); + void set_format_version(int32_t value); // Gets the List of locas for loca table builder. These may be manipulated // in any way by the caller and the changes will be reflected in the final @@ -140,8 +140,8 @@ class LocaTable : public Table, public RefCounted { virtual ~LocaTable(); - int32_t format_version() { return format_version_; } - int32_t num_glyphs() { return num_glyphs_; } + int32_t format_version(); + int32_t num_glyphs(); // Return the offset for the given glyph id. Valid glyph ids are from 0 to the // one less than the number of glyphs. The zero entry is the special entry for From d4854e20ecf3bfe967a36261a993c516215fe9f4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 15:52:32 +0530 Subject: [PATCH 21/40] sfntly working on linux 64bit --- setup/extensions.py | 26 +- setup/sfntly.py | 84 ++++ src/calibre/constants.py | 1 + src/calibre/debug.py | 7 + src/calibre/utils/fonts/sfntly.cpp | 606 +++++++++++++++++++++++++++++ src/calibre/utils/fonts/sfntly.h | 196 ++++++++++ src/calibre/utils/fonts/subset.py | 136 +++++++ 7 files changed, 1054 insertions(+), 2 deletions(-) create mode 100644 setup/sfntly.py create mode 100644 src/calibre/utils/fonts/sfntly.cpp create mode 100644 src/calibre/utils/fonts/sfntly.h create mode 100644 src/calibre/utils/fonts/subset.py diff --git a/setup/extensions.py b/setup/extensions.py index cfbb148873..9a8a6e20a6 100644 --- a/setup/extensions.py +++ b/setup/extensions.py @@ -19,6 +19,7 @@ from setup.build_environment import (chmlib_inc_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'], ), @@ -363,8 +384,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) @@ -385,7 +407,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: diff --git a/setup/sfntly.py b/setup/sfntly.py new file mode 100644 index 0000000000..60d7808d5f --- /dev/null +++ b/setup/sfntly.py @@ -0,0 +1,84 @@ +#!/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 ' +__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('/Zi /nologo /W4 /WX /O2 /Ob2 /Oy /GF /Gm- /MT /GS /Gy ' + '/fp:precise /Zc:wchar_t /Zc:forScope /GR-') + else: + 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') + + 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(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 + + diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 3c89db2d1a..953749c92b 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -90,6 +90,7 @@ class Plugins(collections.Mapping): 'speedup', 'freetype', 'woff', + 'sfntly', ] if iswindows: plugins.extend(['winutil', 'wpd', 'winfonts']) diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 22871cab9e..f7fd6f2d72 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -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 diff --git a/src/calibre/utils/fonts/sfntly.cpp b/src/calibre/utils/fonts/sfntly.cpp new file mode 100644 index 0000000000..f156611909 --- /dev/null +++ b/src/calibre/utils/fonts/sfntly.cpp @@ -0,0 +1,606 @@ +/* + * sfntly.cpp + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#define _UNICODE +#define UNICODE +#define PY_SSIZE_T_CLEAN +#include +#include "sfntly.h" + +#include + +#include +#include + +static PyObject *Error = NULL; +static PyObject *NoGlyphs = 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() { + CharacterMap* chars_to_glyph_ids = new CharacterMap; + bool success = GetCharacterMap(chars_to_glyph_ids); + if (!success) { + delete chars_to_glyph_ids; + 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; + PyErr_SetString(Error, "Error resolving composite glyphs.\n"); + return NULL; + } + Ptr 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. + 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; + 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 composite_glyph = + down_cast(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 cmap_table = down_cast(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(font_->GetTable(Tag::loca)); + glyph_table_ = down_cast(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 cmap_table_builder = + down_cast + (font_builder_->NewTableBuilder(Tag::cmap)); + if (!cmap_table_builder) + return false; + Ptr cmap_builder = + down_cast + (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 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 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 loca_table_builder = + down_cast + (font_builder_->NewTableBuilder(Tag::loca)); + Ptr glyph_table_builder = + down_cast + (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 loca_table = + down_cast(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 first_loca_table = + down_cast + (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 loca_table = + down_cast + (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 glyph_table = + down_cast + (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 data = glyph->ReadFontData(); + Ptr 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 info_builder = + new FontSourcedInfoBuilder(font_, 0, predicate_); + + Ptr font_info; + font_info.Attach(info_builder->GetFontInfo()); + if (!font_info) { + PyErr_SetString(Error, "Could not create font info"); + return NULL; + } + + IntegerSet* table_blacklist = new IntegerSet; + table_blacklist->insert(Tag::DSIG); + Ptr font_assembler = new FontAssembler(font_info, + table_blacklist); + Ptr 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 &predicate) { + FontPtr font; + Ptr font_factory; + FontArray fonts; + MemoryInputStream stream; + PyObject *stats, *stats2; + + if (!stream.Attach(reinterpret_cast(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].Detach(); + if (font->num_tables() == 0) { + PyErr_SetString(Error, "Loaded font has 0 tables."); + return NULL; + } + Ptr subsetter = new PredicateSubsetter(font, predicate); + Ptr new_font; + new_font.Attach(subsetter->Subset()); + if (!new_font) return NULL; + + Ptr 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 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); +} + + + diff --git a/src/calibre/utils/fonts/sfntly.h b/src/calibre/utils/fonts/sfntly.h new file mode 100644 index 0000000000..8b015d3dd3 --- /dev/null +++ b/src/calibre/utils/fonts/sfntly.h @@ -0,0 +1,196 @@ +/* + * sfntly.h + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ +#pragma once + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace sfntly; + +typedef int32_t FontId; +typedef std::map > FontIdMap; + +class CharacterPredicate : virtual public RefCount { + public: + CharacterPredicate() {} + virtual ~CharacterPredicate() {} + virtual bool operator()(int32_t character) const = 0; +}; + +class CompositePredicate : public CharacterPredicate, + public RefCounted { + 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 CharacterMap; +typedef std::set 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 { + 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 { + 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_; + FontId font_id_; + CharacterPredicate* predicate_; + + Ptr cmap_; + Ptr loca_table_; + Ptr glyph_table_; + }; + + +// Assembles FontInfo into font builders. +// Does not take ownership of data passed to it. +class FontAssembler : public RefCounted { + 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 font_info_; + Ptr font_factory_; + Ptr font_builder_; + IntegerSet* table_blacklist_; +}; + +class PredicateSubsetter : public RefCounted { + public: + PredicateSubsetter(Font* font, CharacterPredicate* predicate); + virtual ~PredicateSubsetter(); + + // Performs subsetting returning the subsetted font. + virtual CALLER_ATTACH Font* Subset(); + + private: + Ptr font_; + Ptr predicate_; +}; diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py new file mode 100644 index 0000000000..51b64af6b2 --- /dev/null +++ b/src/calibre/utils/fonts/subset.py @@ -0,0 +1,136 @@ +#!/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 ' +__docformat__ = 'restructuredtext en' + +from future_builtins import map + +class NoGlyphs(ValueError): + 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): + 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') + +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 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) + + From f8d9dead205dc32ab5c8d25ba9eec5928bf2356e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 16:09:45 +0530 Subject: [PATCH 22/40] Fix mem leak in sfntly --- src/calibre/utils/fonts/sfntly.cpp | 2 +- src/calibre/utils/fonts/subset.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/calibre/utils/fonts/sfntly.cpp b/src/calibre/utils/fonts/sfntly.cpp index f156611909..8f74bdddfa 100644 --- a/src/calibre/utils/fonts/sfntly.cpp +++ b/src/calibre/utils/fonts/sfntly.cpp @@ -497,7 +497,7 @@ do_subset(const char *data, Py_ssize_t sz, Ptr &predicate) { return NULL; } - font = fonts[0].Detach(); + font = fonts[0]; if (font->num_tables() == 0) { PyErr_SetString(Error, "Loaded font has 0 tables."); return NULL; diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py index 51b64af6b2..cf4d66193a 100644 --- a/src/calibre/utils/fonts/subset.py +++ b/src/calibre/utils/fonts/subset.py @@ -76,6 +76,21 @@ def print_stats(old_stats, new_stats): '%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 main(args): import sys, time from calibre import prints From c3c64c452a9f58d97059baa8f68e95f94f3693af Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 16:29:29 +0530 Subject: [PATCH 23/40] Do not pass obviously invalid data to sfntly (invalid data tends to cause sfntly to segfault) --- src/calibre/utils/fonts/subset.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py index cf4d66193a..30d0109b30 100644 --- a/src/calibre/utils/fonts/subset.py +++ b/src/calibre/utils/fonts/subset.py @@ -20,6 +20,9 @@ def load_sfntly(): 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: From 8a08e13f7367b9e3fbe5962630ba93513bced255 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 16:30:02 +0530 Subject: [PATCH 24/40] Another fix for compiling with -fPIC --- src/sfntly/src/sfntly/font.cc | 11 +++++++++++ src/sfntly/src/sfntly/font.h | 8 ++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/sfntly/src/sfntly/font.cc b/src/sfntly/src/sfntly/font.cc index 347e0c13e9..f326bccea2 100644 --- a/src/sfntly/src/sfntly/font.cc +++ b/src/sfntly/src/sfntly/font.cc @@ -54,6 +54,17 @@ bool Font::HasTable(int32_t tag) { 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; diff --git a/src/sfntly/src/sfntly/font.h b/src/sfntly/src/sfntly/font.h index 975e8cc52c..ef8b97f854 100644 --- a/src/sfntly/src/sfntly/font.h +++ b/src/sfntly/src/sfntly/font.h @@ -232,17 +232,17 @@ class Font : public RefCounted { virtual ~Font(); // Gets the sfnt version set in the sfnt wrapper of the font. - int32_t sfnt_version() { return sfnt_version_; } + 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() { return &digest_; } + ByteVector* digest(); // Get the checksum for this font. - int64_t checksum() { return checksum_; } + int64_t checksum(); // Get the number of tables in this font. - int32_t num_tables() { return (int32_t)tables_.size(); } + int32_t num_tables(); // Whether the font has a particular table. bool HasTable(int32_t tag); From 8cf35971ae333566c68d89c8f8b1a2dfba1f18a4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 17:30:03 +0530 Subject: [PATCH 25/40] Add build test for sfntly and remove unneccessary compiler flags on windows --- setup/sfntly.py | 9 +++++++-- src/calibre/test_build.py | 6 ++++++ src/calibre/utils/fonts/subset.py | 5 +++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/setup/sfntly.py b/setup/sfntly.py index 60d7808d5f..79a25a53f0 100644 --- a/setup/sfntly.py +++ b/setup/sfntly.py @@ -45,9 +45,9 @@ class SfntlyBuilderMixin(object): if iswindows: self.sfntly_cflags += [ '-D_UNICODE', '-DUNICODE', - ] + shlex.split('/Zi /nologo /W4 /WX /O2 /Ob2 /Oy /GF /Gm- /MT /GS /Gy ' - '/fp:precise /Zc:wchar_t /Zc:forScope /GR-') + ] + shlex.split('/W4 /WX /Gm- /Gy /GR-') else: + # Possibly add -fno-inline (slower, but more robust) self.sfntly_cflags += [ '-Werror', '-fno-exceptions', @@ -59,6 +59,11 @@ class SfntlyBuilderMixin(object): 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') + cflags.insert(0, '/O2' if iswindows else '-O2') groups = [] all_headers = set() diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index d6b3c9a400..2b00d63163 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -37,6 +37,11 @@ def test_freetype(): test() print ('FreeType OK!') +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 matches = win_pnp_drives.scanner() @@ -115,6 +120,7 @@ def test(): test_plugins() test_lxml() test_freetype() + test_sfntly() test_sqlite() test_qt() test_imaging() diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py index 30d0109b30..41f0365cdf 100644 --- a/src/calibre/utils/fonts/subset.py +++ b/src/calibre/utils/fonts/subset.py @@ -93,6 +93,11 @@ def test_mem(): 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 main(args): import sys, time From 18805c4c689dc9f09d0028ac46669a4674752f7b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 22:41:15 +0530 Subject: [PATCH 26/40] Dont scan font files in worker processes --- src/calibre/constants.py | 1 + src/calibre/utils/fonts/scanner.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 953749c92b..4ae0005b0d 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -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() diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py index e90448726d..f3bfd027cf 100644 --- a/src/calibre/utils/fonts/scanner.py +++ b/src/calibre/utils/fonts/scanner.py @@ -12,7 +12,8 @@ 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 +from calibre.constants import (config_dir, iswindows, isosx, plugins, DEBUG, + isworker) from calibre.utils.fonts.metadata import FontMetadata, UnsupportedFont from calibre.utils.fonts.utils import panose_to_css_generic_family from calibre.utils.icu import sort_key @@ -150,7 +151,8 @@ class Scanner(Thread): if not hasattr(self, 'cache'): from calibre.utils.config import JSONConfig self.cache = JSONConfig('fonts/scanner_cache') - self.cache.refresh() + else: + self.cache.refresh() if self.cache.get('version', None) != self.CACHE_VERSION: self.cache.clear() self.cached_fonts = self.cache.get('fonts', {}) @@ -162,6 +164,10 @@ class Scanner(Thread): 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: From 062dbe2bbee71dbedd441b83ed9151368bcfb4dc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 23:40:26 +0530 Subject: [PATCH 27/40] sfntly: Do not segfault for fonts that dont have loca tables. Also nicer error messages for such fonts. --- src/calibre/utils/fonts/sfntly.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/calibre/utils/fonts/sfntly.cpp b/src/calibre/utils/fonts/sfntly.cpp index 8f74bdddfa..adbdbd779a 100644 --- a/src/calibre/utils/fonts/sfntly.cpp +++ b/src/calibre/utils/fonts/sfntly.cpp @@ -18,6 +18,7 @@ static PyObject *Error = NULL; static PyObject *NoGlyphs = NULL; +static PyObject *UnsupportedFont = NULL; // Predicates {{{ CompositePredicate::CompositePredicate(IntegerSet &chars, IntegerList &ranges) : @@ -112,11 +113,15 @@ FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontSourcedInfoBuilder::~FontSourcedInfoBuilder() { } CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() { + if (!cmap_) { + PyErr_SetString(Error, "This font has no cmap table!"); + return NULL; + } CharacterMap* chars_to_glyph_ids = new CharacterMap; bool success = GetCharacterMap(chars_to_glyph_ids); if (!success) { delete chars_to_glyph_ids; - PyErr_SetString(Error, "Error creating character map.\n"); + if (!PyErr_Occurred()) PyErr_SetString(Error, "Error creating character map.\n"); return NULL; } GlyphIdSet* resolved_glyph_ids = new GlyphIdSet; @@ -124,7 +129,7 @@ CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() { if (!success) { delete chars_to_glyph_ids; delete resolved_glyph_ids; - PyErr_SetString(Error, "Error resolving composite glyphs.\n"); + if (!PyErr_Occurred()) PyErr_SetString(Error, "Error resolving composite glyphs.\n"); return NULL; } Ptr font_info = new FontInfo; @@ -178,6 +183,10 @@ bool FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph // 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()) { @@ -189,6 +198,10 @@ bool FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph } 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; @@ -449,7 +462,7 @@ CALLER_ATTACH Font* PredicateSubsetter::Subset() { Ptr font_info; font_info.Attach(info_builder->GetFontInfo()); if (!font_info) { - PyErr_SetString(Error, "Could not create font info"); + if (!PyErr_Occurred()) PyErr_SetString(Error, "Could not create font info"); return NULL; } @@ -600,6 +613,10 @@ initsfntly(void) { 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); } From 5efd38eb4b4d8bb5c84eef7437020d91399d5b4a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 23:41:05 +0530 Subject: [PATCH 28/40] sfntly builds on windows --- setup/extensions.py | 1 + setup/sfntly.py | 8 +++++-- src/calibre/utils/fonts/subset.py | 35 ++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/setup/extensions.py b/setup/extensions.py index 9a8a6e20a6..ebc258d7bd 100644 --- a/setup/extensions.py +++ b/setup/extensions.py @@ -257,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') diff --git a/setup/sfntly.py b/setup/sfntly.py index 79a25a53f0..298d0044f0 100644 --- a/setup/sfntly.py +++ b/setup/sfntly.py @@ -46,6 +46,7 @@ class SfntlyBuilderMixin(object): 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 += [ @@ -63,7 +64,10 @@ class SfntlyBuilderMixin(object): cflags.remove('/Ox') if '-O3' in cflags: cflags.remove('-O3') - cflags.insert(0, '/O2' if iswindows else '-O2') + if '/W3' in cflags: + cflags.remove('/W3') + if '-ggdb' not in cflags: + cflags.insert(0, '/O2' if iswindows else '-O2') groups = [] all_headers = set() @@ -71,7 +75,7 @@ class SfntlyBuilderMixin(object): 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(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('/')) diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py index 41f0365cdf..4fbadee955 100644 --- a/src/calibre/utils/fonts/subset.py +++ b/src/calibre/utils/fonts/subset.py @@ -10,6 +10,12 @@ __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(): @@ -19,7 +25,7 @@ def load_sfntly(): raise RuntimeError('Failed to load sfntly: %s'%err) return sfntly -def subset(font_data, individual_chars, ranges): +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]) @@ -37,6 +43,8 @@ def subset(font_data, individual_chars, ranges): 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 @@ -99,6 +107,31 @@ def test(): if len(sf) > 0.3 * len(raw): raise Exception('Subsetting failed') +def all(): + from calibre.utils.fonts.scanner import font_scanner + failed = [] + 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']) + try: + sf, old_stats, new_stats = subset(raw, set(('a', 'b', 'c')), ()) + except (NoGlyphs, UnsupportedFont) as e: + 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 failed: + print ('\n\nFailures:') + for name, path, err in failed: + print (name, path, err) + print() + + def main(args): import sys, time from calibre import prints From 3fd03f45d723473d9f9ddd57cea569ef7790a435 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 30 Oct 2012 23:50:16 +0530 Subject: [PATCH 29/40] Correct error message for fonts with no format 4 cmap tables --- src/calibre/utils/fonts/sfntly.cpp | 7 ++++++- src/calibre/utils/fonts/subset.py | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/calibre/utils/fonts/sfntly.cpp b/src/calibre/utils/fonts/sfntly.cpp index adbdbd779a..d8a3b37a60 100644 --- a/src/calibre/utils/fonts/sfntly.cpp +++ b/src/calibre/utils/fonts/sfntly.cpp @@ -114,7 +114,7 @@ FontSourcedInfoBuilder::~FontSourcedInfoBuilder() { } CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() { if (!cmap_) { - PyErr_SetString(Error, "This font has no cmap table!"); + 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; @@ -515,6 +515,11 @@ do_subset(const char *data, Py_ssize_t sz, Ptr &predicate) { PyErr_SetString(Error, "Loaded font has 0 tables."); return NULL; } + Ptr cmap_table = down_cast(font->GetTable(Tag::cmap)); + if (!cmap_table) { + PyErr_SetString(Error, "Loaded font has no cmap table."); + return NULL; + } Ptr subsetter = new PredicateSubsetter(font, predicate); Ptr new_font; new_font.Attach(subsetter->Subset()); diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py index 4fbadee955..ffe09475ee 100644 --- a/src/calibre/utils/fonts/subset.py +++ b/src/calibre/utils/fonts/subset.py @@ -110,13 +110,18 @@ def test(): def all(): from calibre.utils.fonts.scanner import font_scanner failed = [] + unsupported = [] 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']) + print ('Subsetting', font['full_name'], end='\t') try: sf, old_stats, new_stats = subset(raw, set(('a', 'b', 'c')), ()) - except (NoGlyphs, UnsupportedFont) as e: + 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!') @@ -125,6 +130,11 @@ def all(): 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: From 609724c65b16747d3c4ea496eb9ed9a5e4fbcbc2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 08:14:34 +0530 Subject: [PATCH 30/40] ... --- manual/faq.rst | 21 +++++++++++++++++++++ src/calibre/utils/fonts/scanner.py | 9 +++++++++ 2 files changed, 30 insertions(+) diff --git a/manual/faq.rst b/manual/faq.rst index d7d6367f69..d46011d8d8 100644 --- a/manual/faq.rst +++ b/manual/faq.rst @@ -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 --------------------- diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py index f3bfd027cf..ade0b41d8b 100644 --- a/src/calibre/utils/fonts/scanner.py +++ b/src/calibre/utils/fonts/scanner.py @@ -271,6 +271,10 @@ class Scanner(Thread): 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: @@ -301,6 +305,11 @@ class Scanner(Thread): 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() From 6b644e1b8797b3f5ae63ee0a8d8398407be24707 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 08:24:39 +0530 Subject: [PATCH 31/40] ... --- src/calibre/utils/fonts/subset.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/calibre/utils/fonts/subset.py b/src/calibre/utils/fonts/subset.py index ffe09475ee..f000f2d608 100644 --- a/src/calibre/utils/fonts/subset.py +++ b/src/calibre/utils/fonts/subset.py @@ -111,10 +111,12 @@ 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: @@ -141,6 +143,9 @@ def all(): print (name, path, err) print() + print('Total:', total, 'Unsupported:', len(unsupported), 'Failed:', + len(failed)) + def main(args): import sys, time From e7397f60d58d39b0ac2f05f858c9421ff4b58ab0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 13:33:13 +0530 Subject: [PATCH 32/40] Put fontconfig back in the OS X build since poppler uses it --- setup/installer/osx/app/main.py | 28 +++++++++++++++++++++++----- src/calibre/test_build.py | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py index a101c574b7..cf1918abaf 100644 --- a/setup/installer/osx/app/main.py +++ b/setup/installer/osx/app/main.py @@ -15,6 +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( + 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', @@ -178,7 +180,7 @@ class Py2App(object): self.add_poppler() self.add_libjpeg() self.add_libpng() - self.add_freetype() + self.add_fontconfig() self.add_imagemagick() self.add_misc_libraries() @@ -377,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) @@ -395,11 +397,27 @@ class Py2App(object): @flush - def add_freetype(self): - info('\nAdding freetype') - for x in ('freetype.6', 'expat.1'): + def add_fontconfig(self): + info('\nAdding fontconfig') + for x in ('fontconfig.1', 'freetype.6', 'expat.1'): src = os.path.join(SW, 'lib', 'lib'+x+'.dylib') self.install_dylib(src) + dst = os.path.join(self.resources_dir, 'fonts') + if os.path.exists(dst): + shutil.rmtree(dst) + src = os.path.join(SW, 'etc', 'fonts') + shutil.copytree(src, dst, symlinks=False) + fc = os.path.join(dst, 'fonts.conf') + raw = open(fc, 'rb').read() + raw = raw.replace('/usr/share/fonts', '''\ + /Library/Fonts + /System/Library/Fonts + /usr/X11R6/lib/X11/fonts + /usr/share/fonts + /var/root/Library/Fonts + /usr/share/fonts + ''') + open(fc, 'wb').write(raw) @flush def add_imagemagick(self): diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index 2b00d63163..c80352229e 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -122,11 +122,11 @@ def test(): test_freetype() test_sfntly() test_sqlite() - test_qt() test_imaging() test_unrar() test_icu() test_woff() + test_qt() if iswindows: test_win32() test_winutil() From 9977bafa6799bf949b0b08401e23cd634fdc86f6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 13:36:10 +0530 Subject: [PATCH 33/40] Do not scan the system for fonts on viewer startup, only on main GUI startup --- src/calibre/gui2/__init__.py | 13 +++++++------ src/calibre/gui2/main.py | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index c1a088bcac..0a725e450a 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -764,7 +764,7 @@ qt_app = None class Application(QApplication): def __init__(self, args, force_calibre_style=False, - override_program_name=None): + override_program_name=None, scan_for_fonts=False): self.file_event_hook = None if override_program_name: args = [override_program_name] + args[1:] @@ -780,7 +780,7 @@ class Application(QApplication): qt_app = self self._file_open_paths = [] self._file_open_lock = RLock() - self.load_builtin_fonts() + self.load_builtin_fonts(scan_for_fonts=scan_for_fonts) self.setup_styles(force_calibre_style) if DEBUG: @@ -793,11 +793,12 @@ class Application(QApplication): self.redirect_notify = True return ret - def load_builtin_fonts(self): + def load_builtin_fonts(self, scan_for_fonts=False): global _rating_font - from calibre.utils.fonts.scanner import font_scanner - # Start scanning the users computer for fonts - font_scanner + 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 diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 0b4a755679..f2b09b3a79 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -95,7 +95,7 @@ def init_qt(args): QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setApplicationName(APP_UID) override = 'calibre-gui' if islinux else None - app = Application(args, override_program_name=override) + app = Application(args, override_program_name=override, scan_for_fonts=True) actions = tuple(Main.create_application_menubar()) app.setWindowIcon(QIcon(I('lt.png'))) return app, opts, args, actions From d69b24371d4cf8999e728399b0f3bd7d4232dc7f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 16:48:03 +0530 Subject: [PATCH 34/40] Much faster custom implementation for checking if a font supports some unicode text --- src/calibre/utils/fonts/free_type.py | 36 ++------ src/calibre/utils/fonts/freetype.cpp | 12 +++ src/calibre/utils/fonts/scanner.py | 11 ++- src/calibre/utils/fonts/utils.py | 125 +++++++++++++++++++++++++-- 4 files changed, 143 insertions(+), 41 deletions(-) diff --git a/src/calibre/utils/fonts/free_type.py b/src/calibre/utils/fonts/free_type.py index 6be782dc79..980bff0f7c 100644 --- a/src/calibre/utils/fonts/free_type.py +++ b/src/calibre/utils/fonts/free_type.py @@ -7,7 +7,7 @@ __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __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.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) - - -if __name__ == '__main__': - test() - test_find_font() diff --git a/src/calibre/utils/fonts/freetype.cpp b/src/calibre/utils/fonts/freetype.cpp index 9086ae0377..58d014c6b9 100644 --- a/src/calibre/utils/fonts/freetype.cpp +++ b/src/calibre/utils/fonts/freetype.cpp @@ -115,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, @@ -134,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 */ }; diff --git a/src/calibre/utils/fonts/scanner.py b/src/calibre/utils/fonts/scanner.py index ade0b41d8b..564018e062 100644 --- a/src/calibre/utils/fonts/scanner.py +++ b/src/calibre/utils/fonts/scanner.py @@ -15,7 +15,6 @@ 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.fonts.utils import panose_to_css_generic_family from calibre.utils.icu import sort_key class NoFonts(ValueError): @@ -117,17 +116,17 @@ class Scanner(Thread): :return: (family name, faces) or None, None ''' - from calibre.utils.fonts.free_type import FreeType, get_printable_characters - ft = FreeType() - found = {} + 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: - ftface = ft.load_font(self.get_font_data(font)) - return ftface.supports_text(text, has_non_printable_chars=False) + raw = self.get_font_data(font) + return supports_text(raw, text) except: pass return False diff --git a/src/calibre/utils/fonts/utils.py b/src/calibre/utils/fonts/utils.py index 29b39bbefd..8aeaccc16a 100644 --- a/src/calibre/utils/fonts/utils.py +++ b/src/calibre/utils/fonts/utils.py @@ -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) @@ -267,16 +272,87 @@ 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.scanner import font_scanner family, faces = font_scanner.find_font_for_text(text) @@ -285,7 +361,40 @@ def get_font_for_text(text, candidate_font_data=None): 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)) @@ -299,5 +408,5 @@ def test(): if __name__ == '__main__': - test() + main() From e45cc99e98389bf4b03497aa5acf7eb97c0079fe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 19:29:52 +0530 Subject: [PATCH 35/40] Fix #1073558 (Doesn`t recognize tablet woo) --- src/calibre/devices/android/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 6159d1b065..db318e5df2 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -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', From a63397353efe19e49f63c9397069db1e48897a31 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Oct 2012 22:16:56 +0530 Subject: [PATCH 36/40] Do not scan system for fonts in viewer processes and on multiple calibre invocations --- src/calibre/gui2/__init__.py | 3 +-- src/calibre/gui2/main.py | 3 ++- src/calibre/gui2/viewer/main.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 0a725e450a..895bb5a6b2 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -764,7 +764,7 @@ qt_app = None class Application(QApplication): def __init__(self, args, force_calibre_style=False, - override_program_name=None, scan_for_fonts=False): + override_program_name=None): self.file_event_hook = None if override_program_name: args = [override_program_name] + args[1:] @@ -780,7 +780,6 @@ class Application(QApplication): qt_app = self self._file_open_paths = [] self._file_open_lock = RLock() - self.load_builtin_fonts(scan_for_fonts=scan_for_fonts) self.setup_styles(force_calibre_style) if DEBUG: diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index f2b09b3a79..7a73f12294 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -95,7 +95,7 @@ def init_qt(args): QCoreApplication.setOrganizationName(ORG_NAME) QCoreApplication.setApplicationName(APP_UID) override = 'calibre-gui' if islinux else None - app = Application(args, override_program_name=override, scan_for_fonts=True) + app = Application(args, override_program_name=override) actions = tuple(Main.create_application_menubar()) app.setWindowIcon(QIcon(I('lt.png'))) return app, opts, args, actions @@ -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_() diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index be332ec207..7b624f170a 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -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) From 62422b2d797cb89854660da1ed2e57042bdbf2df Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Nov 2012 09:19:59 +0530 Subject: [PATCH 37/40] Kobo driver: When using a SD card do not delete shelves that contain on books on the card (there might be books in the shelf in the main mmeory). Fixes #1073792 (Shelves where being deleted when using an SD Card in Kobo Touch) --- src/calibre/devices/kobo/books.py | 10 +++++-- src/calibre/devices/kobo/driver.py | 43 +++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index ca51d67e1d..43b06185a2 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -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) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index dcab3026ec..34ddf21ed3 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -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() From f8af6aacdeac1e49b10e5df5df20f3cc8fdea93c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Nov 2012 12:06:54 +0530 Subject: [PATCH 38/40] E-book viewer: Add the ability to rotate images to the popup image viewer. Fixes #1073513 ([enhancement] possibilty to rotate picture) --- resources/images/rotate-right.png | Bin 0 -> 6095 bytes resources/images/view-image.png | Bin 0 -> 24905 bytes src/calibre/gui2/viewer/documentview.py | 2 +- src/calibre/gui2/viewer/image_popup.py | 14 +++++++++++++- 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 resources/images/rotate-right.png create mode 100644 resources/images/view-image.png diff --git a/resources/images/rotate-right.png b/resources/images/rotate-right.png new file mode 100644 index 0000000000000000000000000000000000000000..664eb88f00355f0de99b33a1abb8e152ef1deb70 GIT binary patch literal 6095 zcmWkycQ_T^8$b76*WQE>x(Hd9%&cogME2gBua&LDH4<6bGb0fLSocDY_pZC16I@)R!q|BrM0E&m|Dth=C{r^r(2mqv3TGtY95I8Gp zDFW~*iR{uAj<*TzG}TnV_5WL8>-$u^hs0am)E9rk#Q!@)i8nV0?6^!9Yp#5ZPHL?<2-FxlJ0 zE2a?p8YgXyztYKn`B+DMHIiT0gpbKSXr*h5kL5#=yEP`KLqJffc0W^+BB1ic_2pH3 zJA8~O+1|^nty%QQR%0$_gk@`~gOXCdCM3{jv$I$*A&hUdLrC20g-g2PZBprQ4;9Z0 zh#v@IM7_kQo|uts03BD?1RZhJ&t+#YbSTlH2WlH$c#N`>;BeHDK?i~mj9Xv5yJ91? zcGC@18sW@4-2pjLvwHD9P-tGF6q7emu_Ym!yVpjIP@t8WrdxJ?Jk9I=nNB% z|JlR1aqZdVz8fnaWJd^uZtWK?-X~gzHvRmtWX>!j8;*Ru%7e){kcV;SXkl$Yi@DXsSY{-#$;7CSREI>53KqRatA*j$^2)=oQP9~2Z&T_jnPjv z*0`~`27rPxv)X|Sc3zu^F-)o76XQo>0Ya__; z(Ac15^MZeb1?q6bP|GyG9EyM@=Z9Rr*O*M;{4BG0tl_i@3VEcM{%mJUdLHkRaQbYA zg8HfNL=l_uoftZa{o!JywY=JK{bfK z4i~M8bZJw^lZsKZpe#a?D&QB|!o^4y8Dyv8Dc_egJM`(^tKI<$aGcYbLIi4aViVuF z4O6j34n1sQNVZRc_|c;EA$d;sYwkhv97|n3rs@MJ!?CnLU*mYP{`kohmRFHTtK7e% zO#ihJ&cYTcsh)ZfRS(i%UFR&5y@dFc<@neu^5f}9C@I4bzQ_fv#q|90Y^{VRC6o)E ze&vMZ*%`H2j-^$4zFjJSH1yglCmM@8R=xlBZ3q40*26V-)E9cW8U98#UE0UnPnUQk zlX%lI*4hSg1Wl2{R3*Sr0Q6p?`W*8jTq zwi%&W62JH9p^w%^3b>cAb#eBYURz^OJghXpAdG3OxW23c5owD%@_Q`i_dJ6sZuabN z$yT2g(0-ubsh4FEn6R#_e4el3o5O~&HekSS6cL5xbG|Va(9P`5{$3_0X0lhAQ|EA4 z`<4uU)op}}##VR7=?aAe@1^RlyQq^mEcACBcuVehOEK~>z^nVHHvT;HeAGz;jSxXX z)Rj=)kcW1bCtTeedM8o9-tr`1(EQ0%w$%)c4_5YM)k}TzA(ocartKzrqa7=k`-~Kqfby3XmFOX~z22@d{y$ucMGR9nOZTUAwxs?huUW zzaT|^rN+8vIx3XA_4zkyq4vu6#h8t{Bo~krs)Zr;haZ57Wtz0&Slaq_7JAfj*;nGy zXA?o6?K8lVY=Im!)FPQWfv`r{>-{Kj`lv?2m)7HSYpMamm#)n=G@qx%3$rt=y;fz6 z4di~x=ctSxDH^)SGnzJG_X@{m_3tg`vSd+HbE^V$m6SkdG6kN3t^2!WfmnPjE+rSB z2XAFOiRfI|uj-Pj8Faz=t^% zFKBm&Gz9%f-+8K3c#RG#32E89Gh!$nIKtr0<+KhOIzna~9NPvQ+B)e`qWS>CFt-|n z;zIO1fVu1st!Wx)1y2!mTch`XeeXN9jSL>nX%Fe32aicmV{ch3lZaJUOzKRt*=rfF z28R06xKT+46e|{q`(Tv1FVG+8D$k8pF}sL>O;))Lr?-*Es;*4RgPyb@+bxE*8373| z$9V*pEp+RHOlVh|a4NHv%`F{POov&F(a*r7tc*VUb#%%mfsLnz`r~Kg6!)pWaf4b~ zk*h#MeeHcY>I2@_#*mPEsM6**+h1*&Hzzt>!FGmZlTIpYT8){3vn;s{L(6aUwz49`;FY6Kb0j9w7;(r z^=F-?z5XREKiIPOi9aCo2U`7!dj^@e5LTLKeQ8IAIsfYUS6bAovs#XCF)}O#H+;Q< zb~{25Am5HPk1Xc!`fJunt1h&Z7&sUcQ>Ep zKCJ|JrPKhA9hJ@e{l%y9$Uj|wG+d58W`?M&Ob!DpLT}Hu1w%t4njcwgsX6~rJT~LG zKDqj4Bv~4M`9H2xA(sQYd;A?YR_r`=UUCf=&SZ{r|L7xmure7YZ7V-V=I0-oJ%?z> z4~WlrmYx^)Z7DmKF#`@o__&jz*_%>iU-T}o@V0XWe8U5`D1`%$>VO7E`5G2_qk)rn zDxW#dpIz-GHR5#}#K;M0wjDmoq1dLWmW##+n|Q6PC)d2jbB?`mTpRfd5+Uquu*6{p z2x6pA*#v?Gixt1)30;? z$>jCZ9g54Y%2s+%6Wt!*;GrzMkkjsH84R4i>W#u(x~cWv!TCb+HUdRQy97k2o0!D1 z)W)AV&o>|6wH|Sj@rrH(i*oE+vyj5Gzw5DeH=l&H<=M=9W`E3jLxtC!2oj3?symkU zq0-ty#OG{UC|AuY3>nhZ9UI>IL`Y2EFTdx&8c*JzE$$3C;(9l!#;^arc$gaTQ;+O6 zHVz~jiLYKCoS{7>3XX{)I1*#LvO|dRM56DQ*|f7%DHE_37;$HGL!o;Nqujx>RaN8-w-ZV z3C;Y2*GLBArRP6OWGF3qA;;``)*yqxUdtDb)<9!GEv%!-VZ6!Bz_@P1s;3q8KzTm= zlUAAWIRQ$!ty&t!=! z+RMCUO`##p{vsBHfmXTqs*S8bKAV+Nk4wpff!3L^^@#1uh`;>W-pZdbeEDDIj~v@o zR1)88-nR2oHpg$kl_VVnn_CK#q}@6X-ZZdZh#=>IRyW(6_tOcVXIAC!TN>K^7Csy9 zjjSx_pE*2ldFK8if$|3P>A~6U$mpcDjoI%LR5xWWMWGJ^^8Y;g(ps}#tWYuaZ=HwX z*{dQC;!Xx=pGSGfFnow5YAGj4uRy`XJ;#5JP*LR~c4NG2tI8}XRQrk&+`A*s9YY*X z{Eb+}FNP^6#^tD^=OZ-?Y%Ke2i9aTN;J{|!!e*lGkbg`4);AXjjhy#h&i;1@I8>5P(bFV_uK|x;uWetvB+rVhSJqI2mR!K}GDSh&8&JUhP7_q;U`&osImNTS@sg zk(w;kFdXs}#_t_KJG?TJnK&g4(D@qdgo}z`;zqH2G__;hrcG;jUB#S=SnxQkxcU^qZwWtYl#6%ii|;!45(+`bu*y($&oA?2zOv2dy6ZC5g(Dol zxQIdepTE`3eZ`lglT5Ebh#f(e9;Bs7NlLt4<0AjvX~1+7M-mGVkcgtf!@zEOxs_*> zZ#9Rr2SsQXGuG9Cjpbk^=i5Iu8Sz5r4NAN=U4syBFKM`3gs->Bto$QxGkSHEJL=@B zH;wqQsAyVWIUeHpk~YmqKAbdy_<_m5?VL3McJp7!T{@^&*UadG&UOjWu0!Bj@-}Op z<6#sLu?g~Pu*Fo1Iq_LVP{b4JREv+nRiCVry99~0DDF$yLLgMLjKYKUaktsiuWtc! zolyS2H2_=fMh*W>K}k~-OJgK#7G^j_RTCYe6T*c3581u&AISe&Uqk85{`{Vp7gu4l)4{UMIW#(n zOV--xqCY6SEGj)n;#F)`AoNbf@Ugsch9K$<8IK8{5!-2m_%2Hv(vUV9{1ZVYQt}2^ zg(cx(C{ZUKu+zWL;~W$@N1oMkyvp*Wm?|ar`AQP(?*cM|k0H_zBzPuj_2g~?iQ zt%ecUs8IL#`~FN2`>As&fp`>b+Y>Lh@qT56Bi0L?TG338R=8={Y*oVr<4Afeq7M`5lb5{>iNrRcXR1}l(y&xhcc9{Kf^LOrnQ{C~_J zLC*6@T7=Clt|%jdFa|x@>Gyy(jQWuOR~@EQ%m&>@1?~_BKu`6s9*yc_X%iQbL!3S} z1Sq{a7iTXBmI(;MXEU^cb9v5zC%bVdQ+E;N8)RUMFgJBx3*|p)bQRGo8)T6mpYd-O z_f@@ka^OwW#2z?QxAirb^Ytq@dIZv9`7Xe6X@4Wb$KW3la0+C2UFG&Rq-1Cs*Ol!S zvJs-n%nVLR0Ud!pq(UI2e;#;$^4C=p-reiLN*$Ho%N^rfo+NuWcs-OXPt5I4g45us z{1XRp8SppCat2u(L2TF()wJINhWZ)D+N(W6;G3#xT0RLnN_F2d!>h5PZ#`pIlnzv4 z82tSbBWLvQS;7AfAo}!(kj?d=eRkHZqN_;;B>&i~X}o%;y8o16YtVohB30%#ED4K) zd`6xTqH&rW3z8t7%C5Uwt%M4WWW<)=gjlIX|B`_uR~KDA+8QK64;l!b$DtyztdHse zlciFl7i`D?Q*j&7^Amz3>kcm7k%qQ}WO&`R;1Z=T=(oGdzWRz3yoMRZ{p0#8z>!4F z02)4!$5+!~?STY1Ey3{3Iy>G2jL-&Vti!#fZ-+je^z=`k_wO*U=bN zIYCq0)?}fkP5(AuPmL-iEC}9h*4>J<@-!6&QbNnjo=pdFx7w3e`aQElQU5cXPhQi8goWmA4dj?BS9Uv24aO@0VJN(gk)~02Fuf*m4p-|db zNGrem3H4OS?m2G^4$H^zHEW2{Za|5(z-}kFZI9;39ovG>Ub`9UL!}pZhY}D2*gqsp zGS8@Va)R=Fc;9+HB1y*a)L3oIfa0bp4dy;kA%aRa)&YS} z2Re5Ot~TeMf1UZf<&|qKpiwCg>%Is2_FeY9%Sd_1FV@+~S$qyiBL9oNWo<6K((uZA zKqUU5c>KG&-AZHl@j(SCXjt5_*zm~P!jC(7JmxZVmF)EatU=ef@y80Mhc$uM>#a%G zr!I&oE2EHeNHnSmJ^yq*#PmHG#Q%olGj|JXm~La=*)@c46}@Q>L)r_Z>DdVf^yeNM}=oP@j!m!$OB z>T~wIYpwNLzqM-BK0<5F58DrJlmFH?@Wb|R+kU73Kj7_$3h)Eoey9LH;O&PB@B`kQ z@7IZh5Z|v;t!h_YeQgD}06-~qf$O>zDFlhugkeY=$A^GJ05{+CwY&d+xrzTE?Nkw; z_gsDNQivUuO3xL8gF_ee_f;+!8Xm5!9UEn6aEMB!2jBM)LZQ)w?G{Vb zDl;c$nV3Az@naKA&(0p2o1J@VadGkKMC-eMs1%*{?@|G-y87Bm5Cqq)8ymZJ!-jR& zz3Nr3;+%8NrmwG(?Gwi_^;(T)vq?J)AxQvmT^BD1C=?2~uAA*&t<`vb&t4vS_z|9Y zcF*F})YOg5X7g(|-}JQ`|8-CKzjyn0r2to5eeDIK!^0oia^{wIU3Jxe;)3(H1JG`^ zIez>YQ&UsSOwTYszrgbH64jL|%|??jjL=DfkVXN5ARu2TP%ib**Vjw`z#zlJLyV5D zrBEsWuy5afZoTz39(&@cL(?Zt{9U5;=Wf2~Ylprs5B|SV`*)!LS6_48j&Z@z|dxkSBIW8c1g96WGv3 zpJdn5Pjkl|cTU~=!2Qnx`+yao4lH35plQEH_Is)Ti9NN#L^2Za?GgY5AZPyuK%r7D zpYzUlyz_!fE`4?Rq6;r%+u3K+ZijsK?>^6Mx7~55R;#`HKRRLjU#(;Ml_ny66fBj#xfB(Tx03ZF2 ziUy}u03n3X{|aq5SpnXB?N3~H(S;X&_QN0gV5L$i^XS75^T7T0bK{L)pSt_rdmaP! zV1zvfOxn*&7@>zWe^x~2Z8kB)vj1u(N&*_p2T<`qf{LSBZ-D`L3 z@LzM;Wgr!Q@yWm9&b#hPXP!a5JDj3_csBX8sEuA zc&hb|<^B2Of&D`7^Gcz;H%*=4�jRSr>Rz}?B2bb*IarjZ+zpM*na+Y?!W&b z{^FC@^E3a)Pjls!J6`hqr)%HKJ|`R7%RSe3dmgPdK@jl8FMjb$?|ILAe#&ti;Q%P5 zWE4e#l+yP+?`1Xgr4-<=|L5PZWy@x+zUoapyK5IW-+VK-+;R)&Uw9$w)~#d9mMx5q zj#4ZZ$>;Orayfz^!1w*rp7;CFg21O#Dls-T#`N?wQ&UqMKYpCa$w_j#90v~{=7xX% zXKJ-7mtA%#xk8RV{^LKzab4c{hAY40>wGW5d8u_Tg}yKKH?aw_v>#8kpXYfvj^hIb zrBthXEI_6lAUzR)lBW=gaT}{v{pDYMicK3garISi;;AQ|;G5t27PsDdE3bRq>o{}k znQY#)ncm(Cg+gJK@P&}D>Rc~nIp4`XC-3*-=DIFpV`KF7^-(UD>Fw?1@ZrPwzR&FJ zEML0eON3#_MV`w9AUG@p%b4Y2m*HOxSXjIQ~c70KSH$r z4Io|n{N}$Gd99Q5)LdV5ZCI`3LV(tGy+sU(AnDpi+k0Jobb9wMzMv3xn+9)S3!nYP zvtA_Q^s|!+{FF@yN}FB-fFq^30d0jZAZg6Ae&GNg{_saQJ~6>QG%8ZS|w;#gU}!oq)L#EMtK@1(&$K&LF+=H!+LWL`g!T6SRgTF+MKQ8l5D>k!E_K&C+rkp%WaTk!T#D zQ9@_Wpd{Hkvw#vHCHSsKX<(FM-!MIc>k0BD?tX5OfBMR|n3DG>%AcNRYx@+exnrIN1A>39i&AA#el;kszf_Y6wu$93zEB z33Dzf5|ALJ&d#rd#z7-xf@9YUp^;K%OSi1G)-PWXlx9{G!d}>Db9mr(e*BFWGd{ka zd+xc5Z~V($!~^HkJF*SG*n^)d5d;O?T#jG((Y5@i^DBh05quaM!KX?Pr^*m(;sl~Z zlO$%;H?{&#G}*dry%sdunu*yKwN{Ll0wqN!-cBuDfJoO%flRjxff52GBnXM?1Qhzl z$n_3W>Kh|h?B@%&KFw{n+(99q=Y8*a7xB@%Nve||KxMwQE8Z5JK9fR#y^gf#pru`h zy^r)*>^?#uF=%TzopDy_c^src79mlj2p|#CD6=cgIfXF7lCqP3IN8wSI9|;GP8suC z->8OS^$Psln?`uewleJ~ zF+xu;f{t0O1gT){=SZ7Ui zuxZyCe{1p8b&V281jI2qiMjp@H?#SSaW1*!5-xl7tGWM~Z&H56`$1>~5-l_`;}*tK zCqf{*TtLbMWa=8E+1_y>=Ga7Nl=VxAy?;##)ezhBnddbYY_6fR=Tp|bne)Fiy2o*R zY6MsvgPQEC6y|P(5JU%W;oa|eJ7E~|;QbG9<2}#PKYjsjP{i@_C^x|I^IZMPel9<& zoC!XTEwpPRbR%#&f=(@6r}tn8Qny;BkJFAc6Z0Wb3X~&pT#4r>93}A_v$(FpaU_ls z=DXcb+Rv_(2&GU;pd5j#%(|SHBVQgQ-#^kkpd}&@#QJ%Xk*O?g9bXaP-z6~AV3%lP{JIqgtW&?gpkPY?-*!T7!(uI zxHKt@Z|m&)Vgw*ntW!5_S#Ek!^H7b+U0k>+&-v${!{d)U%ANNG3pTfI=`i{_(#8_vd?MP3i0ERHFW^cx3n_Sfd$|!?U z4oWE;1)h{BMxJbx1lh8SttwhLUcM@N`pw&_Zt^#{jfCdRAfeF1S#yXWbmCju`c0b6b#vc zc}QKJaUa?DlrVm83Sg8#SwAUK)_n9He(LRSqgJi*_+yXr_>q`gsUOD=P+oz2zK4xN zMc#AeAR02Co)W%};5*hHrwWjy!tYWDIw+ulMwoD9K12(FBc&luQsPK~lHgjak<$8o zDeOL`LzNJ?$`C>+!BGO&G5%IaoA|QU*fTIfp*TdLvW8rFfG>ROK8_wa%3FW(E%XGM z=Hz1_C0dX!(IPM785&vEW}FI%>3=BcNi;=w2PGPLO;96yial?aL@yj+fAPLcOy7rE;w zK{w22-&4}3(=ap5a%DgS)@@8Lh6uoM1dcM6EFF-JKq`rJ1dapB5%|)6S9V_ql)_lP zloj_Fjc)2orC+a-%=;M@11ulQhrPTHr6i~L~RhJD)I##Z% zAX8VM5JpF`e+II(RoaeH2&+gY%cm@YWZUQk z#>|t8Az=)zkT}9XsZtV82wWjDSKtU`&_N1ZsZdfPq|6k-6$a*&G@l*oN*u>@qOxv4 zDuwI$R0h{k=o!WhN__2($C;j<=09KeW_%hnPdsZn(oz5{#48KiQlze+BTQwXUCVBN zZy;VFv;DA&=ydsUX%$2vv-JpR&M$3Dn+hBY6{l%0O9G{Rh{oJe*7n3)eDN#Tz56+y z+%tjSzX8WDkuUU6$d~Xv-*l)FJh-pUy?a)y-%mp~LjY5Oo9`WAr`Dg?EQYWdF^yoc zo=|UVTqSTEfvW_bT^ywh7f=FU30%jlcO8jR;5l}^lz1u?xDm8L40Eis$0{XoWd{k$ zl_7ct*5l{O>^v~XbIx&X_P@_ z3X&pI8k=X;7w-?IzQT4#QAdhgv_@*@`k@WS3b99a@5H;({mA7b@ccslPYRY50fD1qj*U45=>>Si#Cw z{+<*Cy4;4%3}x%SXAYo%q`5-8u$LV>Ud#OaJkP&yh=wyvQ0O7Z7s=%dIG)41cJy=I zE6WH8j$@J%H|?x4wG?G!FC%KxQ?-KM;Sy=cAJ5Ig(on~-Z9sdQjjZG=;>dB7gTui`AH5PJjf-NTue`? zNNZ}R%_>?H$Xq~|&leI$bX=;E9XNNbwQ4D{2tit6r?PO+xQL87$TZThp`JyVj<9WV z>3a_YjWG%#sm>l{^GJ!UTeooJ&>;@YG|3mr6mlhUK@mR)xa`b;-hyJs*+pKlF^8i} zyxod5H$J|^Ts6ug4#QF{CQiDAE{xD@eD6q!CsihF$69h48Kj?G)!_(A z%FNFjBq38E*CK>#U4?SYF;Ym%{UZ#Fj5A%0*|%>$rBZ?Q;~Qws?YC^bO9;|1aG8dG z_JKMXI+^X8@&=g+tP>$RT9Y3F1IKqyG*~Jnj zAp{A6rJ6Q^cG5-SboqGKv0;IhkKcUfl^#%07&bP3XgfvHs} zp)5*t!o=*@^q4dP_}99Cq+O>qcYt%xJsW`I$0vyVesZ}2UQodI1Ei9iIqGNZ-cf?< zexytxr%=*l+CmL?@2zD@kcM}wR8DlQ6M||ZL8R7c8NZWSu@zAIIkkAlTD;0W+vHCw z*eqv|CXj4?AhjY#ARU`jQWEJXgp!u9Nhp;&kT#l^Hs3E~raVH}QNS!SXxsoPA?%I@zFP&*maU_q zQJtmYC!BN6+04w$FgaHz$d~bh0*)8pdM=l33yko)e7vg!*IrPf6m%>q)`Eu)G+1uL zUB2Jkw6@YuS*+HYYAdnOZiTH3t4~RulfgHcfR$bErdhtO@7)A3JyvBTQFZbHO5ixk zDxA4q8c+L$9(o6dS!%?bn4V^Qe4IitPqcW<3REJ5%B)*i%U2?G4-%o$L=QH**U9CZ z#e|BPQrPfMWZP}{m|A$2^s-@LnwgY_{GP78Y$mY2G(~^eV{~+cxw$zOTaqBi-ya+zE2XJg?^R15Y9c8Th$)jynTAi_Y>=0@?nudUtrup6t|E@!( z5RU3%{f;u>o#h28Oa65BRjS*Kq|t-4uA%E1nZkioAeBup+0G7fC5q(%VqA_MpP^95 zQ>j#FFHKl>pTfGbN<){Q?_%~UW&7CVy|M|z)R&WyMJGiLn*_>;rZCAPZNHnry2O@= zks-qA5a6Xt!YnqoR^}Kh7bq0+EY8obc> z?t7s@XzS86uzRx2wqXxXnIuze%WH%Hoj|Q^tUqg_tM)}}llagULqv)mo%4f6q$y<4 zss7Pji%Khn>^@0inRsFgI)pZnDJK7_5jYacm3~%AJAGWM@oh(4pF>1&QENT2Mwh_^6E`F9zEP3(y1r}`)5K% zdlaQ0wG3kw%`ibKtF?(ihqSImbac~>PjsS&#D+*|$7JNvmIP&`Sw@S{C}Psj#_E#< z5@|Y9#%A~x%)pdm6)#upAzvtRVxa**|G)sPJ&z!eAay2qteBH4_7hjKlX zqZsN_9l`VBTz<;-3xVXKbpg93Lqcn<8o}}9gu&Rj3u7sodZc*Nu24p^0zkQ3CTZ7A zXAAReiOmB@-N|ohAv47gX}&|G_ixnMj120`@--l|$h1VJ3L!vY2cj^~>}bmwuoas& zpK;oXuT!l|A$)OhfhcTHEEKImG;#C2c&>+34oXS}d!#AN>azNDG_MzTk!>(;0=k56RYsdGO8cu5nhFvL zA~u99wl^MQvU*w@_;nnYQn`m_+bBS(WDwG@bt5p9DyYy%A&qj;K`kxpK(FWkfks;R z7j$qE>msajrATHq!Q}iqej{~u(rU@eO+q>?0@UjboVbbWn%%-U#`k?Z*CTOU!a5O#A>m-$iOul;wLK0CCj7@$#qi;Ue`uRzXrtR6=C? zWmQ)v)$y1@PjgSH;3Z@uilA)ukVfim1xp79S0lhnmW7|(!Z5TqXS+NB-*a(YheW9? z58d(m9q3=(I#w!#MoHK>;4)i{nQtapRja_v19z23F|u>K=`TcT{iZMu-Suoj8%Wiw zyFXI^{i4qu=$DupL$vAsL>pU*6VOuQNIP5$I1+L}4kz)zzO4ixTs($5yv+#FIQ2~Z zWcFW2JCc;piEdvnG8B-Ru!JRlmfwgD?-8O?GTy-$I_~bf;Q%LR0>iM45D>;j5tI^! z_&F}IlT|;w=&cRe(!psmGA2@A2Yn@l?+NDWotY~gQrG#cSD(eMI|*S@m0x8$t6S3b zH$@f=HVQG;ClaI3C}|K)8pX=;G{V#rtkPtMRO#ocq$4eZPE0I!}0!3I= zy7eOX7q-{|OUhF17RC?fJ%R5is*QwtoQgO*G&|GkEuB3&Dr;3DrO}+^K2rTiNtxDG z5F)GLPcZB~N$U_T;wTIMc7P4X0moGcJEyVMr~{B2SZ}ORce8nedKzo-+VG2XE`dmu zR|urgXdyGDO!txLBXtSaTtjD^BV`T=dKFR3#v5Luot6XwsC+-ws)={=xg1VA!F42& zlq8YJ7__DX{prmb-qOW%t;VO!Ski&ja$Z3*Axt{LL}0?5^fRkHL&|_NpW3YuR@u7w zLz3Q{=m=Y9U1~!>t7eXd$d1`-scoIU2^BpB(sl3y!vU&G%cifSWfodlloQ#F%Iu>N zlmZY*cF$6aOv%!9%KA4UOb>UILu5$=uVDrmOx~AjKa)15=LmTy@cn0=YoSnLFWLNTbKu|wWY4E>wbik zFLG$2OZ5^f;EW<@%TJ`XmM*NY?Gt5gMw<#Ej4`DBmi2md-Nf2~GCA&REs#M_VzS-od#ANNz1_cdN>&$HlG_ETMTvNMWEEx7bKRb_C8pueo zPH{wIDg><%R3LQZ5!}?QR5Rq~KKnfy@Oj>FZDYElOAqgA^ ze1VJ;dJ6@@FlJ$40lzeY5@}*and`|kEb9n6CB~huwiUk_a!IsI$T}z6%_u=?@xrdl zW{=yRFq|VLptj`fH0`tqVCIu4mYafo&+q5bOE1QkPvBVva2-?ESC2IXr{k-I?NEEq zW-^3{v$D=N;iJw%J1qrH7lrJ4a_C?{2Hkc}4gzRf2QPtGOEj_RP@#}g+c9`;T~&uo zC&Cau2Tg3}A*8i;DVxs1PHn?KAMzf=t%SaEfyv3^RBJ2rZ`hE9xoJ$VZ9*vJNvX1? z{6KdLEqzL1o>FFsgARIT$e8VCZO-zbEFA3Wz|#@Hv_G?U^4lp&r4o+g5^9HCyLRDu z9tB?!xZt~x^FSw(V@q8pw0_VQ!)9oZ7BHms_=ZuN@5-X2>K?hv7VM~>9l=Y7YZVAH z#+nVAnzu_G>f^E0a`fVCb%dP zsYx7-O0YF{9oNyF6HxZVDoK&S!l&-+q&4JoP|Snt0HOs_^5oM`15jCemMK36OX^aj zF*cUDKCzm=3RLE^GDeUZzs<9xJZLpatd1DS_ORP5S$w(I{hU?-f?PmQC{Y|3V{-BZ zW@hFXA0KD-xnmS6>nJ9YWd|0PO$t?ZJzUDc;_S9;SY#cx6gDqlLmj2k8}EXAEi&IP zvQA3FO`CxrHm*}{|4LV(DLo2JOQbI8Y zr5pqf6icQA?uC8(3Ce>6l{FTz4VlftR}16mOCO|tu8yS5-&E-7x-@st;RfcJMK&R| z+og2eRjRYu4}?!*X4) ze-<_kTHl9BP^9An2+&#PqeICv25;X;(oOtCw<6eetAu9%Q$phB1WQfLQ?35CnBxZ=!Duw_=Vi={?8e&0`K;S|-4`ol2a|OLYiCw#% zWp-|kfvvBx_4_Hqu&}N*G7ZzP<>I3Aj3+-=>Nb0hQRGj833jgq{*OQb1UvP%3f99k&z5F#}s(X?TP! zAy3P}q_QPj*7v9Nf1UkA#tD=y<`n6R9PHn;R#9Z7zD2ov32$mJgNt)UjywM5ZNNV*4?#gb?T|Z8(ArVCn9cvb9aBNW_brpSdFLKLS z2uC4eGx(%1IS65ekG&V2*u}n&APG(Kr&1L3mB1I^wnLijHcvdY6UQ$wxaCz{7OQQj z(luQ&HayC@LeOy)S{>#iB<-+GJadqemuF@^LHL@y^k~Ep1A}EMg@B>;>v($a z1kXLb3op>=dt!`pvE)u@5SicwiCtm~|CJ&P_CQfW&V`}DK5o11cIM{h7})YkTd$v5 zxSjcvB3oMKtvVAy(%Dv78F@Eq2pc6?=HH1TL^c>@A_|dZ`c}V#AU&3})bP7gfRnTV zFJB;6tl&5lO9;TR=G?YWz(S*K_pAQJ7B9;!KlxJ+i5QF0* z#I+@+Czp_UvI3BF;6^9H)B`4_o-RquGbC;BJ;6|~V4!010B&r`z;6DRTLI06S6+h@ z=G{pug<#QDlb;DVE%r2mPbYDx6vCwqab;&e;~F~d!U(ma3?g-d!U#GGA-lp^DdZ}n z{_Ye2!vo5FL*z>phR?W=*@b0p`0|%nw|*TfbI0g$VW0Y?RLXPbo%e9)&|&)4olDP} zO-P|oLS?gZOdcS8u@m-%R3{0>2J6`*PK%1d@&>8wRIn7vCALnI>>Nta$t{Sk(sMeB z@nVsGmkJ;x1jQ2N-a&#~k(w-X$8C2pzqrVmThHXgvBM1H1ta~?*CVJ_;qWAcZ9yCg zqR4b?*o_JZJJ``d1!4bYbL6G4#45ES1AB2464w?Kc7tAgDE<(G(n;G6|D>&iGaXQG&$j_*?1N6tN)7?{l1illjv#C{h?)tJ4@3r?k_g)E&gU>Rir_mi zIsjus;7OB7^h{XON6d8h*cxGDMKpzA>(25KfOBx9WcRdHRSy^0y3t#)aDnLgd`QBc7 z`iBV{HKt_`_uhX$SH6A+7hZG`&pfk}t>>P{`XOjF;ot-uI0BnDfa6+NPfXM$lo5W? z?tD&1Ii+@GJ{Y6J4tyH+{zf zX5NDo(590O^)Nq(kjDSo?wrOG5>uF(ST`yJ6nkJ`3`&6^t-gS#o@#K{=f1?$+(FLR zbOFs{k8}5pUqU#ND6aE+@7edAE5OM_LD?9oSm>eFGeCW5mJQo4W#;Ky`1Gg$mOuZq zkJI1ROW15PT=WP>p&h}|NjP#0#zu^g)0s9(fsC#2(?pRag0UqvPD&2x8u8N_PL|>Q!{_Mh+a@$0x7lA+gCKkwy%H#Ez(^=%BhjOAC{+&5lLd!etwh z$T7mzCebXSj^#&I_>Cq+Em+eB8%JUD8t9IozVel?@z|q}que}az2T?K3m@$D)6A|+ zGjTSjq0=ev;Y;G(MctsJP4J3sCe+`vrTUQ!hP&~;&yi4^eMt- z&4kA}2VVuWv`4OQE%|UL z%)oQncJ12DKmOA{6Kl<;%ilu($XZ)>-!)0aKCdtm?KDC5!beMW3<5-1KL^&*8nd5w#m=3FYA-)(ni%Cx@Azn*jjn34Z3ZF5w(rN$q*WCv)@QRjIV<&qn%$P0+^nj;S-EHgRJC zEi{!4>ruYKQ3-wJ3OEU_UZQ^}M#Yjmk^&0VXmKPwL38mKlap1vyq8r5y;OTi1xW9? zQ>0@&SCA`Et_%>4)R{i=0_9B?v9$jYKKAjC^SdAY9X4;?%*@OTgM)*dF=YI?tKjG~ zEG$AU53Xxnfij<+2;%e=ZVI9_*Kd_Tntmlp(TJ{*yTm4%QsHYW_!z>}&$qx0p%Fpc zHrv_}wCd*h){emDQP@0Uer+mk^?IG(|NTGY_|z1Ya*_3~cpZM=uOg}0eTXhpO8YKV z3}Gxj#wc+)fI=V00HS8Xt)G8@hwl3-ho8BJ=F$=JiJ;OGpnJyX9SYdcJAzam8^Feub^B{OW1U?)+0ZUczeFOEbYpmbt zLWNGThD8Y3>D+8&E7btH#s#bbB-+5f*7hZpErS3l62uK?CeU0qL%ZW^?b5&6=Qo>e ze*3@u9((ugW5<=R5=9N&GyVn5gsZ zFFnjt5B>{thjx<8?`5ErV|!08L)%`-!cvV^p+$dTkO4W)wk>PWHN|puf!;!e(T(dU z`X1GG740_IH9JdgW60t{#1$LQXX?Z>BO?tWmS_as&g?r-fRm$uw5-?$I%b_C7#JEO zZr6z;GkN6D(hL(56a4n?{C9rq|Nafe$JcY{zyZcLZDL*7lu;G(FmVD-%tNaMt`B}_ z(7^3h0Lu-OfXL~DqNf_#Op8iW_)lW{lLGby({X65Jq*oKubJ&@`eD-s*f0#k`PJvF zR%`tBM}L>+cJJk9-}fGR`vzF9F7cCZ`EkDVrR$MbeSpy|=d9u*tA_Y3ffd>)aiQ0s z;G_5L}+v-a+2TvZy)8uAN(K}U35MZM<*Ct zH%5N|h4oM_K&1pzb1+|pl@Q#>Ky>;hPe*k)LML-}3MH_qWo;vXC>_PKC=e%|P_Na3 zunCnO7+nKv*T9BB2u^a56UV3cz2E&k4jewr@BGgH#j7v5n5pSGcJJQ9a<$4^-tv=t z`G)JcWXnew@6WLiD)u*fi81+wG)!@QgM48OkKg_f&)oYBUU>L6dh?PiMmMqMyb&TC zNKtdR-%4X?lETIjyy_D3(~C6Q5?{I`GNCW$5-(SYrQp(6 zUC4pm6HL|TI6QxpTOPTGvrFrF_Q)Y7=9f;J4^DqIY?a`E8T2WG6{M9snnFHL z*i#{Ck0FwXE!$tgh6{N_jvk{BEdC6(O~LEBqvew$1CKp;&;?5f~VPwZqVV3g`FmLyz&N zfBs1p78iKOJKn+C_3Ho_9_k0SWlHerPyJlotT(q_oMf* z`;l8{O&*{x%5l-=^BC^!q1|ZEw`PRtV<(8+gj`;d#EN|2ap;9(#BqzL(WIRul$2uP z$OKXsSgFo1mzyP;mn4S;^YhDW+cwVD@wM!$9>UGHDJ)3#9X^EI++@p!F?xFZ7eDC9 z3h+`*0Lsn_%mx;OuU_tgSI&cX~}0_ zFZ7jRpcjVwU?A7gpYHb8pZ+X2edFtdaYAjmNYdMhk0FVn0y z86GyF&KVolQD1qEFMsvx-0|SU)K}^(9iKoY^IW)L17~bGi$*mh(h1et67_aMB@9VK zi@xDLbd)eZw?cJ!nc8fXobstgbF3L0AgV9n`S9%G9$r{D#%!&@+MYZj%ENlek!X@) zGiJIr%}QI-(;wgp!9u&r6&DxT(pdNM3Xmeesau?>Lz=Vhe!GuA)7RIJ&?bGg|Bt}w7$s6Z)iDtIbo=r2R%B~<;u{=@w7pZqZg4je+dF3;`U%fftvx4rG%eDQ|Q za_+h3a&%&fbz{Q}5B2luzx@mo$EOeiwr}6gPh9(E0M0w-EC%{}DXIql&&U3p;ej45 zKl>tv%01K;YqX;n*K;X4j>&4qElNt_`8lL&Gd$Wuv5;fx=rVD0+2jwRn3YzYx#f_N z{1|5rZ9x+=JSvfHi8#^ZM=Go=EOKmanw4WAVLhSL=b>^54;|de@k3R9osWI53ZRWs zC8Yvgb6M>mMq1@s=^I4o7}r%uk+3Xh*>u5ccwy(mOioVnhkx{O?zr;~-t{x@qEs$Z zuhq#H4Eri8DEC3q2g@N;8%6=@O=z@C4BrkrqlxKFq)8-K*+|2Kd|-rM%tOgros;Qq zEA=|pU;ioYy6Yaaj%l`9JpJGk}WBo6sL5AC*CTx{~LpZ!_BdE<@vu0wxcFEcYUP#5Os>)I`zd}=4Z{n7uyNB-+C zQ^@Bj6!Kj4rZ+NB>EYRj?qg|smR7S(z1pH$ZITd?cQyMCPEijV^yW*regJq(9+;%n zSSHZ{t|KTFax5;cuu_Xjya=815N^U!*kq*{;ud`tPgI#(u9E8um~SpIA1+hw9bt_p zdH(TPj_g@DWg*B*DnPdqXx(Aoo!_ScL1NxSm1RLY@3knG%Q&8cs~r5mV|i{C2@DRe zW$#muq8klvzvC_*yzc?dId3~ZcJ(z}e6e{6b=Yp>dY!mcVt)%h{ek#@MSE_~UcUDA zZ}Qk9kFs2=qoibUc^=)Y;s-86eZ4fAf>siub(`i=lPDC7tQlt0#tkekE)vHv>({NL zUawQHHweR!FpOx_>-_4k{U$&AzISuc1?L0sBNxAla;1-F9=Mw$yPv1oXwjmLbQDt) z3wYGX6+Jd=Tu*g=nXuksacLeuFG->hH=iSy^O&1+a8*Pz6b$y~@M6tMvqd5zyj(!D z;ZaJ4nVeYQKzNAxsETY(5-8Aaf^%}1b5eVmM36?>KwArw|C07C9Nlx^(;qsLA|Voz zz|Au>x(U5baV~@I!sWGwzF*ngM%X!3q_=(DCF`~mltWZnq2s*%h5uz z`|Rt+(7trP9mT+EF%d+{D&x+Zf-lj{g3B3Z=qJmM_)nb>=NE{h7z9L7NV~Q~PQ(m)d1QYN<739ANx7~abrBaDFjyYrVCKi{MsW%$tr`MFSJWBJeZ{Eb7JWq z!$U({cFm7c=o#SF8@>v}CL<{eIEm!Q)Cyk7qt>VsMG?(fi=~ws4Odd~Ttw2Ox>U#Y z0~*V1<`!01TB@^leFc(;MHV>{?SnWZh>Pe}%*K&1hBg%F@%!m7oE|ze5_n1_5SI3> z^7VGwg3WctiA{35fES z#}CnHwTQ!r4Qtmiz5(VJ7TC4xS@`-ljKu_jUCd9+OQFnf5QHJkc9VL&NxR)545L-I zA2yq`n>CIdKg2*!FKheOpcB|Xxxmo4i*P;momfP>nw7;mf%K`%nDv|bDOWsZXO~ej z&+_sNzxI(|=TATOCq%LNsoL%5oJDPAh2wSLl1>Dn&}MWk2^kvfp{2nW9&Xvkbril3)OCzlN@zJD|M>SeaQNv5IB;Mu%L}u# z>k;{)Z!&ig_<_gd)D)8^PS9>Pk-}kMppTxO9{T$S@B{a>8Lh>I1=?ZA^2`EJ+`!8_ z3>Es2t|pf&P5I^%-X}TdT8Bn_=NtM57&3Dmo1I7HGyXaTHQ2IW(1~ zu^e&B?YHm;e>B8@{gq!Kjw9kYWc&7Wsnu#MEidC}ji(e+DLl_34BPztFMg2U_{c|C zziy0TslfTKxR7`M`iJ>{Kk>Kdq)oG4BM2l@$7Tuq0?lT~y+@wm!u~Dv6zm0@A+rD@zUwf$KT+_LNDq;_^*7Tu1YXzx)EFpod8Hv**c&Id9uVd}H6;-26X3 zhL-bF@$myUe4jr;snFx0oIhtlyZ5F&m80OR}InE z=W%>-k)dLa;Y}Vd99iPxbH`{eJM5V|L_r1&^bD|)i>T(7m|U3U^MC(&ww<|^AHDiV ziQ||kjyZ4pxjgvL!^BaH_AozL=O{ea#dSUYkJ4{_h=2Kq zFS6%}r>RxzNY}+JCQL0*NbLre_zJo?HCh5dh4q-^~OCg>V$U3TTII9=h*A_8)(N zJ4gr@5Ahd&_E%^-FgZ-K#hgWa|hr&RSnS(V- zJq2L&!SMVth?Xm=1_>m2N=K~H}N&kYSCLRBJBAL())U2_Q$=D>KKg|mARH4pV- z8GogX7R6*LQz4x%Q?+Vj(lw&3ZT#R{-(z-P9}AZ*#jzdw`g$prifrAs9m~=%46~8i zW|-)PiJ|Mj zLNGFX096%K^Ci4KFFST`<~O&lBNso2RjVQ$yODf=ia)X#Gb&g#zZ2Uv5JsJ$En^IB zKZv5)2uD(wZE#?p1-4HA(rzkMNk_Lyt2acwY>}xakd8?-9A>Im#47?6%Vk>qAu5#& zi=%#08HF?EXs*+v%(#$_@0j;V-HeO~to}*HOwxAbj))*d6QL~{n7@#8>#hSS zSvIeav(8z@u5Axccj72PAF@)xG&;EEs*Aa2-yrH#iP2r#*|^hTy3moLYOFYQ0k{7A zHvZwmUt{mq2e{(H*Ry~76Fm9kAE>5l_p`m2G45mhDtZF0;0TFrPvFk$ zMhS+w@9y>F3zHle%aJQNtf;!IUAU0NL7nc^r!%l~8{OR+-F;o$ci$7_N(#PCmuSQw z>TAUY3dI6ADq(Lc?|s)hxc}itX>D!Ab{tlmw1Q%xM4?bX*Gx3i#8g#;s$u8`rmmAp zr}^49zQt!g^>F~A?GY}%@{M$LNBPAq-(}~4I7PdPs!Lh}0n+g@gP{pl&tJyEMICf` z{A5#kif$3zcCc#|3WXAulz4+)G*_WouHcOX$Y-idO;xB?UjqSNm`5&M7abc3TyV}Q zY#lzp*ieP5F1wn!vzM^{&?tjD2H5ob$4Qh&ajg$uszCcHNKe=3)Sh$yLBE>U* z*vWhHK~; znm|(xbVElsHOj>bU;3A?@K0a(JOGh!m^05mpJgX3qWXbPkf__#s#&u61ap@AnAIL; zb~M70?z!Z0c^0hd=Zv)%Qnjia+&fA!Q|7*3-cCAQptrLXkEvtVCB?EurDmi1&DTB& zoQ5MMpL^ds5vpL`f_@xSPF}o%3s#&2o&eEnqC9xlqkLoEKlAoA*U+(KF0ViBY|dD- zoJzKa?$cR+^POa^HZ~mCfzcU520Uzj{5gunGQm!pIsQ&Q^VKhM=e>6`wmZigzwlm) z`9Z$)(f8pEMDeHwQc=iR4nqf1)N8HihQ-QN%Xsk7rOtbC}lbK+|^8y<<1{{ogW$@mL9i)j?Mu0um3%c zWwUMj0A^HY?aDRWclSfw@zdWkQJCaQU;aEDJyD9K3YumzK7Jn?9(t6k-~JX7r39b& z>OWI1x`ZQP0&NDRYM#6AzJnXT`F##0<2b6|caQy%kG=0&Ha)qK(bPdMzx*P8^V=Wt z)1Us9eTM>k=RfWsXv7HzgM9KmSMcmYde#dpG@n z4_^CzV$lx#ejoD|E#lI5T*bzHTVTxP(7r5&tuR%v*!I{C-t)0{5lY_kVgjJ3qIbd^(HJB=v$o*F4;J`(_3fJxibFP!e8-iZv8f;~O{rh}m-% z(l@_@C5uj@u0X4|mx^mKR@hIZZvhEQ;nH_pPrz`9%=0n0&m!nEaBDR-Z`_AOi9({%>yNqfCDGP~PcUk|(ef-U-vHyYvpy5mR7&^-qFGo>TRvfpSOsP)5 z5KLtYtUB=&bfb=_+SK(nG^b8m2zr;!!L6@hL06c(u(1jj>B1C>Vlua*g`up)r#^8d z#p*6LZ`wd;-i6%yvq!k7){qPbPdha(KG{(YBc=> zjn~5uKT4%sVa<8x0Pwmq&g946`yTh+xt_#G0?TnR0|v4V(XMuU0;cRLk!UO3?QP_| zi|Gu%oqEke)m0ST#Hv>i-U4IeN8d|%F#)D`_@qQLR9d>cXu6JSn9Of$V_9#Ar=A<4 zrKN@bPK`rjqwLFtnH7$*YM}?As3@9_E~+R(Vv9QCV|h9{I{4$ZowVeK_}%^AAe&EN zqu>??Sh}plIp>5?mATw{+g2XgbPs2)4RU4oWrRYYS~iu6P9m*SaQCw8)IOBT2&bL> zHfnXr!i5jAbWWY)mUpA+aol{8&b~Q(<%Vnc)cc}r*|D9IR`gS??dS6!eJ{15$?iQR zb{xtvnN5-?R4F9OToQXb)CE&+k(RbrPCsEj%NF*Km`t+%k;m9Snq_X!e0;tTn$?FE zGRb8#B!*M?%sSWq)4%YU>pn+&dj!XEShR2vC@?lU-t24>Q`1p36T{Q+6X-^R1W#|> zf>6Ya!)WD-lV}TvxcwKu;Lx5iifIV=yu=U2F@q+iYBG@@BXJc?ojJmhcN78~7B-&#tl_w29)0Y3JSMn`OfM@Ix#y3CjJ zP*8{`5}4Zb_-|mS8iwJ)&^2^jL)Ub+ZQG6_6wW#OECA-tp39s5_G%v9_+?Vb9q6hT zU(kc0K_#D}WYp>KcakY)5sr&vX%s9QQwl6wA(wF}R!c|K2zaptXzHRh@&Poq?v3O1 zz*MafUKQ?yavf?@0wYkTI0}IUlN;OSBouaUq>z=Gm1yBXzM zn_S98Fw2cVP%Kz7TOeBn11T4M>ic+lC3AgX=U^b$NF~!0=x>{#rA;s~B+0s96`msO zfzwyO<^pIzm=n<%qQ=->Fk-MV3zc8h2y}vH5j=Er5hn#k5R8pkY9;8=;B_JJ8&!pi%ZAYk$G5jLSa4b0 z8mE@lSvjkfy3gjZu{?TN;t3jD6I7TGMM@4N<1ja>a@pGX>`x6tNg-2mm_o2PY|*nA z`se68zOBxb2V#9;VwoDhUYF;@QxqZr4=YWLZ+4h`dua; zP~)aaK_7@LHMDX{%2YDzTo$`zEni!TC(N2NtqDOW8tsh~+n zXQsI3y=QahFSjFI8`p7JzU(*#cJ3yZ%QXq0V;DM`uH(@g@4BX=>N?wZ>_8C;tIs$M zfW-@!aKjfL;LU$?996SUcTWr9po=!UKu3#C0ZCA`QC$_skd*QbJ!99AC{?%7uSd%Z zIP&3dZ2Joup{@+Y3CFh81lJJ~N23dy1i<8s#=< zSt#+hR*4=L!cQbIgnW150|TH4RyS7%3m44i2S2=vul?&A*}ij%x>w-$ z_y`3h;jR#oPM6f445hk*&nGaXO0Y$xD622q`7b0uBgAi7;#HAI8`1|sCc>uRmF!wRNy-Tavk*B`#YzpLkoA;o%_;)CK-vike|yZE!hfSsSA(Dm?vc z278A_`%=g)lGv(2xK%PRSYxUnX<4SyRuEitz89Y+7%W=&x9b?o3?A8*Bw*Xr3MzL9 zhp=DJW-1gk$;vofx(v1)bQw;n{48UW-e1RKK~X9M7Ft-YM6qEq54H!Q?HanO;5sfr z6%HmO4?ek`PyFLmeDnHW;kquibXXwa;GqL#GC4H0F#$9U6T>tx3=_R+4OCrc=W_$7 zLgBPiR{?P5DHk9vL0t5>o_yL6$6tFzIAD zK5dchDwD4Hc-_J_p4~V~vEULqS#bT|c5>%!NqYKCI%n0XFNdPU!!JUMi>g4teEH6wAwaXPY;)w7hjXo@OVx#R9p)>7mMZJ{9D%>$XNhv3Z zbXkn4E(f0$lyYU(S0&ycM1c!(VIq{-UA1{~!y!r)SpQI*)8=__`W!j~Dyh6hCSGT1 z5~h+0C$4Y_cf-_fmF2M@p#?GQcpP(3WnOm=cWl~+QW8W@^pcCix!y2;cR3ir8vlMz zj%|Adt4^0hN`k2aI+;M7yboH&pg-J5W9xDS9g0g`S5Y-VYeYi`m#S)$%n6jb#2-+o zIiObr86-FU_=^OBVJ=#8H7*j@X+-Iaj*XGdWKpL(T-Wd$CO*H3-W(28MP+dJAgZdc zYGosM_UzRcP^quun?JddfUky(IB1$lC~FZ6=#*8Lc&<*wr#6CjdHGnHBbR}K<^)hQ z6|d@61y2Bieo%ZWstRV!AzKg}It0ohunvGsDRf;S*`KUX+6=M)+HA>iT~f=`@rA+E z6_~0s@HF)IyC?y{_yL zw!7ewB-1V)RnRd@qhQ*!MqL!$Bs-;0>9+ZQsT?)COxtq70wqM$KZ`fyB6OOPbvw4P zJy9p-%Q3h_p`@1K6uA0$L5Hg`;d7bVU!~Pn=-!c%1ZP8eLUqh6B@V z%n5W|N7qzTU1jI)L6DMFC$9wHqH`|>_dmJe#_?A3c2J_ z1F4!8;P5!mhyrX@f^;1lt*La7vm}|ai&K#J%b*EKdbeQSLY0=|Z1M>MWv@deFF=K4 zM&hk17$HgC0y_=n9LYIvG$>{*6jxB2PVWXP@v5AaBxqNa#_+jS0Q7#wDubKOh1)ei9@3mPFU{cP~4`xvqWk{kh3xr=ReKf zmMJDiB_UU`c%H(rzmDY!s(VdZG?!$|VQ;}DXbMWbDuGRcykk?ECFnR_(5vW-#%*F@ z6{qMB4H=D^$#oT_=%5RM)pVpeWe^q^t{@WdP^db5{hJ>`kt%D>`G?o!;dpWvj;CWR2f$ub}TJ zGk+eO&~2bZbT)3QvLvX{+V7y^GP1wK8b4;AEjT;Bw|WP4c}4d!MN= z%XT$xMyeDh1pzN44Sutwf$NaA=~1nojYcXe!^B$!dS z>Bf&ET*u51ctHX@IcsU(xBR3pMzYghx_3=lMeGb;l{)!4gdFG3NlIOzoZUN4L0 zuHXY7xQ(Cu;%(HF3Yk%tWZESV6nHHSlh+s&+Q|HAMgR&)dy9^$38pFzK@}|3Wy-Sg z`@uDxKMH}rcydnNU9lu zE+r9Dp#_CvRbo3fZLJECm={N_lgQMdEa~hta19+H>g3}RDi)De4IxzGqDFSg!ZeDw zArA%DA?nxgdo+9=gId)=6rsCEBj{Ce^crnJgJ9mmG(hQqct$X|)kRV27^ z@JU(%P;p(*AenQ?1CG`>CRMgky^_|JhGt-`1|k54=Ah>sL`70jZNC1kk5RK6)?V}$ zr0bG!lSGMkl9CS+Qw1Cenk#5=B~iahZ&c$@rH({0 ztJgqN>I6FkWxtpD{u)8yf~U+xMzS{nJ_k-+?89tT85yZCRRy#X1u3u;mxH^8$x8@z zWXO&~AqR4@PNWCQX_ynLLjiVI!1Y7548wO@m?oG3kX5Kof##76_kzC!l>$^J8#RPx z_czwi2eWlr+8W2#CLySUtyz>kpw#M&kAdCp642q>H+}?L+FW$u-vTc2c#@9x4zlSq ziDUv*Icy19TSByiLJdces-UVWd-v^U@#4k!JRW*`yE*@ycOn&sAOG%)ESTq|MbWUF zIwM(*ItzT+h#=dxFik(V-JEU|LR8#_cCBo93#Y47yF$*0JLw2qmBadYOe*nxc{*6@lWq2&JLVrwDcWBAVsmrnO!K!j(-0c-gEl)-2Q3R3&@y z5B%a^yjH6*GBV7>M4Ut-PPts+(82x8o;3$oLOhWq5{Zz`W=JLyjZ(d;imvIjwzkmH z(x~C2DJqIkxO@E{c*DgPuT>>;9f|&bWr|?k<|+c;ihvFS?eOB)~KYo*xx- zI2W*?ZPsYmJ)9wq+bp?KhZ$*8@e=A(Qr2;x5o9JBMV`bFZNBg)Yj`G{=GTp`ba4(d zOEx;ea;JTr&f)fGq^vdE8_rCQ;}Oa3&sTL402Z>D20wER$`MkoM@h4LrRrSJsKCS*;lyvGe%KzuT)lYtxO*OO$1K z0o^OsQ04Vm#igsG_2&IzFEVL)Yo)>YkCDzxST?yuN7(PFTK-CpSNXX_|yv+h*=d z%9oxD&Nzakl&;6)F}+^zi`e{1>^>|YHKQQWumCf?a>Q^PCT#`S5XvzkA8%UxfvjhTrdNzWf9NeujsK8Qi@KMJV+5_kj!X zWCFiGfMr>XkH=Ajf~u)>$6^?Uj-o(McMQ=?p1pVddak(qO#qy_Y9*VW*@9^pL?YoA zo!{&An$6p55J1y3*W>Z9b?eqm>(;F^EX$f6K3^MNFxi+P!BHTkd`&;ztGAiig4W{N51LINy+ml^XgHr*OhoYZf3Jt zBbiK^_uqfNdiB*;cb&LmWe8m2$s_`>u^68ipQ$X;)7^#IRPgMMMQ4obJ?rn|l8Y|{ zVAaYMJhNpRPi@}3E0dm#14t>Qs;cgW4I7>iLP#m4E2Nb8{eC472$;26O{-R`rlKeY zN2w)nv}MH$k9)aF6|c6v7Z$VpuR_9+HJy)WUJ5b21%8x&J>vaO4Fl)~+MDm*-~9Hs zZ#j9@DXWr+IPqi>+iIlA*R2{oUETEb_R!PQ{k#quk0%)!8DTP&n#n){c;LZ@dFrVr zFZ%rFK082TFs=hNA%rcZtc&RzS5;MLnuZX9Kp-Go_vj9UR}0&=vF)QLC%hV1mc^gy znvTS)u|EGD;=LNUu4@7@pamSPrpRl)fejj>LF8&hXF( zsmTUa;nn<2W-nBc+t$Pjz#DLNor?3WY}J_s~$E^S%H5?_d4- zfBeF`k0gO0V2kw99V{d)S760~9 zCq0Hg54gYxFa`_*<(qE0>G7|8`G%jwI%CA5F*IGJRdS;&H?w&3j$6;t>rm0*ele^`mPI$}${Q2QXOE3atZ~ET%9{ zH~b{p86_6$#LzV=)hezlsaC6ujEpimJc8vk++3=n5bKULJc7W_Z~f(GZ~E?yhhFMr z$L0WkzL=g9M1Wa9+xxG*cJ)U;`ti4xN(Em$o}yl>A~@g&u1 z8N=hj*Y0VB*Z=?k>`6pHR3t%bYb*Qr?|=9Wm%RRyz*d^o6^;?$e>bKrK^M>&jYelZ z@#K@AE)+_>csx$2T*hM>D5}Qo>(>43CpZ7-Cg4d97b!bNfWH95Xm||!fKDK=>8Yna z?eqEi;_(D~_U(^b M07*qoM6N<$f~P4+xc~qF literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 33b36cf6ec..81ee82f2b6 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -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) diff --git a/src/calibre/gui2/viewer/image_popup.py b/src/calibre/gui2/viewer/image_popup.py index 67e9831a52..b0842a7d9c 100644 --- a/src/calibre/gui2/viewer/image_popup.py +++ b/src/calibre/gui2/viewer/image_popup.py @@ -8,7 +8,8 @@ __copyright__ = '2012, Kovid Goyal ' __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) From 765f5030455a9a897d8d1677a40935079a2baeec Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Nov 2012 12:44:35 +0530 Subject: [PATCH 39/40] Ebook viewer image popup: ALlow using the mouse wheel to zoom the image --- src/calibre/gui2/viewer/image_popup.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/calibre/gui2/viewer/image_popup.py b/src/calibre/gui2/viewer/image_popup.py index b0842a7d9c..075143f3c3 100644 --- a/src/calibre/gui2/viewer/image_popup.py +++ b/src/calibre/gui2/viewer/image_popup.py @@ -105,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): @@ -126,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_() From d6c15c3b5c6fd33252635b835955afc17bb04200 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Nov 2012 23:44:23 +0530 Subject: [PATCH 40/40] Delco Times by Krittika Goyal --- recipes/delco_times.recipe | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 recipes/delco_times.recipe diff --git a/recipes/delco_times.recipe b/recipes/delco_times.recipe new file mode 100644 index 0000000000..6c163bd3e5 --- /dev/null +++ b/recipes/delco_times.recipe @@ -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'), +] +