michael@0: /* GRAPHITE2 LICENSING michael@0: michael@0: Copyright 2012, SIL International michael@0: All rights reserved. michael@0: michael@0: This library is free software; you can redistribute it and/or modify michael@0: it under the terms of the GNU Lesser General Public License as published michael@0: by the Free Software Foundation; either version 2.1 of License, or michael@0: (at your option) any later version. michael@0: michael@0: This program is distributed in the hope that it will be useful, michael@0: but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: Lesser General Public License for more details. michael@0: michael@0: You should also have received a copy of the GNU Lesser General Public michael@0: License along with this library in the file named "LICENSE". michael@0: If not, write to the Free Software Foundation, 51 Franklin Street, michael@0: Suite 500, Boston, MA 02110-1335, USA or visit their web page on the michael@0: internet at http://www.fsf.org/licenses/lgpl.html. michael@0: michael@0: Alternatively, the contents of this file may be used under the terms of the michael@0: Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public michael@0: License, as published by the Free Software Foundation, either version 2 michael@0: of the License or (at your option) any later version. michael@0: */ michael@0: #include "graphite2/Font.h" michael@0: michael@0: #include "inc/Main.h" michael@0: #include "inc/Face.h" //for the tags michael@0: #include "inc/GlyphCache.h" michael@0: #include "inc/GlyphFace.h" michael@0: #include "inc/Endian.h" michael@0: michael@0: using namespace graphite2; michael@0: michael@0: namespace michael@0: { michael@0: // Iterator over version 1 or 2 glat entries which consist of a series of michael@0: // +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ michael@0: // v1 |k|n|v1 |v2 |...|vN | or v2 | k | n |v1 |v2 |...|vN | michael@0: // +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ michael@0: // variable length structures. michael@0: michael@0: template michael@0: class _glat_iterator : public std::iterator > michael@0: { michael@0: unsigned short key() const { return be::peek(_e) + _n; } michael@0: unsigned int run() const { return be::peek(_e+sizeof(W)); } michael@0: void advance_entry() { _n = 0; _e = _v; be::skip(_v,2); } michael@0: public: michael@0: _glat_iterator(const void * glat=0) : _e(reinterpret_cast(glat)), _v(_e+2*sizeof(W)), _n(0) {} michael@0: michael@0: _glat_iterator & operator ++ () { michael@0: ++_n; be::skip(_v); michael@0: if (_n == run()) advance_entry(); michael@0: return *this; michael@0: } michael@0: _glat_iterator operator ++ (int) { _glat_iterator tmp(*this); operator++(); return tmp; } michael@0: michael@0: // This is strictly a >= operator. A true == operator could be michael@0: // implemented that test for overlap but it would be more expensive a michael@0: // test. michael@0: bool operator == (const _glat_iterator & rhs) { return _v >= rhs._e; } michael@0: bool operator != (const _glat_iterator & rhs) { return !operator==(rhs); } michael@0: michael@0: value_type operator * () const { michael@0: return value_type(key(), be::peek(_v)); michael@0: } michael@0: michael@0: protected: michael@0: const byte * _e, * _v; michael@0: ptrdiff_t _n; michael@0: }; michael@0: michael@0: typedef _glat_iterator glat_iterator; michael@0: typedef _glat_iterator glat2_iterator; michael@0: } michael@0: michael@0: michael@0: class GlyphCache::Loader michael@0: { michael@0: public: michael@0: Loader(const Face & face, const bool dumb_font); //return result indicates success. Do not use if failed. michael@0: michael@0: operator bool () const throw(); michael@0: unsigned short int units_per_em() const throw(); michael@0: unsigned short int num_glyphs() const throw(); michael@0: unsigned short int num_attrs() const throw(); michael@0: michael@0: const GlyphFace * read_glyph(unsigned short gid, GlyphFace &) const throw(); michael@0: michael@0: CLASS_NEW_DELETE; michael@0: private: michael@0: Face::Table _head, michael@0: _hhea, michael@0: _hmtx, michael@0: _glyf, michael@0: _loca, michael@0: m_pGlat, michael@0: m_pGloc; michael@0: michael@0: bool _long_fmt; michael@0: unsigned short _num_glyphs_graphics, //i.e. boundary box and advance michael@0: _num_glyphs_attributes, michael@0: _num_attrs; // number of glyph attributes per glyph michael@0: }; michael@0: michael@0: michael@0: michael@0: GlyphCache::GlyphCache(const Face & face, const uint32 face_options) michael@0: : _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))), michael@0: _glyphs(_glyph_loader && *_glyph_loader ? grzeroalloc(_glyph_loader->num_glyphs()) : 0), michael@0: _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0), michael@0: _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0), michael@0: _upem(_glyphs ? _glyph_loader->units_per_em() : 0) michael@0: { michael@0: if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs) michael@0: { michael@0: GlyphFace * const glyphs = new GlyphFace [_num_glyphs]; michael@0: if (!glyphs) michael@0: return; michael@0: michael@0: // The 0 glyph is definately required. michael@0: _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0]); michael@0: michael@0: // glyphs[0] has the same address as the glyphs array just allocated, michael@0: // thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points michael@0: // to the entire array. michael@0: const GlyphFace * loaded = _glyphs[0]; michael@0: for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid) michael@0: _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid]); michael@0: michael@0: if (!loaded) michael@0: { michael@0: _glyphs[0] = 0; michael@0: delete [] glyphs; michael@0: } michael@0: delete _glyph_loader; michael@0: _glyph_loader = 0; michael@0: } michael@0: michael@0: if (_glyphs && glyph(0) == 0) michael@0: { michael@0: free(_glyphs); michael@0: _glyphs = 0; michael@0: _num_glyphs = _num_attrs = _upem = 0; michael@0: } michael@0: } michael@0: michael@0: michael@0: GlyphCache::~GlyphCache() michael@0: { michael@0: if (_glyphs) michael@0: { michael@0: if (_glyph_loader) michael@0: { michael@0: const GlyphFace * * g = _glyphs; michael@0: for(unsigned short n = _num_glyphs; n; --n, ++g) michael@0: delete *g; michael@0: } michael@0: else michael@0: delete [] _glyphs[0]; michael@0: free(_glyphs); michael@0: } michael@0: delete _glyph_loader; michael@0: } michael@0: michael@0: const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result may be changed by subsequent call with a different glyphid michael@0: { michael@0: const GlyphFace * & p = _glyphs[glyphid]; michael@0: if (p == 0 && _glyph_loader) michael@0: { michael@0: GlyphFace * g = new GlyphFace(); michael@0: if (g) p = _glyph_loader->read_glyph(glyphid, *g); michael@0: if (!p) michael@0: { michael@0: delete g; michael@0: return *_glyphs; michael@0: } michael@0: } michael@0: return p; michael@0: } michael@0: michael@0: michael@0: michael@0: GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) michael@0: : _head(face, Tag::head), michael@0: _hhea(face, Tag::hhea), michael@0: _hmtx(face, Tag::hmtx), michael@0: _glyf(face, Tag::glyf), michael@0: _loca(face, Tag::loca), michael@0: _long_fmt(false), michael@0: _num_glyphs_graphics(0), michael@0: _num_glyphs_attributes(0), michael@0: _num_attrs(0) michael@0: { michael@0: if (!operator bool()) michael@0: return; michael@0: michael@0: const Face::Table maxp = Face::Table(face, Tag::maxp); michael@0: if (!maxp) { _head = Face::Table(); return; } michael@0: michael@0: _num_glyphs_graphics = TtfUtil::GlyphCount(maxp); michael@0: // This will fail if the number of glyphs is wildly out of range. michael@0: if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1)) michael@0: { michael@0: _head = Face::Table(); michael@0: return; michael@0: } michael@0: michael@0: if (!dumb_font) michael@0: { michael@0: if ((m_pGlat = Face::Table(face, Tag::Glat)) == NULL michael@0: || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL michael@0: || m_pGloc.size() < 6) michael@0: { michael@0: _head = Face::Table(); michael@0: return; michael@0: } michael@0: const byte * p = m_pGloc; michael@0: const int version = be::read(p); michael@0: const uint16 flags = be::read(p); michael@0: _num_attrs = be::read(p); michael@0: // We can accurately calculate the number of attributed glyphs by michael@0: // subtracting the length of the attribids array (numAttribs long if present) michael@0: // and dividing by either 2 or 4 depending on shor or lonf format michael@0: _long_fmt = flags & 1; michael@0: _num_glyphs_attributes = (m_pGloc.size() michael@0: - (p - m_pGloc) michael@0: - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0)) michael@0: / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1; michael@0: michael@0: if (version != 0x00010000 michael@0: || _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate? michael@0: || _num_glyphs_graphics > _num_glyphs_attributes) michael@0: { michael@0: _head = Face::Table(); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: inline michael@0: GlyphCache::Loader::operator bool () const throw() michael@0: { michael@0: return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca)); michael@0: } michael@0: michael@0: inline michael@0: unsigned short int GlyphCache::Loader::units_per_em() const throw() michael@0: { michael@0: return _head ? TtfUtil::DesignUnits(_head) : 0; michael@0: } michael@0: michael@0: inline michael@0: unsigned short int GlyphCache::Loader::num_glyphs() const throw() michael@0: { michael@0: return max(_num_glyphs_graphics, _num_glyphs_attributes); michael@0: } michael@0: michael@0: inline michael@0: unsigned short int GlyphCache::Loader::num_attrs() const throw() michael@0: { michael@0: return _num_attrs; michael@0: } michael@0: michael@0: const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph) const throw() michael@0: { michael@0: Rect bbox; michael@0: Position advance; michael@0: michael@0: if (glyphid < _num_glyphs_graphics) michael@0: { michael@0: int nLsb; michael@0: unsigned int nAdvWid; michael@0: if (_glyf) michael@0: { michael@0: int xMin, yMin, xMax, yMax; michael@0: size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head); michael@0: void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size()); michael@0: michael@0: if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax)) michael@0: bbox = Rect(Position(static_cast(xMin), static_cast(yMin)), michael@0: Position(static_cast(xMax), static_cast(yMax))); michael@0: } michael@0: if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid)) michael@0: advance = Position(static_cast(nAdvWid), 0); michael@0: } michael@0: michael@0: if (glyphid < _num_glyphs_attributes) michael@0: { michael@0: const byte * gloc = m_pGloc; michael@0: size_t glocs = 0, gloce = 0; michael@0: michael@0: be::skip(gloc); michael@0: be::skip(gloc,2); michael@0: if (_long_fmt) michael@0: { michael@0: be::skip(gloc, glyphid); michael@0: glocs = be::read(gloc); michael@0: gloce = be::peek(gloc); michael@0: } michael@0: else michael@0: { michael@0: be::skip(gloc, glyphid); michael@0: glocs = be::read(gloc); michael@0: gloce = be::peek(gloc); michael@0: } michael@0: michael@0: if (glocs >= m_pGlat.size() || gloce > m_pGlat.size()) michael@0: return 0; michael@0: michael@0: const uint32 glat_version = be::peek(m_pGlat); michael@0: if (glat_version < 0x00020000) michael@0: { michael@0: if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16) michael@0: || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16))) michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce)); michael@0: } michael@0: else michael@0: { michael@0: if (gloce - glocs < 3*sizeof(uint16) michael@0: || gloce - glocs > _num_attrs*3*sizeof(uint16)) michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce)); michael@0: } michael@0: michael@0: if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs) michael@0: return 0; michael@0: } michael@0: michael@0: return &glyph; michael@0: }