1 /*  GRAPHITE2 LICENSING
2 
3     Copyright 2010, SIL International
4     All rights reserved.
5 
6     This library is free software; you can redistribute it and/or modify
7     it under the terms of the GNU Lesser General Public License as published
8     by the Free Software Foundation; either version 2.1 of License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Lesser General Public License for more details.
15 
16     You should also have received a copy of the GNU Lesser General Public
17     License along with this library in the file named "LICENSE".
18     If not, write to the Free Software Foundation, 51 Franklin Street,
19     Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20     internet at http://www.fsf.org/licenses/lgpl.html.
21 
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
27 #include <cstring>
28 #include "graphite2/Segment.h"
29 #include "inc/CmapCache.h"
30 #include "inc/debug.h"
31 #include "inc/Decompressor.h"
32 #include "inc/Endian.h"
33 #include "inc/Face.h"
34 #include "inc/FileFace.h"
35 #include "inc/GlyphFace.h"
36 #include "inc/json.h"
37 #include "inc/SegCacheStore.h"
38 #include "inc/Segment.h"
39 #include "inc/NameTable.h"
40 #include "inc/Error.h"
41 
42 using namespace graphite2;
43 
44 namespace
45 {
46 enum compression
47 {
48     NONE,
49     LZ4
50 };
51 
52 }
53 
Face(const void * appFaceHandle,const gr_face_ops & ops)54 Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
55 : m_appFaceHandle(appFaceHandle),
56   m_pFileFace(NULL),
57   m_pGlyphFaceCache(NULL),
58   m_cmap(NULL),
59   m_pNames(NULL),
60   m_logger(NULL),
61   m_error(0), m_errcntxt(0),
62   m_silfs(NULL),
63   m_numSilf(0),
64   m_ascent(0),
65   m_descent(0)
66 {
67     memset(&m_ops, 0, sizeof m_ops);
68     memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size));
69 }
70 
71 
~Face()72 Face::~Face()
73 {
74     setLogger(0);
75     delete m_pGlyphFaceCache;
76     delete m_cmap;
77     delete[] m_silfs;
78 #ifndef GRAPHITE2_NFILEFACE
79     delete m_pFileFace;
80 #endif
81     delete m_pNames;
82 }
83 
default_glyph_advance(const void * font_ptr,gr_uint16 glyphid)84 float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid)
85 {
86     const Font & font = *reinterpret_cast<const Font *>(font_ptr);
87 
88     return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
89 }
90 
readGlyphs(uint32 faceOptions)91 bool Face::readGlyphs(uint32 faceOptions)
92 {
93     Error e;
94 #ifdef GRAPHITE2_TELEMETRY
95     telemetry::category _glyph_cat(tele.glyph);
96 #endif
97     error_context(EC_READGLYPHS);
98     m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
99 
100     if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
101         || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
102         || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
103     {
104         return error(e);
105     }
106 
107     if (faceOptions & gr_face_cacheCmap)
108         m_cmap = new CachedCmap(*this);
109     else
110         m_cmap = new DirectCmap(*this);
111     if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
112         return error(e);
113 
114     if (faceOptions & gr_face_preloadGlyphs)
115         nameTable();        // preload the name table along with the glyphs.
116 
117     return true;
118 }
119 
readGraphite(const Table & silf)120 bool Face::readGraphite(const Table & silf)
121 {
122 #ifdef GRAPHITE2_TELEMETRY
123     telemetry::category _silf_cat(tele.silf);
124 #endif
125     Error e;
126     error_context(EC_READSILF);
127     const byte * p = silf;
128     if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
129 
130     const uint32 version = be::read<uint32>(p);
131     if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
132     if (version >= 0x00030000)
133         be::skip<uint32>(p);        // compilerVersion
134     m_numSilf = be::read<uint16>(p);
135 
136     be::skip<uint16>(p);            // reserved
137 
138     bool havePasses = false;
139     m_silfs = new Silf[m_numSilf];
140     if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
141     for (int i = 0; i < m_numSilf; i++)
142     {
143         error_context(EC_ASILF + (i << 8));
144         const uint32 offset = be::read<uint32>(p),
145                      next   = i == m_numSilf - 1 ? silf.size() : be::peek<uint32>(p);
146         if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
147             return error(e);
148 
149         if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
150             return false;
151 
152         if (m_silfs[i].numPasses())
153             havePasses = true;
154     }
155 
156     return havePasses;
157 }
158 
readFeatures()159 bool Face::readFeatures()
160 {
161     return m_Sill.readFace(*this);
162 }
163 
runGraphite(Segment * seg,const Silf * aSilf) const164 bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
165 {
166 #if !defined GRAPHITE2_NTRACING
167     json * dbgout = logger();
168     if (dbgout)
169     {
170         *dbgout << json::object
171                     << "id"         << objectid(seg)
172                     << "passes"     << json::array;
173     }
174 #endif
175 
176 //    if ((seg->dir() & 1) != aSilf->dir())
177 //        seg->reverseSlots();
178     if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
179         seg->doMirror(aSilf->aMirror());
180     bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
181     if (res)
182     {
183         seg->associateChars(0, seg->charInfoCount());
184         if (aSilf->flags() & 0x20)
185             res &= seg->initCollisions();
186         if (res)
187             res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
188     }
189 
190 #if !defined GRAPHITE2_NTRACING
191     if (dbgout)
192 {
193         seg->positionSlots(0, 0, 0, seg->currdir());
194         *dbgout             << json::item
195                             << json::close // Close up the passes array
196                 << "outputdir" << (seg->currdir() ? "rtl" : "ltr")
197                 << "output" << json::array;
198         for(Slot * s = seg->first(); s; s = s->next())
199             *dbgout     << dslot(seg, s);
200         *dbgout         << json::close
201                 << "advance" << seg->advance()
202                 << "chars"   << json::array;
203         for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i)
204             *dbgout     << json::flat << *seg->charinfo(i);
205         *dbgout         << json::close  // Close up the chars array
206                     << json::close;     // Close up the segment object
207     }
208 #endif
209 
210     return res;
211 }
212 
setLogger(FILE * log_file GR_MAYBE_UNUSED)213 void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED)
214 {
215 #if !defined GRAPHITE2_NTRACING
216     delete m_logger;
217     m_logger = log_file ? new json(log_file) : 0;
218 #endif
219 }
220 
chooseSilf(uint32 script) const221 const Silf *Face::chooseSilf(uint32 script) const
222 {
223     if (m_numSilf == 0)
224         return NULL;
225     else if (m_numSilf == 1 || script == 0)
226         return m_silfs;
227     else // do more work here
228         return m_silfs;
229 }
230 
findPseudo(uint32 uid) const231 uint16 Face::findPseudo(uint32 uid) const
232 {
233     return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
234 }
235 
getGlyphMetric(uint16 gid,uint8 metric) const236 int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const
237 {
238     switch (metrics(metric))
239     {
240         case kgmetAscent : return m_ascent;
241         case kgmetDescent : return m_descent;
242         default:
243             if (gid >= glyphs().numGlyphs()) return 0;
244             return glyphs().glyph(gid)->getMetric(metric);
245     }
246 }
247 
takeFileFace(FileFace * pFileFace GR_MAYBE_UNUSED)248 void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
249 {
250 #ifndef GRAPHITE2_NFILEFACE
251     if (m_pFileFace==pFileFace)
252       return;
253 
254     delete m_pFileFace;
255     m_pFileFace = pFileFace;
256 #endif
257 }
258 
nameTable() const259 NameTable * Face::nameTable() const
260 {
261     if (m_pNames) return m_pNames;
262     const Table name(*this, Tag::name);
263     if (name)
264         m_pNames = new NameTable(name, name.size());
265     return m_pNames;
266 }
267 
languageForLocale(const char * locale) const268 uint16 Face::languageForLocale(const char * locale) const
269 {
270     nameTable();
271     if (m_pNames)
272         return m_pNames->getLanguageId(locale);
273     return 0;
274 }
275 
276 
277 
Table(const Face & face,const Tag n,uint32 version)278 Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
279 : _f(&face), _compressed(false)
280 {
281     size_t sz = 0;
282     _p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz));
283     _sz = uint32(sz);
284 
285     if (!TtfUtil::CheckTable(n, _p, _sz))
286     {
287         releaseBuffers();     // Make sure we release the table buffer even if the table failed it's checks
288         return;
289     }
290 
291     if (be::peek<uint32>(_p) >= version)
292         decompress();
293 }
294 
releaseBuffers()295 void Face::Table::releaseBuffers()
296 {
297     if (_compressed)
298         free(const_cast<byte *>(_p));
299     else if (_p && _f->m_ops.release_table)
300         (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
301     _p = 0; _sz = 0;
302 }
303 
operator =(const Table & rhs)304 Face::Table & Face::Table::operator = (const Table & rhs) throw()
305 {
306     if (_p == rhs._p)   return *this;
307 
308     this->~Table();
309     new (this) Table(rhs);
310     return *this;
311 }
312 
decompress()313 Error Face::Table::decompress()
314 {
315     Error e;
316     if (e.test(_sz < 5 * sizeof(uint32), E_BADSIZE))
317         return e;
318     byte * uncompressed_table = 0;
319     size_t uncompressed_size = 0;
320 
321     const byte * p = _p;
322     const uint32 version = be::read<uint32>(p);    // Table version number.
323 
324     // The scheme is in the top 5 bits of the 1st uint32.
325     const uint32 hdr = be::read<uint32>(p);
326     switch(compression(hdr >> 27))
327     {
328     case NONE: return e;
329 
330     case LZ4:
331     {
332         uncompressed_size  = hdr & 0x07ffffff;
333         uncompressed_table = gralloc<byte>(uncompressed_size);
334         if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM))
335         {
336             memset(uncompressed_table, 0, 4);   // make sure version number is initialised
337             // coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null
338             // coverity[checked_return : FALSE] - we test e later
339             e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
340         }
341         break;
342     }
343 
344     default:
345         e.error(E_BADSCHEME);
346     };
347 
348     // Check the uncompressed version number against the original.
349     if (!e)
350         // coverity[forward_null : FALSE] - uncompressed_table has already been tested so can't be null
351         // coverity[checked_return : FALSE] - we test e later
352         e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
353 
354     // Tell the provider to release the compressed form since were replacing
355     //   it anyway.
356     releaseBuffers();
357 
358     if (e)
359     {
360         free(uncompressed_table);
361         uncompressed_table = 0;
362         uncompressed_size  = 0;
363     }
364 
365     _p = uncompressed_table;
366     _sz = uncompressed_size;
367     _compressed = true;
368 
369     return e;
370 }
371