Rev 75 | Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <codecvt>
#include "tresources.h"
#include "tfont.h"
#include "texpat++.h"
#include "tconfig.h"
#include "terror.h"
#include "tnameformat.h"
#include <include/core/SkFontStyle.h>
#define FTABLE_DSIG 0x44534947
#define FTABLE_EBDT 0x45424454
#define FTABLE_EBLC 0x45424c43
#define FTABLE_GDEF 0x47444546
#define FTABLE_GPOS 0x47504f53
#define FTABLE_GSUB 0x47535542
#define FTABLE_LTSH 0x4c545348
#define FTABLE_OS_2 0x4f532f32
#define FTABLE_VDMX 0x56444d58
#define FTABLE_cmap 0x636d6170
#define FTABLE_cvt 0x63767420
#define FTABLE_fpgm 0x6670676d
#define FTABLE_gasp 0x67617370
#define FTABLE_glyf 0x676c7966
#define FTABLE_head 0x68656164
#define FTABLE_hhea 0x68686561
#define FTABLE_hmtx 0x686d7478
#define FTABLE_kern 0x6b65726e
#define FTABLE_loca 0x6c6f6361
#define FTABLE_maxp 0x6d617870
#define FTABLE_name 0x6e616d65
#define FTABLE_post 0x706f7374
#define FTABLE_prep 0x70726570
#define FTABLE_PID_UNICODE 0
#define FTABLE_PID_MACINTOSH 1
#define FTABLE_PID_MICROSOFT 3
#define FTABLE_SID_UNI_VERSION1 0
#define FTABLE_SID_UNI_VERSION2 1
#define FTABLE_SID_UNI_ISO10646 2
#define FTABLE_SID_UNI_UNI2BMP 3
#define FTABLE_SID_UNI_UNI2 4
#define FTABLE_SID_UNI_UNIVS 5
#define FTABLE_SID_UNI_LASTRES 6
#define FTABLE_SID_MSC_SYMBOL 0
#define FTABLE_SID_MSC_UNICODE 1
#define FTABLE_SID_MSC_SHIFTJIS 2
#define FTABLE_SID_MSC_PRC 3
#define FTABLE_SID_MSC_BIGFIVE 4
#define FTABLE_SID_MSC_JOHAB 5
#define FTABLE_SID_MSC_UNIUCS4 10
typedef struct FTABLE_FORMAT0_t
{
uint16_t length;
uint16_t language;
unsigned char glyphIndex[256];
}FTABLE_FORMAT0_t;
typedef struct FTABLE_FORMAT4_t
{
uint16_t length;
uint16_t language;
uint16_t segCountX2;
uint16_t searchRange;
uint16_t entrySelector;
uint16_t rangeShift;
uint16_t *endCode;
uint16_t reservedPad;
uint16_t *startCode;
uint16_t *idDelta;
uint16_t *idRangeOffset;
uint16_t *glyphIndexArray;
}FTABLE_FORMAT4_t;
typedef struct FTABLE_FORMATS_t
{
uint16_t format{0};
union fdef
{
FTABLE_FORMAT0_t format0;
FTABLE_FORMAT4_t format4;
}fdef;
}FTABLE_FORMATS_t;
typedef struct FTABLE_SUBTABLE_t
{
uint16_t platformID;
uint16_t platformSpecificID;
uint32_t offset;
FTABLE_FORMATS_t format;
}FTABLE_SUBTABLE_t;
typedef struct FTABLE_CMAP_t
{
uint16_t version{0};
uint16_t numSubtables{0};
FTABLE_SUBTABLE_t *subtables;
}FTABLE_CMAP_t;
using std::string;
using std::vector;
using std::map;
using std::pair;
using namespace Expat;
TFont::TFont()
{
DECL_TRACER("TFont::TFont()");
initialize();
}
TFont::~TFont()
{
DECL_TRACER("TFont::~TFont()");
}
void TFont::initialize()
{
DECL_TRACER("TFont::initialize()");
if (mFonts.size() > 0)
mFonts.clear();
// System fonts first
FONT_T font;
if (!systemFonts())
{
TError::clear();
MSG_INFO("Initializing virtual system fonts because no system files installed!");
font.number = 1;
font.file = "cour.ttf";
font.faceIndex = 1;
font.fullName = "Courier New";
font.name = "Courier New";
font.size = 9;
font.subfamilyName = "normal";
font.fileSize = 0;
font.usageCount = 0;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 2;
font.faceIndex = 2;
font.size = 12;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 3;
font.faceIndex = 3;
font.size = 18;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 4;
font.faceIndex = 4;
font.size = 26;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 5;
font.faceIndex = 5;
font.size = 32;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 6;
font.faceIndex = 6;
font.size = 18;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 7;
font.faceIndex = 7;
font.size = 26;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 8;
font.faceIndex = 8;
font.size = 34;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 9;
font.file = "Amxbold_.ttf";
font.faceIndex = 9;
font.fullName = "AMX Bold";
font.name = "AMX Bold";
font.size = 14;
font.subfamilyName = "bold";
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 10;
font.faceIndex = 10;
font.size = 20;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 11;
font.faceIndex = 11;
font.size = 36;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 19;
font.file = "arial.ttf";
font.faceIndex = 19;
font.fullName = "Arial";
font.name = "Arial";
font.size = 9;
font.subfamilyName = "normal";
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 20;
font.faceIndex = 20;
font.size = 10;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 21;
font.faceIndex = 21;
font.size = 12;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 22;
font.faceIndex = 22;
font.size = 14;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 23;
font.faceIndex = 23;
font.size = 16;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 24;
font.faceIndex = 24;
font.size = 18;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 25;
font.faceIndex = 25;
font.size = 20;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 26;
font.faceIndex = 26;
font.size = 24;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 27;
font.faceIndex = 27;
font.size = 36;
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 28;
font.file = "arialbd.ttf";
font.faceIndex = 28;
font.fullName = "Arial Bold";
font.name = "Arial Bold";
font.size = 10;
font.subfamilyName = "bold";
mFonts.insert(pair<int, FONT_T>(font.number, font));
font.number = 29;
font.faceIndex = 29;
font.size = 8;
mFonts.insert(pair<int, FONT_T>(font.number, font));
}
// read the individual fonts from file
TError::clear();
string path = makeFileName(TConfig::getProjectPath(), "fnt.xma");
if (!isValidFile())
{
MSG_ERROR("File " << path << " doesn't exist or is not readable!");
TError::setError();
return;
}
TExpat xml(path);
xml.setEncoding(ENC_CP1250);
if (!xml.parse())
return;
int depth = 0;
size_t index = 0;
size_t oldIndex = 0;
if ((index = xml.getElementIndex("fontList", &depth)) == TExpat::npos)
{
MSG_DEBUG("File does not contain the element \"fontList\"!");
TError::setError();
return;
}
depth++;
while ((index = xml.getNextElementIndex("font", depth)) != TExpat::npos)
{
string name, content;
FONT_T ft;
vector<ATTRIBUTE_t> attrs = xml.getAttributes(index);
if (!attrs.empty())
ft.number = xml.getAttributeInt("number", attrs);
else
{
MSG_ERROR("Element font contains no or invalid attribute!");
TError::setError();
return;
}
while ((index = xml.getNextElementFromIndex(index, &name, &content, &attrs)) != TExpat::npos)
{
string e = name;
if (e.compare("file") == 0)
ft.file = content;
else if (e.compare("fileSize") == 0)
ft.fileSize = xml.convertElementToInt(content);
else if (e.compare("faceIndex") == 0)
ft.faceIndex = xml.convertElementToInt(content);
else if (e.compare("name") == 0)
ft.name = content;
else if (e.compare("subfamilyName") == 0)
ft.subfamilyName = content;
else if (e.compare("fullName") == 0)
ft.fullName = content;
else if (e.compare("size") == 0)
ft.size = xml.convertElementToInt(content);
else if (e.compare("usageCount") == 0)
ft.usageCount = xml.convertElementToInt(content);
oldIndex = index;
}
mFonts.insert(pair<int, FONT_T>(ft.number, ft));
if (index == TExpat::npos)
index = oldIndex + 1;
xml.setIndex(index);
}
}
bool TFont::systemFonts()
{
DECL_TRACER("TFont::systemFonts()");
string path = makeFileName(TConfig::getProjectPath(), "__system/graphics/fnt.xma");
if (!isValidFile())
{
MSG_ERROR("File " << path << " doesn't exist or is not readable!");
TError::setError();
return false;
}
TExpat xml(path);
xml.setEncoding(ENC_CP1250);
if (!xml.parse())
return false;
int depth = 0;
size_t index = 0;
size_t oldIndex = 0;
if ((index = xml.getElementIndex("fontList", &depth)) == TExpat::npos)
{
MSG_DEBUG("File does not contain the element \"fontList\"!");
TError::setError();
return false;
}
depth++;
while ((index = xml.getNextElementIndex("font", depth)) != TExpat::npos)
{
string name, content;
FONT_T ft;
vector<ATTRIBUTE_t> attrs = xml.getAttributes(index);
if (!attrs.empty())
ft.number = xml.getAttributeInt("number", attrs);
else
{
MSG_ERROR("Element font contains no or invalid attribute!");
TError::setError();
return false;
}
while ((index = xml.getNextElementFromIndex(index, &name, &content, &attrs)) != TExpat::npos)
{
string e = name;
if (e.compare("file") == 0)
ft.file = content;
else if (e.compare("fileSize") == 0)
ft.fileSize = xml.convertElementToInt(content);
else if (e.compare("faceIndex") == 0)
ft.faceIndex = xml.convertElementToInt(content);
else if (e.compare("name") == 0)
ft.name = content;
else if (e.compare("subfamilyName") == 0)
ft.subfamilyName = content;
else if (e.compare("fullName") == 0)
ft.fullName = content;
else if (e.compare("size") == 0)
ft.size = xml.convertElementToInt(content);
else if (e.compare("usageCount") == 0)
ft.usageCount = xml.convertElementToInt(content);
oldIndex = index;
}
mFonts.insert(pair<int, FONT_T>(ft.number, ft));
if (index == TExpat::npos)
index = oldIndex + 1;
xml.setIndex(index);
}
return true;
}
FONT_T TFont::getFont(int number)
{
DECL_TRACER("TFont::getFont(int number)");
if (mFonts.size() == 0)
{
MSG_WARNING("No fonts found!");
return FONT_T();
}
map<int, FONT_T>::iterator iter = mFonts.find(number);
if (iter == mFonts.end())
{
MSG_WARNING("No font with number " << number << " found!");
return FONT_T();
}
return iter->second;
}
FONT_STYLE TFont::getStyle(int number)
{
DECL_TRACER("TFont::getStyle(int number)");
map<int, FONT_T>::iterator iter = mFonts.find(number);
if (iter == mFonts.end())
return FONT_NONE;
if (iter->second.subfamilyName.compare("Regular") == 0)
return FONT_NORMAL;
else if (iter->second.subfamilyName.compare("Italic") == 0)
return FONT_ITALIC;
else if (iter->second.subfamilyName.compare("Bold") == 0)
return FONT_BOLD;
else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
return FONT_BOLD_ITALIC;
return FONT_NORMAL;
}
FONT_STYLE TFont::getStyle(FONT_T& font)
{
DECL_TRACER("TFont::getStyle(int number)");
if (font.subfamilyName.compare("Regular") == 0)
return FONT_NORMAL;
else if (font.subfamilyName.compare("Italic") == 0)
return FONT_ITALIC;
else if (font.subfamilyName.compare("Bold") == 0)
return FONT_BOLD;
else if (font.subfamilyName.compare("Bold Italic") == 0)
return FONT_BOLD_ITALIC;
return FONT_NORMAL;
}
SkFontStyle TFont::getSkiaStyle(int number)
{
DECL_TRACER("TFont::getSkiaStyle(int number)");
map<int, FONT_T>::iterator iter = mFonts.find(number);
if (iter == mFonts.end())
return SkFontStyle(SkFontStyle::kInvisible_Weight, SkFontStyle::kUltraCondensed_Width, SkFontStyle::kUpright_Slant);
if (iter->second.subfamilyName.compare("Regular") == 0)
return SkFontStyle::Normal();
else if (iter->second.subfamilyName.compare("Italic") == 0)
return SkFontStyle::Italic();
else if (iter->second.subfamilyName.compare("Bold") == 0)
return SkFontStyle::Bold();
else if (iter->second.subfamilyName.compare("Bold Italic") == 0)
return SkFontStyle::BoldItalic();
return SkFontStyle::Normal();
}
#define MAX_FACES 10
sk_sp<SkTypeface> TFont::getTypeFace(int number)
{
DECL_TRACER("TFont::getTypeFace(int number)");
map<int, FONT_T>::iterator iter = mFonts.find(number);
if (iter == mFonts.end())
{
MSG_ERROR("No font with index " << number << " found!");
TError::setError();
return sk_sp<SkTypeface>();
}
string path;
if (number < 32) // System font?
path = TConfig::getProjectPath() + "/__system/graphics/fonts/" + iter->second.file;
else
path = TConfig::getProjectPath() + "/fonts/" + iter->second.file;
sk_sp<SkTypeface> tf;
MSG_TRACE("Loading font \"" << path << "\" ...");
if (isValidFile(path))
tf = MakeResourceAsTypeface(path.c_str(), iter->second.faceIndex);
else
MSG_WARNING("File " << path << " is not a valid file or does not exist!");
if (!tf)
{
MSG_ERROR("Error loading font \"" << path << "\"");
MSG_TRACE("Trying with alternative function ...");
TError::setError();
tf = SkTypeface::MakeFromName(iter->second.fullName.c_str(), getSkiaStyle(number));
if (tf)
TError::clear();
else
{
MSG_ERROR("Alternative method failed loading the font " << iter->second.fullName);
return tf;
}
}
else
{
MSG_TRACE("Font \"" << path << "\" was loaded successfull.");
}
SkString sname;
tf->getFamilyName(&sname);
MSG_DEBUG("Found font name \"" << sname.c_str() << "\" with attributes: bold=" << ((tf->isBold())?"TRUE":"FALSE") << ", italic=" << ((tf->isItalic())?"TRUE":"FALSE") << ", fixed=" << ((tf->isFixedPitch())?"TRUE":"FALSE"));
if (iter->second.name.compare(sname.c_str()) != 0)
{
MSG_WARNING("The loaded font \"" << sname.c_str() << "\" is not the wanted font \"" << iter->second.name << "\"!");
}
FONT_STYLE style = getStyle(iter->second);
if (style == FONT_BOLD && tf->isBold())
return tf;
else if (style == FONT_ITALIC && tf->isItalic())
return tf;
else if (style == FONT_BOLD_ITALIC && tf->isBold() && tf->isItalic())
return tf;
else if (style == FONT_NORMAL && !tf->isBold() && !tf->isItalic())
return tf;
MSG_WARNING("The wanted font style " << iter->second.subfamilyName << " was not found!");
return tf;
}
vector<string> TFont::getFontPathList()
{
DECL_TRACER("TFont::getFontPathList()");
vector<string> list;
string path = TConfig::getProjectPath() + "/fonts";
list.push_back(path);
path = TConfig::getProjectPath() + "/__system/graphics/fonts";
list.push_back(path);
return list;
}
size_t _numTables;
FTABLE_CMAP_t _cmapTable;
void TFont::parseCmap(const unsigned char* cmaps)
{
DECL_TRACER("TFont::parseCmap(const unsigned char* cmaps)");
if (!cmaps)
return;
_cmapTable.version = getUint16(cmaps);
_cmapTable.numSubtables = getUint16(cmaps+sizeof(uint16_t));
MSG_DEBUG("Found version " << _cmapTable.version << ", found " << _cmapTable.numSubtables << " cmap tables.");
_cmapTable.subtables = new FTABLE_SUBTABLE_t[_cmapTable.numSubtables];
size_t pos = sizeof(uint16_t) * 2;
for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
{
FTABLE_SUBTABLE_t st;
st.platformID = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
st.platformSpecificID = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
st.offset = getUint32(cmaps+pos);
pos += sizeof(uint32_t);
memmove(&_cmapTable.subtables[i], &st, sizeof(st));
MSG_DEBUG("Table " << (i+1) << ": platformID=" << st.platformID << ", platformSpecificID=" << st.platformSpecificID << ", offset=" << st.offset);
}
// Get the format and read the mapping
for (uint16_t i = 0; i < _cmapTable.numSubtables; i++)
{
if (_cmapTable.subtables[i].platformID == FTABLE_PID_MACINTOSH) // We ignore old Macintosh format
{
_cmapTable.subtables[i].format.format = -1;
continue;
}
FTABLE_FORMATS_t format;
format.format = getUint16(cmaps+_cmapTable.subtables[i].offset);
pos = _cmapTable.subtables[i].offset + sizeof(uint16_t);
switch(format.format)
{
case 0:
format.fdef.format0.length = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format0.language = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
memcpy(format.fdef.format0.glyphIndex, cmaps+pos, sizeof(format.fdef.format0.glyphIndex));
break;
case 4:
format.fdef.format4.length = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format4.language = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format4.segCountX2 = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format4.searchRange = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format4.entrySelector = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format4.rangeShift = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
uint16_t segCount = format.fdef.format4.segCountX2 / 2;
format.fdef.format4.endCode = new uint16_t[segCount];
for (uint16_t j = 0; j < segCount; j++)
{
format.fdef.format4.endCode[j] = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
}
format.fdef.format4.reservedPad = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
format.fdef.format4.startCode = new uint16_t[segCount];
for (uint16_t j = 0; j < segCount; j++)
{
format.fdef.format4.startCode[j] = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
}
format.fdef.format4.idDelta = new uint16_t[segCount];
for (uint16_t j = 0; j < segCount; j++)
{
format.fdef.format4.idDelta[j] = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
}
format.fdef.format4.idRangeOffset = new uint16_t[segCount];
for (uint16_t j = 0; j < segCount; j++)
{
format.fdef.format4.idRangeOffset[j] = getUint16(cmaps+pos);
pos += sizeof(uint16_t);
}
format.fdef.format4.glyphIndexArray = (uint16_t *)(cmaps+pos);
memcpy(&_cmapTable.subtables[i].format, &format, sizeof(FTABLE_FORMATS_t));
break;
}
}
}
uint16_t TFont::getGlyphIndex(SkUnichar ch)
{
DECL_TRACER("TFont::getGlyphIndex(const char* ch)");
uint16_t lCh = ch;
bool symbol = false;
for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
{
if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
{
if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE &&_cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
(_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
symbol = true; // Table does not have unicode table mapping (wingding)
// Find the segment where the wanted character is in.
if (_cmapTable.subtables[nTbs].format.format == 4)
{
FTABLE_FORMAT4_t form = _cmapTable.subtables[nTbs].format.fdef.format4;
uint16_t segCount = form.segCountX2 / 2;
uint16_t segment = 0xffff;
MSG_DEBUG("segCountX2: " << form.segCountX2 << ", # segments: " << segCount);
for (uint16_t sc = 0; sc < segCount; sc++)
{
MSG_DEBUG("Table: " << nTbs << ": Checking range " << std::hex << std::setw(4) << std::setfill('0') << form.startCode[sc] << " to " << std::setw(4) << std::setfill('0') << form.endCode[sc] << std::dec);
if (symbol)
{
if (ch <= 0x00ff)
lCh = ch + (form.startCode[sc] & 0xff00);
else
lCh = ch + (form.startCode[sc] & 0xf000);
}
if (lCh >= form.startCode[sc] && lCh <= form.endCode[sc])
{
segment = sc;
break;
}
}
if (segment == 0xffff || form.startCode[segment] == 0xffff || form.endCode[segment] == 0xffff)
{
MSG_WARNING("The character " << std::hex << std::setw(4) << std::setfill('0') << lCh << " is not supported by any segment!" << std::dec);
continue;
// return 0xffff;
}
MSG_DEBUG("Table: " << (nTbs+1) << ": idRangeOffset: " << std::hex << std::setw(4) << std::setfill('0') << form.idRangeOffset[segment] << ", idDelta: " << std::setw(4) << std::setfill('0') << form.idDelta[segment] << std::dec);
uint16_t glyphIndex;
if (form.idRangeOffset[segment] == 0)
glyphIndex = form.idDelta[segment] + lCh;
else
{
uint16_t gArray = getUint16((unsigned char *)(form.glyphIndexArray + (form.idRangeOffset[segment] / 2)));
MSG_DEBUG("Value from glyphArray: " << std::hex << std::setw(4) << std::setfill('0') << gArray);
if (symbol && segment > 0 && gArray != 0)
glyphIndex = gArray + (lCh - form.startCode[segment]) - 2;
else
glyphIndex = gArray / 2 + (lCh - form.startCode[segment]);
}
MSG_DEBUG("Found index 0x" << std::hex << std::setw(4) << std::setfill('0') << glyphIndex << " for unichar 0x" << std::setw(4) << std::setfill('0') << lCh << std::dec);
return glyphIndex;
}
else
{
MSG_WARNING("Ignoring table with unsupported format " << _cmapTable.subtables[nTbs].format.format);
}
}
}
return 0xffff;
}
void TFont::_freeCmap()
{
DECL_TRACER("TFont::_freeCmap()");
for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
{
if (_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE ||
_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT)
{
if (_cmapTable.subtables[nTbs].format.format == 4)
{
delete[] _cmapTable.subtables[nTbs].format.fdef.format4.endCode;
delete[] _cmapTable.subtables[nTbs].format.fdef.format4.startCode;
delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idDelta;
delete[] _cmapTable.subtables[nTbs].format.fdef.format4.idRangeOffset;
}
}
}
delete[] _cmapTable.subtables;
}
SkGlyphID *TFont::textToGlyphs(const string& str, sk_sp<SkTypeface>& typeFace, size_t *size)
{
DECL_TRACER("TFont::textToGlyphs(const string& str, SkTypeface& typeFace)");
*size = 0;
int tables = typeFace->countTables();
SkFontTableTag *tbTags = new SkFontTableTag[tables];
int tags = typeFace->getTableTags(tbTags);
bool haveCmap = false;
unsigned char *cmaps = nullptr;
// Find the "cmap" and the "glyph" tables and get them
for (int i = 0; i < tags; i++)
{
SkFontTableTag fttg = tbTags[i];
if (fttg == FTABLE_cmap)
{
size_t tbSize = typeFace->getTableSize(fttg);
cmaps = new unsigned char[tbSize];
typeFace->getTableData(fttg, 0, tbSize, cmaps);
haveCmap = true;
break;
}
}
if (!haveCmap)
{
MSG_ERROR("Invalid font. Missing CMAP table!");
TError::setError();
delete[] tbTags;
return nullptr;
}
delete[] tbTags;
parseCmap(cmaps);
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::u16string dest = convert.from_bytes(str);
SkGlyphID *gIds = new SkGlyphID[dest.length()];
*size = dest.length();
for (size_t i = 0; i < dest.length(); i++)
{
SkUnichar uniChar = (SkUnichar)dest[i];
gIds[i] = getGlyphIndex(uniChar);
}
_freeCmap();
delete[] cmaps;
return gIds;
}
bool TFont::isSymbol(sk_sp<SkTypeface>& typeFace)
{
DECL_TRACER("TFont::isSymbol(sk_sp<SkTypeface>& typeFace)");
int tables = typeFace->countTables();
SkFontTableTag *tbTags = new SkFontTableTag[tables];
int tags = typeFace->getTableTags(tbTags);
bool haveCmap = false;
unsigned char *cmaps = nullptr;
// Find the "cmap" table and get them
for (int i = 0; i < tags; i++)
{
SkFontTableTag fttg = tbTags[i];
if (fttg == FTABLE_cmap)
{
size_t tbSize = typeFace->getTableSize(fttg);
cmaps = new unsigned char[tbSize];
typeFace->getTableData(fttg, 0, tbSize, cmaps);
haveCmap = true;
break;
}
}
if (!haveCmap)
{
MSG_ERROR("Invalid font. Missing CMAP table!");
TError::setError();
delete[] tbTags;
return false;
}
delete[] tbTags;
parseCmap(cmaps);
for (uint16_t nTbs = 0; nTbs < _cmapTable.numSubtables; nTbs++)
{
if ((_cmapTable.subtables[nTbs].platformID == FTABLE_PID_UNICODE && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_UNI_VERSION1) ||
(_cmapTable.subtables[nTbs].platformID == FTABLE_PID_MICROSOFT && _cmapTable.subtables[nTbs].platformSpecificID == FTABLE_SID_MSC_SYMBOL))
{
_freeCmap();
delete[] cmaps;
return true;
}
}
_freeCmap();
delete[] cmaps;
return false;
}
size_t TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)
{
DECL_TRACER("TFont::utf8ToUtf16(const string& str, uint16_t **uni, bool toSymbol)");
if (str.empty() || !uni)
{
*uni = nullptr;
return 0;
}
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::u16string dest = convert.from_bytes(str);
*uni = new uint16_t[dest.length()+1];
memset(*uni, 0, sizeof(SkUnichar) * (dest.length()+1));
for (size_t i = 0; i < dest.length(); i++)
{
*uni[i] = dest[i];
if (toSymbol && *uni[i] < 0xf000)
*uni[i] += 0xf000;
}
return dest.length();
}