Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* GRAPHITE2 LICENSING |
michael@0 | 2 | |
michael@0 | 3 | Copyright 2012, SIL International |
michael@0 | 4 | All rights reserved. |
michael@0 | 5 | |
michael@0 | 6 | This library is free software; you can redistribute it and/or modify |
michael@0 | 7 | it under the terms of the GNU Lesser General Public License as published |
michael@0 | 8 | by the Free Software Foundation; either version 2.1 of License, or |
michael@0 | 9 | (at your option) any later version. |
michael@0 | 10 | |
michael@0 | 11 | This program is distributed in the hope that it will be useful, |
michael@0 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
michael@0 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
michael@0 | 14 | Lesser General Public License for more details. |
michael@0 | 15 | |
michael@0 | 16 | You should also have received a copy of the GNU Lesser General Public |
michael@0 | 17 | License along with this library in the file named "LICENSE". |
michael@0 | 18 | If not, write to the Free Software Foundation, 51 Franklin Street, |
michael@0 | 19 | Suite 500, Boston, MA 02110-1335, USA or visit their web page on the |
michael@0 | 20 | internet at http://www.fsf.org/licenses/lgpl.html. |
michael@0 | 21 | |
michael@0 | 22 | Alternatively, the contents of this file may be used under the terms of the |
michael@0 | 23 | Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public |
michael@0 | 24 | License, as published by the Free Software Foundation, either version 2 |
michael@0 | 25 | of the License or (at your option) any later version. |
michael@0 | 26 | */ |
michael@0 | 27 | #include "graphite2/Font.h" |
michael@0 | 28 | |
michael@0 | 29 | #include "inc/Main.h" |
michael@0 | 30 | #include "inc/Face.h" //for the tags |
michael@0 | 31 | #include "inc/GlyphCache.h" |
michael@0 | 32 | #include "inc/GlyphFace.h" |
michael@0 | 33 | #include "inc/Endian.h" |
michael@0 | 34 | |
michael@0 | 35 | using namespace graphite2; |
michael@0 | 36 | |
michael@0 | 37 | namespace |
michael@0 | 38 | { |
michael@0 | 39 | // Iterator over version 1 or 2 glat entries which consist of a series of |
michael@0 | 40 | // +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ |
michael@0 | 41 | // v1 |k|n|v1 |v2 |...|vN | or v2 | k | n |v1 |v2 |...|vN | |
michael@0 | 42 | // +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ |
michael@0 | 43 | // variable length structures. |
michael@0 | 44 | |
michael@0 | 45 | template<typename W> |
michael@0 | 46 | class _glat_iterator : public std::iterator<std::input_iterator_tag, std::pair<sparse::key_type, sparse::mapped_type> > |
michael@0 | 47 | { |
michael@0 | 48 | unsigned short key() const { return be::peek<W>(_e) + _n; } |
michael@0 | 49 | unsigned int run() const { return be::peek<W>(_e+sizeof(W)); } |
michael@0 | 50 | void advance_entry() { _n = 0; _e = _v; be::skip<W>(_v,2); } |
michael@0 | 51 | public: |
michael@0 | 52 | _glat_iterator(const void * glat=0) : _e(reinterpret_cast<const byte *>(glat)), _v(_e+2*sizeof(W)), _n(0) {} |
michael@0 | 53 | |
michael@0 | 54 | _glat_iterator<W> & operator ++ () { |
michael@0 | 55 | ++_n; be::skip<uint16>(_v); |
michael@0 | 56 | if (_n == run()) advance_entry(); |
michael@0 | 57 | return *this; |
michael@0 | 58 | } |
michael@0 | 59 | _glat_iterator<W> operator ++ (int) { _glat_iterator<W> tmp(*this); operator++(); return tmp; } |
michael@0 | 60 | |
michael@0 | 61 | // This is strictly a >= operator. A true == operator could be |
michael@0 | 62 | // implemented that test for overlap but it would be more expensive a |
michael@0 | 63 | // test. |
michael@0 | 64 | bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e; } |
michael@0 | 65 | bool operator != (const _glat_iterator<W> & rhs) { return !operator==(rhs); } |
michael@0 | 66 | |
michael@0 | 67 | value_type operator * () const { |
michael@0 | 68 | return value_type(key(), be::peek<uint16>(_v)); |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | protected: |
michael@0 | 72 | const byte * _e, * _v; |
michael@0 | 73 | ptrdiff_t _n; |
michael@0 | 74 | }; |
michael@0 | 75 | |
michael@0 | 76 | typedef _glat_iterator<uint8> glat_iterator; |
michael@0 | 77 | typedef _glat_iterator<uint16> glat2_iterator; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | |
michael@0 | 81 | class GlyphCache::Loader |
michael@0 | 82 | { |
michael@0 | 83 | public: |
michael@0 | 84 | Loader(const Face & face, const bool dumb_font); //return result indicates success. Do not use if failed. |
michael@0 | 85 | |
michael@0 | 86 | operator bool () const throw(); |
michael@0 | 87 | unsigned short int units_per_em() const throw(); |
michael@0 | 88 | unsigned short int num_glyphs() const throw(); |
michael@0 | 89 | unsigned short int num_attrs() const throw(); |
michael@0 | 90 | |
michael@0 | 91 | const GlyphFace * read_glyph(unsigned short gid, GlyphFace &) const throw(); |
michael@0 | 92 | |
michael@0 | 93 | CLASS_NEW_DELETE; |
michael@0 | 94 | private: |
michael@0 | 95 | Face::Table _head, |
michael@0 | 96 | _hhea, |
michael@0 | 97 | _hmtx, |
michael@0 | 98 | _glyf, |
michael@0 | 99 | _loca, |
michael@0 | 100 | m_pGlat, |
michael@0 | 101 | m_pGloc; |
michael@0 | 102 | |
michael@0 | 103 | bool _long_fmt; |
michael@0 | 104 | unsigned short _num_glyphs_graphics, //i.e. boundary box and advance |
michael@0 | 105 | _num_glyphs_attributes, |
michael@0 | 106 | _num_attrs; // number of glyph attributes per glyph |
michael@0 | 107 | }; |
michael@0 | 108 | |
michael@0 | 109 | |
michael@0 | 110 | |
michael@0 | 111 | GlyphCache::GlyphCache(const Face & face, const uint32 face_options) |
michael@0 | 112 | : _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))), |
michael@0 | 113 | _glyphs(_glyph_loader && *_glyph_loader ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0), |
michael@0 | 114 | _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0), |
michael@0 | 115 | _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0), |
michael@0 | 116 | _upem(_glyphs ? _glyph_loader->units_per_em() : 0) |
michael@0 | 117 | { |
michael@0 | 118 | if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs) |
michael@0 | 119 | { |
michael@0 | 120 | GlyphFace * const glyphs = new GlyphFace [_num_glyphs]; |
michael@0 | 121 | if (!glyphs) |
michael@0 | 122 | return; |
michael@0 | 123 | |
michael@0 | 124 | // The 0 glyph is definately required. |
michael@0 | 125 | _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0]); |
michael@0 | 126 | |
michael@0 | 127 | // glyphs[0] has the same address as the glyphs array just allocated, |
michael@0 | 128 | // thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points |
michael@0 | 129 | // to the entire array. |
michael@0 | 130 | const GlyphFace * loaded = _glyphs[0]; |
michael@0 | 131 | for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid) |
michael@0 | 132 | _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid]); |
michael@0 | 133 | |
michael@0 | 134 | if (!loaded) |
michael@0 | 135 | { |
michael@0 | 136 | _glyphs[0] = 0; |
michael@0 | 137 | delete [] glyphs; |
michael@0 | 138 | } |
michael@0 | 139 | delete _glyph_loader; |
michael@0 | 140 | _glyph_loader = 0; |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | if (_glyphs && glyph(0) == 0) |
michael@0 | 144 | { |
michael@0 | 145 | free(_glyphs); |
michael@0 | 146 | _glyphs = 0; |
michael@0 | 147 | _num_glyphs = _num_attrs = _upem = 0; |
michael@0 | 148 | } |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | |
michael@0 | 152 | GlyphCache::~GlyphCache() |
michael@0 | 153 | { |
michael@0 | 154 | if (_glyphs) |
michael@0 | 155 | { |
michael@0 | 156 | if (_glyph_loader) |
michael@0 | 157 | { |
michael@0 | 158 | const GlyphFace * * g = _glyphs; |
michael@0 | 159 | for(unsigned short n = _num_glyphs; n; --n, ++g) |
michael@0 | 160 | delete *g; |
michael@0 | 161 | } |
michael@0 | 162 | else |
michael@0 | 163 | delete [] _glyphs[0]; |
michael@0 | 164 | free(_glyphs); |
michael@0 | 165 | } |
michael@0 | 166 | delete _glyph_loader; |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result may be changed by subsequent call with a different glyphid |
michael@0 | 170 | { |
michael@0 | 171 | const GlyphFace * & p = _glyphs[glyphid]; |
michael@0 | 172 | if (p == 0 && _glyph_loader) |
michael@0 | 173 | { |
michael@0 | 174 | GlyphFace * g = new GlyphFace(); |
michael@0 | 175 | if (g) p = _glyph_loader->read_glyph(glyphid, *g); |
michael@0 | 176 | if (!p) |
michael@0 | 177 | { |
michael@0 | 178 | delete g; |
michael@0 | 179 | return *_glyphs; |
michael@0 | 180 | } |
michael@0 | 181 | } |
michael@0 | 182 | return p; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | |
michael@0 | 186 | |
michael@0 | 187 | GlyphCache::Loader::Loader(const Face & face, const bool dumb_font) |
michael@0 | 188 | : _head(face, Tag::head), |
michael@0 | 189 | _hhea(face, Tag::hhea), |
michael@0 | 190 | _hmtx(face, Tag::hmtx), |
michael@0 | 191 | _glyf(face, Tag::glyf), |
michael@0 | 192 | _loca(face, Tag::loca), |
michael@0 | 193 | _long_fmt(false), |
michael@0 | 194 | _num_glyphs_graphics(0), |
michael@0 | 195 | _num_glyphs_attributes(0), |
michael@0 | 196 | _num_attrs(0) |
michael@0 | 197 | { |
michael@0 | 198 | if (!operator bool()) |
michael@0 | 199 | return; |
michael@0 | 200 | |
michael@0 | 201 | const Face::Table maxp = Face::Table(face, Tag::maxp); |
michael@0 | 202 | if (!maxp) { _head = Face::Table(); return; } |
michael@0 | 203 | |
michael@0 | 204 | _num_glyphs_graphics = TtfUtil::GlyphCount(maxp); |
michael@0 | 205 | // This will fail if the number of glyphs is wildly out of range. |
michael@0 | 206 | if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1)) |
michael@0 | 207 | { |
michael@0 | 208 | _head = Face::Table(); |
michael@0 | 209 | return; |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | if (!dumb_font) |
michael@0 | 213 | { |
michael@0 | 214 | if ((m_pGlat = Face::Table(face, Tag::Glat)) == NULL |
michael@0 | 215 | || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL |
michael@0 | 216 | || m_pGloc.size() < 6) |
michael@0 | 217 | { |
michael@0 | 218 | _head = Face::Table(); |
michael@0 | 219 | return; |
michael@0 | 220 | } |
michael@0 | 221 | const byte * p = m_pGloc; |
michael@0 | 222 | const int version = be::read<uint32>(p); |
michael@0 | 223 | const uint16 flags = be::read<uint16>(p); |
michael@0 | 224 | _num_attrs = be::read<uint16>(p); |
michael@0 | 225 | // We can accurately calculate the number of attributed glyphs by |
michael@0 | 226 | // subtracting the length of the attribids array (numAttribs long if present) |
michael@0 | 227 | // and dividing by either 2 or 4 depending on shor or lonf format |
michael@0 | 228 | _long_fmt = flags & 1; |
michael@0 | 229 | _num_glyphs_attributes = (m_pGloc.size() |
michael@0 | 230 | - (p - m_pGloc) |
michael@0 | 231 | - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0)) |
michael@0 | 232 | / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1; |
michael@0 | 233 | |
michael@0 | 234 | if (version != 0x00010000 |
michael@0 | 235 | || _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate? |
michael@0 | 236 | || _num_glyphs_graphics > _num_glyphs_attributes) |
michael@0 | 237 | { |
michael@0 | 238 | _head = Face::Table(); |
michael@0 | 239 | return; |
michael@0 | 240 | } |
michael@0 | 241 | } |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | inline |
michael@0 | 245 | GlyphCache::Loader::operator bool () const throw() |
michael@0 | 246 | { |
michael@0 | 247 | return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca)); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | inline |
michael@0 | 251 | unsigned short int GlyphCache::Loader::units_per_em() const throw() |
michael@0 | 252 | { |
michael@0 | 253 | return _head ? TtfUtil::DesignUnits(_head) : 0; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | inline |
michael@0 | 257 | unsigned short int GlyphCache::Loader::num_glyphs() const throw() |
michael@0 | 258 | { |
michael@0 | 259 | return max(_num_glyphs_graphics, _num_glyphs_attributes); |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | inline |
michael@0 | 263 | unsigned short int GlyphCache::Loader::num_attrs() const throw() |
michael@0 | 264 | { |
michael@0 | 265 | return _num_attrs; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph) const throw() |
michael@0 | 269 | { |
michael@0 | 270 | Rect bbox; |
michael@0 | 271 | Position advance; |
michael@0 | 272 | |
michael@0 | 273 | if (glyphid < _num_glyphs_graphics) |
michael@0 | 274 | { |
michael@0 | 275 | int nLsb; |
michael@0 | 276 | unsigned int nAdvWid; |
michael@0 | 277 | if (_glyf) |
michael@0 | 278 | { |
michael@0 | 279 | int xMin, yMin, xMax, yMax; |
michael@0 | 280 | size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head); |
michael@0 | 281 | void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size()); |
michael@0 | 282 | |
michael@0 | 283 | if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax)) |
michael@0 | 284 | bbox = Rect(Position(static_cast<float>(xMin), static_cast<float>(yMin)), |
michael@0 | 285 | Position(static_cast<float>(xMax), static_cast<float>(yMax))); |
michael@0 | 286 | } |
michael@0 | 287 | if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid)) |
michael@0 | 288 | advance = Position(static_cast<float>(nAdvWid), 0); |
michael@0 | 289 | } |
michael@0 | 290 | |
michael@0 | 291 | if (glyphid < _num_glyphs_attributes) |
michael@0 | 292 | { |
michael@0 | 293 | const byte * gloc = m_pGloc; |
michael@0 | 294 | size_t glocs = 0, gloce = 0; |
michael@0 | 295 | |
michael@0 | 296 | be::skip<uint32>(gloc); |
michael@0 | 297 | be::skip<uint16>(gloc,2); |
michael@0 | 298 | if (_long_fmt) |
michael@0 | 299 | { |
michael@0 | 300 | be::skip<uint32>(gloc, glyphid); |
michael@0 | 301 | glocs = be::read<uint32>(gloc); |
michael@0 | 302 | gloce = be::peek<uint32>(gloc); |
michael@0 | 303 | } |
michael@0 | 304 | else |
michael@0 | 305 | { |
michael@0 | 306 | be::skip<uint16>(gloc, glyphid); |
michael@0 | 307 | glocs = be::read<uint16>(gloc); |
michael@0 | 308 | gloce = be::peek<uint16>(gloc); |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | if (glocs >= m_pGlat.size() || gloce > m_pGlat.size()) |
michael@0 | 312 | return 0; |
michael@0 | 313 | |
michael@0 | 314 | const uint32 glat_version = be::peek<uint32>(m_pGlat); |
michael@0 | 315 | if (glat_version < 0x00020000) |
michael@0 | 316 | { |
michael@0 | 317 | if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16) |
michael@0 | 318 | || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16))) |
michael@0 | 319 | { |
michael@0 | 320 | return 0; |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce)); |
michael@0 | 324 | } |
michael@0 | 325 | else |
michael@0 | 326 | { |
michael@0 | 327 | if (gloce - glocs < 3*sizeof(uint16) |
michael@0 | 328 | || gloce - glocs > _num_attrs*3*sizeof(uint16)) |
michael@0 | 329 | { |
michael@0 | 330 | return 0; |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce)); |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs) |
michael@0 | 337 | return 0; |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | return &glyph; |
michael@0 | 341 | } |