// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
// Copyright 2010, SIL International, All rights reserved.
#include <cstring>
#include "graphite2/Segment.h"
#include "inc/CmapCache.h"
#include "inc/debug.h"
#include "inc/Decompressor.h"
#include "inc/Endian.h"
#include "inc/Face.h"
#include "inc/FileFace.h"
#include "inc/GlyphFace.h"
#include "inc/json.h"
#include "inc/Segment.h"
#include "inc/NameTable.h"
#include "inc/Error.h"
using namespace graphite2;
namespace
{
enum compression
{
NONE,
LZ4
};
}
Face::Face(const void * appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
: m_appFaceHandle(appFaceHandle),
m_pFileFace(NULL),
m_pGlyphFaceCache(NULL),
m_cmap(NULL),
m_pNames(NULL),
m_logger(NULL),
m_error(0 ), m_errcntxt(0 ),
m_silfs(NULL),
m_numSilf(0 ),
m_ascent(0 ),
m_descent(0 )
{
memset(&m_ops, 0 , sizeof m_ops);
memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size));
}
Face::~Face()
{
setLogger(0 );
delete m_pGlyphFaceCache;
delete m_cmap;
delete [] m_silfs;
#ifndef GRAPHITE2_NFILEFACE
delete m_pFileFace;
#endif
delete m_pNames;
}
float Face::default_glyph_advance(const void * font_ptr, gr_uint16 glyphid)
{
const Font & font = *reinterpret_cast<const Font *>(font_ptr);
return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
}
bool Face::readGlyphs(uint32 faceOptions)
{
Error e;
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _glyph_cat(tele.glyph);
#endif
error_context(EC_READGLYPHS);
m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
|| e.test(m_pGlyphFaceCache->numGlyphs() == 0 , E_NOGLYPHS)
|| e.test(m_pGlyphFaceCache->unitsPerEm() == 0 , E_BADUPEM))
{
return error(e);
}
if (faceOptions & gr_face_cacheCmap)
m_cmap = new CachedCmap(*this);
else
m_cmap = new DirectCmap(*this);
if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
return error(e);
if (faceOptions & gr_face_preloadGlyphs)
nameTable(); // preload the name table along with the glyphs.
return true;
}
bool Face::readGraphite(const Table & silf)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _silf_cat(tele.silf);
#endif
Error e;
error_context(EC_READSILF);
const byte * p = silf;
if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20 , E_BADSIZE)) return error(e);
const uint32 version = be::read<uint32>(p);
if (e.test(version < 0 x00020000, E_TOOOLD)) return error(e);
if (version >= 0 x00030000)
be::skip<uint32>(p); // compilerVersion
m_numSilf = be::read<uint16>(p);
be::skip<uint16>(p); // reserved
bool havePasses = false ;
m_silfs = new Silf[m_numSilf];
if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
for (int i = 0 ; i < m_numSilf; i++)
{
error_context(EC_ASILF + (i << 8 ));
const uint32 offset = be::read<uint32>(p),
next = i == m_numSilf - 1 ? uint32(silf.size()) : be::peek<uint32>(p);
if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
return error(e);
if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
return false ;
if (m_silfs[i].numPasses())
havePasses = true;
}
return havePasses;
}
bool Face::readFeatures()
{
return m_Sill.readFace(*this);
}
bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
{
#if !defined GRAPHITE2_NTRACING
json * dbgout = logger();
if (dbgout)
{
*dbgout << json::object
<< "id" << objectid(seg)
<< "passes" << json::array;
}
#endif
// if ((seg->dir() & 1) != aSilf->dir())
// seg->reverseSlots();
if ((seg->dir() & 3 ) == 3 && aSilf->bidiPass() == 0 xFF)
seg->doMirror(aSilf->aMirror());
bool res = aSilf->runGraphite(seg, 0 , aSilf->positionPass(), true);
if (res)
{
seg->associateChars(0 , seg->charInfoCount());
if (aSilf->flags() & 0 x20)
res &= seg->initCollisions();
if (res)
res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false );
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
{
seg->positionSlots(0 , 0 , 0 , seg->currdir());
*dbgout << json::item
<< json::close // Close up the passes array
<< "outputdir" << (seg->currdir() ? "rtl" : "ltr" )
<< "output" << json::array;
for (Slot * s = seg->first(); s; s = s->next())
*dbgout << dslot(seg, s);
*dbgout << json::close
<< "advance" << seg->advance()
<< "chars" << json::array;
for (size_t i = 0 , n = seg->charInfoCount(); i != n; ++i)
*dbgout << json::flat << *seg->charinfo(int (i));
*dbgout << json::close // Close up the chars array
<< json::close; // Close up the segment object
}
#endif
return res;
}
void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED)
{
#if !defined GRAPHITE2_NTRACING
delete m_logger;
m_logger = log_file ? new json(log_file) : 0 ;
#endif
}
const Silf *Face::chooseSilf(uint32 script) const
{
if (m_numSilf == 0 )
return NULL;
else if (m_numSilf == 1 || script == 0 )
return m_silfs;
else // do more work here
return m_silfs;
}
uint16 Face::findPseudo(uint32 uid) const
{
return (m_numSilf) ? m_silfs[0 ].findPseudo(uid) : 0 ;
}
int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const
{
switch (metrics(metric))
{
case kgmetAscent : return m_ascent;
case kgmetDescent : return m_descent;
default :
if (gid >= glyphs().numGlyphs()) return 0 ;
return glyphs().glyph(gid)->getMetric(metric);
}
}
void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
{
#ifndef GRAPHITE2_NFILEFACE
if (m_pFileFace==pFileFace)
return ;
delete m_pFileFace;
m_pFileFace = pFileFace;
#endif
}
NameTable * Face::nameTable() const
{
if (m_pNames) return m_pNames;
const Table name(*this, Tag::name);
if (name)
m_pNames = new NameTable(name, name.size());
return m_pNames;
}
uint16 Face::languageForLocale(const char * locale) const
{
nameTable();
if (m_pNames)
return m_pNames->getLanguageId(locale);
return 0 ;
}
Face::Table::Table(const Face & face, const Tag n, uint32 version) throw ()
: _f(&face), _sz(0 ), _compressed(false )
{
_p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &_sz));
if (!TtfUtil::CheckTable(n, _p, _sz))
{
release(); // Make sure we release the table buffer even if the table failed its checks
return ;
}
if (be::peek<uint32>(_p) >= version)
decompress();
}
void Face::Table::release()
{
if (_compressed)
free(const_cast <byte *>(_p));
else if (_p && _f->m_ops.release_table)
(*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
_p = 0 ; _sz = 0 ;
}
Face::Table & Face::Table::operator = (const Table && rhs) throw ()
{
if (this == &rhs) return *this;
release();
new (this) Table(std::move(rhs));
return *this;
}
Error Face::Table::decompress()
{
Error e;
if (e.test(_sz < 5 * sizeof (uint32), E_BADSIZE))
return e;
byte * uncompressed_table = 0 ;
size_t uncompressed_size = 0 ;
const byte * p = _p;
const uint32 version = be::read<uint32>(p); // Table version number.
// The scheme is in the top 5 bits of the 1st uint32.
const uint32 hdr = be::read<uint32>(p);
switch (compression(hdr >> 27 ))
{
case NONE: return e;
case LZ4:
{
uncompressed_size = hdr & 0 x07ffffff;
uncompressed_table = gralloc<byte>(uncompressed_size);
if (!e.test(!uncompressed_table || uncompressed_size < 4 , E_OUTOFMEM))
{
memset(uncompressed_table, 0 , 4 ); // make sure version number is initialised
// coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null
// coverity[checked_return : FALSE] - we test e later
e.test(lz4::decompress(p, _sz - 2 *sizeof (uint32), uncompressed_table, uncompressed_size) != signed (uncompressed_size), E_SHRINKERFAILED);
}
break ;
}
default :
e.error(E_BADSCHEME);
};
// Check the uncompressed version number against the original.
if (!e)
// coverity[forward_null : FALSE] - uncompressed_table has already been tested so can't be null
// coverity[checked_return : FALSE] - we test e later
e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
// Tell the provider to release the compressed form since were replacing
// it anyway.
release();
if (e)
{
free(uncompressed_table);
uncompressed_table = 0 ;
uncompressed_size = 0 ;
}
_p = uncompressed_table;
_sz = uncompressed_size;
_compressed = true;
return e;
}
Messung V0.5 in Prozent C=90 H=95 G=92
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland