michael@0: /* GRAPHITE2 LICENSING michael@0: michael@0: Copyright 2010, 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 michael@0: #include "graphite2/Segment.h" michael@0: #include "inc/CmapCache.h" michael@0: #include "inc/debug.h" michael@0: #include "inc/Endian.h" michael@0: #include "inc/Face.h" michael@0: #include "inc/FileFace.h" michael@0: #include "inc/GlyphFace.h" michael@0: #include "inc/json.h" michael@0: #include "inc/SegCacheStore.h" michael@0: #include "inc/Segment.h" michael@0: #include "inc/NameTable.h" michael@0: #include "inc/Error.h" michael@0: michael@0: using namespace graphite2; michael@0: michael@0: Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops) michael@0: : m_appFaceHandle(appFaceHandle), michael@0: m_pFileFace(NULL), michael@0: m_pGlyphFaceCache(NULL), michael@0: m_cmap(NULL), michael@0: m_pNames(NULL), michael@0: m_logger(NULL), michael@0: m_error(0), m_errcntxt(0), michael@0: m_silfs(NULL), michael@0: m_numSilf(0), michael@0: m_ascent(0), michael@0: m_descent(0) michael@0: { michael@0: memset(&m_ops, 0, sizeof m_ops); michael@0: memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size)); michael@0: } michael@0: michael@0: michael@0: Face::~Face() michael@0: { michael@0: setLogger(0); michael@0: delete m_pGlyphFaceCache; michael@0: delete m_cmap; michael@0: delete[] m_silfs; michael@0: #ifndef GRAPHITE2_NFILEFACE michael@0: delete m_pFileFace; michael@0: #endif michael@0: delete m_pNames; michael@0: } michael@0: michael@0: float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid) michael@0: { michael@0: const Font & font = *reinterpret_cast(font_ptr); michael@0: michael@0: return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale(); michael@0: } michael@0: michael@0: bool Face::readGlyphs(uint32 faceOptions) michael@0: { michael@0: Error e; michael@0: #ifdef GRAPHITE2_TELEMETRY michael@0: telemetry::category _glyph_cat(tele.glyph); michael@0: #endif michael@0: error_context(EC_READGLYPHS); michael@0: if (faceOptions & gr_face_cacheCmap) michael@0: m_cmap = new CachedCmap(*this); michael@0: else michael@0: m_cmap = new DirectCmap(*this); michael@0: michael@0: m_pGlyphFaceCache = new GlyphCache(*this, faceOptions); michael@0: if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM) michael@0: || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS) michael@0: || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM) michael@0: || e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP)) michael@0: { michael@0: return error(e); michael@0: } michael@0: michael@0: if (faceOptions & gr_face_preloadGlyphs) michael@0: nameTable(); // preload the name table along with the glyphs. michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool Face::readGraphite(const Table & silf) michael@0: { michael@0: #ifdef GRAPHITE2_TELEMETRY michael@0: telemetry::category _silf_cat(tele.silf); michael@0: #endif michael@0: Error e; michael@0: error_context(EC_READSILF); michael@0: const byte * p = silf; michael@0: if (e.test(!p, E_NOSILF)) return error(e); michael@0: michael@0: const uint32 version = be::read(p); michael@0: if (e.test(version < 0x00020000, E_TOOOLD)) return error(e); michael@0: if (version >= 0x00030000) michael@0: be::skip(p); // compilerVersion michael@0: m_numSilf = be::read(p); michael@0: be::skip(p); // reserved michael@0: michael@0: bool havePasses = false; michael@0: m_silfs = new Silf[m_numSilf]; michael@0: for (int i = 0; i < m_numSilf; i++) michael@0: { michael@0: error_context(EC_ASILF + (i << 8)); michael@0: const uint32 offset = be::read(p), michael@0: next = i == m_numSilf - 1 ? silf.size() : be::peek(p); michael@0: if (e.test(next > silf.size() || offset >= next, E_BADSIZE)) michael@0: return error(e); michael@0: michael@0: if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version)) michael@0: return false; michael@0: michael@0: if (m_silfs[i].numPasses()) michael@0: havePasses = true; michael@0: } michael@0: michael@0: return havePasses; michael@0: } michael@0: michael@0: bool Face::readFeatures() michael@0: { michael@0: return m_Sill.readFace(*this); michael@0: } michael@0: michael@0: bool Face::runGraphite(Segment *seg, const Silf *aSilf) const michael@0: { michael@0: #if !defined GRAPHITE2_NTRACING michael@0: json * dbgout = logger(); michael@0: if (dbgout) michael@0: { michael@0: *dbgout << json::object michael@0: << "id" << objectid(seg) michael@0: << "passes" << json::array; michael@0: } michael@0: #endif michael@0: michael@0: bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass(), true); michael@0: if (res) michael@0: res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false); michael@0: michael@0: #if !defined GRAPHITE2_NTRACING michael@0: if (dbgout) michael@0: { michael@0: *dbgout << json::item michael@0: << json::close // Close up the passes array michael@0: << "output" << json::array; michael@0: for(Slot * s = seg->first(); s; s = s->next()) michael@0: *dbgout << dslot(seg, s); michael@0: seg->finalise(0); // Call this here to fix up charinfo back indexes. michael@0: *dbgout << json::close michael@0: << "advance" << seg->advance() michael@0: << "chars" << json::array; michael@0: for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i) michael@0: *dbgout << json::flat << *seg->charinfo(i); michael@0: *dbgout << json::close // Close up the chars array michael@0: << json::close; // Close up the segment object michael@0: } michael@0: #endif michael@0: michael@0: return res; michael@0: } michael@0: michael@0: void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED) michael@0: { michael@0: #if !defined GRAPHITE2_NTRACING michael@0: delete m_logger; michael@0: m_logger = log_file ? new json(log_file) : 0; michael@0: #endif michael@0: } michael@0: michael@0: const Silf *Face::chooseSilf(uint32 script) const michael@0: { michael@0: if (m_numSilf == 0) michael@0: return NULL; michael@0: else if (m_numSilf == 1 || script == 0) michael@0: return m_silfs; michael@0: else // do more work here michael@0: return m_silfs; michael@0: } michael@0: michael@0: uint16 Face::findPseudo(uint32 uid) const michael@0: { michael@0: return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0; michael@0: } michael@0: michael@0: uint16 Face::getGlyphMetric(uint16 gid, uint8 metric) const michael@0: { michael@0: switch (metrics(metric)) michael@0: { michael@0: case kgmetAscent : return m_ascent; michael@0: case kgmetDescent : return m_descent; michael@0: default: return glyphs().glyph(gid)->getMetric(metric); michael@0: } michael@0: } michael@0: michael@0: void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/) michael@0: { michael@0: #ifndef GRAPHITE2_NFILEFACE michael@0: if (m_pFileFace==pFileFace) michael@0: return; michael@0: michael@0: delete m_pFileFace; michael@0: m_pFileFace = pFileFace; michael@0: #endif michael@0: } michael@0: michael@0: NameTable * Face::nameTable() const michael@0: { michael@0: if (m_pNames) return m_pNames; michael@0: const Table name(*this, Tag::name); michael@0: if (name) michael@0: m_pNames = new NameTable(name, name.size()); michael@0: return m_pNames; michael@0: } michael@0: michael@0: uint16 Face::languageForLocale(const char * locale) const michael@0: { michael@0: nameTable(); michael@0: if (m_pNames) michael@0: return m_pNames->getLanguageId(locale); michael@0: return 0; michael@0: } michael@0: michael@0: Face::Table::Table(const Face & face, const Tag n) throw() michael@0: : _f(&face) michael@0: { michael@0: size_t sz = 0; michael@0: _p = reinterpret_cast((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &sz)); michael@0: _sz = uint32(sz); michael@0: if (!TtfUtil::CheckTable(n, _p, _sz)) michael@0: { michael@0: this->~Table(); // Make sure we release the table buffer even if the table filed it's checks michael@0: _p = 0; _sz = 0; michael@0: } michael@0: } michael@0: michael@0: Face::Table & Face::Table::operator = (const Table & rhs) throw() michael@0: { michael@0: if (_p == rhs._p) return *this; michael@0: michael@0: this->~Table(); michael@0: new (this) Table(rhs); michael@0: return *this; michael@0: } michael@0: