Path: blob/master/thirdparty/graphite/src/GlyphCache.cpp
9902 views
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later1// Copyright 2012, SIL International, All rights reserved.23#include "graphite2/Font.h"45#include "inc/Main.h"6#include "inc/Face.h" //for the tags7#include "inc/GlyphCache.h"8#include "inc/GlyphFace.h"9#include "inc/Endian.h"10#include "inc/bits.h"1112using namespace graphite2;1314namespace15{16// Iterator over version 1 or 2 glat entries which consist of a series of17// +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+18// v1 |k|n|v1 |v2 |...|vN | or v2 | k | n |v1 |v2 |...|vN |19// +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+20// variable length structures.2122template<typename W>23class _glat_iterator24{25unsigned short key() const { return uint16(be::peek<W>(_e) + _n); }26unsigned int run() const { return be::peek<W>(_e+sizeof(W)); }27void advance_entry() { _n = 0; _e = _v; be::skip<W>(_v,2); }28public:29using iterator_category = std::input_iterator_tag;30using value_type = std::pair<sparse::key_type, sparse::mapped_type>;31using difference_type = ptrdiff_t;32using pointer = value_type *;33using reference = value_type &;3435_glat_iterator(const void * glat=0) : _e(reinterpret_cast<const byte *>(glat)), _v(_e+2*sizeof(W)), _n(0) {}3637_glat_iterator<W> & operator ++ () {38++_n; be::skip<uint16>(_v);39if (_n == run()) advance_entry();40return *this;41}42_glat_iterator<W> operator ++ (int) { _glat_iterator<W> tmp(*this); operator++(); return tmp; }4344// This is strictly a >= operator. A true == operator could be45// implemented that test for overlap but it would be more expensive a46// test.47bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e - 1; }48bool operator != (const _glat_iterator<W> & rhs) { return !operator==(rhs); }4950value_type operator * () const {51return value_type(key(), be::peek<uint16>(_v));52}5354protected:55const byte * _e, * _v;56size_t _n;57};5859typedef _glat_iterator<uint8> glat_iterator;60typedef _glat_iterator<uint16> glat2_iterator;61}6263const SlantBox SlantBox::empty = {0,0,0,0};646566class GlyphCache::Loader67{68public:69Loader(const Face & face); //return result indicates success. Do not use if failed.7071operator bool () const throw();72unsigned short int units_per_em() const throw();73unsigned short int num_glyphs() const throw();74unsigned short int num_attrs() const throw();75bool has_boxes() const throw();7677const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw();78GlyphBox * read_box(uint16 gid, GlyphBox *curr, const GlyphFace & face) const throw();7980CLASS_NEW_DELETE;81private:82Face::Table _head,83_hhea,84_hmtx,85_glyf,86_loca,87m_pGlat,88m_pGloc;8990bool _long_fmt;91bool _has_boxes;92unsigned short _num_glyphs_graphics, //i.e. boundary box and advance93_num_glyphs_attributes,94_num_attrs; // number of glyph attributes per glyph95};96979899GlyphCache::GlyphCache(const Face & face, const uint32 face_options)100: _glyph_loader(new Loader(face)),101_glyphs(_glyph_loader && *_glyph_loader && _glyph_loader->num_glyphs()102? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),103_boxes(_glyph_loader && _glyph_loader->has_boxes() && _glyph_loader->num_glyphs()104? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),105_num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0),106_num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0),107_upem(_glyphs ? _glyph_loader->units_per_em() : 0)108{109if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs)110{111int numsubs = 0;112GlyphFace * const glyphs = new GlyphFace [_num_glyphs];113if (!glyphs)114return;115116// The 0 glyph is definately required.117_glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs);118119// glyphs[0] has the same address as the glyphs array just allocated,120// thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points121// to the entire array.122const GlyphFace * loaded = _glyphs[0];123for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid)124_glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs);125126if (!loaded)127{128_glyphs[0] = 0;129delete [] glyphs;130}131else if (numsubs > 0 && _boxes)132{133GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float));134GlyphBox * currbox = boxes;135136for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid)137{138_boxes[gid] = currbox;139currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]);140}141if (!currbox)142{143free(boxes);144_boxes[0] = 0;145}146}147delete _glyph_loader;148_glyph_loader = 0;149// coverity[leaked_storage : FALSE] - calling read_glyph on index 0 saved150// glyphs as _glyphs[0]. Setting _glyph_loader to nullptr here flags that151// the dtor needs to call delete[] on _glyphs[0] to release what was allocated152// as glyphs153}154155if (_glyphs && glyph(0) == 0)156{157free(_glyphs);158_glyphs = 0;159if (_boxes)160{161free(_boxes);162_boxes = 0;163}164_num_glyphs = _num_attrs = _upem = 0;165}166}167168169GlyphCache::~GlyphCache()170{171if (_glyphs)172{173if (_glyph_loader)174{175const GlyphFace * * g = _glyphs;176for(unsigned short n = _num_glyphs; n; --n, ++g)177delete *g;178}179else180delete [] _glyphs[0];181free(_glyphs);182}183if (_boxes)184{185if (_glyph_loader)186{187GlyphBox * * g = _boxes;188for (uint16 n = _num_glyphs; n; --n, ++g)189free(*g);190}191else192free(_boxes[0]);193free(_boxes);194}195delete _glyph_loader;196}197198const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result may be changed by subsequent call with a different glyphid199{200if (glyphid >= numGlyphs())201return _glyphs[0];202const GlyphFace * & p = _glyphs[glyphid];203if (p == 0 && _glyph_loader)204{205int numsubs = 0;206GlyphFace * g = new GlyphFace();207if (g) p = _glyph_loader->read_glyph(glyphid, *g, &numsubs);208if (!p)209{210delete g;211return *_glyphs;212}213if (_boxes)214{215_boxes[glyphid] = (GlyphBox *)gralloc<char>(sizeof(GlyphBox) + 8 * numsubs * sizeof(float));216if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid]))217{218free(_boxes[glyphid]);219_boxes[glyphid] = 0;220}221}222}223return p;224}225226227228GlyphCache::Loader::Loader(const Face & face)229: _head(face, Tag::head),230_hhea(face, Tag::hhea),231_hmtx(face, Tag::hmtx),232_glyf(face, Tag::glyf),233_loca(face, Tag::loca),234_long_fmt(false),235_has_boxes(false),236_num_glyphs_graphics(0),237_num_glyphs_attributes(0),238_num_attrs(0)239{240if (!operator bool())241return;242243const Face::Table maxp = Face::Table(face, Tag::maxp);244if (!maxp) { _head = Face::Table(); return; }245246_num_glyphs_graphics = static_cast<unsigned short>(TtfUtil::GlyphCount(maxp));247// This will fail if the number of glyphs is wildly out of range.248if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2))249{250_head = Face::Table();251return;252}253254if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL255|| (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL256|| m_pGloc.size() < 8)257{258_head = Face::Table();259return;260}261const byte * p = m_pGloc;262int version = be::read<uint32>(p);263const uint16 flags = be::read<uint16>(p);264_num_attrs = be::read<uint16>(p);265// We can accurately calculate the number of attributed glyphs by266// subtracting the length of the attribids array (numAttribs long if present)267// and dividing by either 2 or 4 depending on shor or lonf format268_long_fmt = flags & 1;269ptrdiff_t tmpnumgattrs = (m_pGloc.size()270- (p - m_pGloc)271- sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))272/ (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;273274if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535275|| _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate?276|| _num_glyphs_graphics > tmpnumgattrs277|| m_pGlat.size() < 4)278{279_head = Face::Table();280return;281}282283_num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);284p = m_pGlat;285version = be::read<uint32>(p);286if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8)) // reject Glat tables that are too new287{288_head = Face::Table();289return;290}291else if (version >= 0x00030000)292{293unsigned int glatflags = be::read<uint32>(p);294_has_boxes = glatflags & 1;295// delete this once the compiler is fixed296_has_boxes = true;297}298}299300inline301GlyphCache::Loader::operator bool () const throw()302{303return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca));304}305306inline307unsigned short int GlyphCache::Loader::units_per_em() const throw()308{309return _head ? TtfUtil::DesignUnits(_head) : 0;310}311312inline313unsigned short int GlyphCache::Loader::num_glyphs() const throw()314{315return max(_num_glyphs_graphics, _num_glyphs_attributes);316}317318inline319unsigned short int GlyphCache::Loader::num_attrs() const throw()320{321return _num_attrs;322}323324inline325bool GlyphCache::Loader::has_boxes () const throw()326{327return _has_boxes;328}329330const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw()331{332Rect bbox;333Position advance;334335if (glyphid < _num_glyphs_graphics)336{337int nLsb;338unsigned int nAdvWid;339if (_glyf)340{341int xMin, yMin, xMax, yMax;342size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head);343void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size());344345if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax))346{347if ((xMin > xMax) || (yMin > yMax))348return 0;349bbox = Rect(Position(static_cast<float>(xMin), static_cast<float>(yMin)),350Position(static_cast<float>(xMax), static_cast<float>(yMax)));351}352}353if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid))354advance = Position(static_cast<float>(nAdvWid), 0);355}356357if (glyphid < _num_glyphs_attributes)358{359const byte * gloc = m_pGloc;360size_t glocs = 0, gloce = 0;361362be::skip<uint32>(gloc);363be::skip<uint16>(gloc,2);364if (_long_fmt)365{366if (8 + glyphid * sizeof(uint32) > m_pGloc.size())367return 0;368be::skip<uint32>(gloc, glyphid);369glocs = be::read<uint32>(gloc);370gloce = be::peek<uint32>(gloc);371}372else373{374if (8 + glyphid * sizeof(uint16) > m_pGloc.size())375return 0;376be::skip<uint16>(gloc, glyphid);377glocs = be::read<uint16>(gloc);378gloce = be::peek<uint16>(gloc);379}380381if (glocs >= m_pGlat.size() - 1 || gloce > m_pGlat.size())382return 0;383384const uint32 glat_version = be::peek<uint32>(m_pGlat);385if (glat_version >= 0x00030000)386{387if (glocs >= gloce)388return 0;389const byte * p = m_pGlat + glocs;390uint16 bmap = be::read<uint16>(p);391int num = bit_set_count((uint32)bmap);392if (numsubs) *numsubs += num;393glocs += 6 + 8 * num;394if (glocs > gloce)395return 0;396}397if (glat_version < 0x00020000)398{399if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16)400|| gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16)))401return 0;402new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce));403}404else405{406if (gloce - glocs < 3*sizeof(uint16) // can a glyph have no attributes? why not?407|| gloce - glocs > _num_attrs*3*sizeof(uint16)408|| glocs > m_pGlat.size() - 2*sizeof(uint16))409return 0;410new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));411}412if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs)413return 0;414}415return &glyph;416}417418inline float scale_to(uint8 t, float zmin, float zmax)419{420return (zmin + t * (zmax - zmin) / 255);421}422423Rect readbox(Rect &b, uint8 zxmin, uint8 zymin, uint8 zxmax, uint8 zymax)424{425return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)),426Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y)));427}428429GlyphBox * GlyphCache::Loader::read_box(uint16 gid, GlyphBox *curr, const GlyphFace & glyph) const throw()430{431if (gid >= _num_glyphs_attributes) return 0;432433const byte * gloc = m_pGloc;434size_t glocs = 0, gloce = 0;435436be::skip<uint32>(gloc);437be::skip<uint16>(gloc,2);438if (_long_fmt)439{440be::skip<uint32>(gloc, gid);441glocs = be::read<uint32>(gloc);442gloce = be::peek<uint32>(gloc);443}444else445{446be::skip<uint16>(gloc, gid);447glocs = be::read<uint16>(gloc);448gloce = be::peek<uint16>(gloc);449}450451if (gloce > m_pGlat.size() || glocs + 6 >= gloce)452return 0;453454const byte * p = m_pGlat + glocs;455uint16 bmap = be::read<uint16>(p);456int num = bit_set_count((uint32)bmap);457458Rect bbox = glyph.theBBox();459Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y),460Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y));461Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]);462::new (curr) GlyphBox(num, bmap, &diabound);463be::skip<uint8>(p, 4);464if (glocs + 6 + num * 8 >= gloce)465return 0;466467for (int i = 0; i < num * 2; ++i)468{469Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]);470curr->addSubBox(i >> 1, i & 1, &box);471be::skip<uint8>(p, 4);472}473return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect));474}475476477