radio/lib/fontParser/tables/cmap.cpp
2018-08-05 00:48:17 +03:00

219 lines
6.1 KiB
C++

#include "cmap.h"
#include <arpa/inet.h>
Cmap::Cmap(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
Table(p_tag, p_checkSum, p_offset, p_length),
initialized(false),
mt(0)
{
}
Cmap::~Cmap()
{
if (initialized) {
delete mt;
}
}
void Cmap::read(const std::string& path)
{
std::ifstream file(path, std::ios::in | std::ios::binary);
file.seekg(offset);
char * buffer;
buffer = new char[2];
file.read(buffer, 2);
version = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
numberOfTables = ntohs(*((uint16_t*) buffer));
delete[] buffer;
buffer = new char[8];
std::list<Enc> encodings;
for (int i = 0; i < numberOfTables; ++i) {
file.read(buffer, 8);
char pb[2] = {buffer[0], buffer[1]};
char eb[2] = {buffer[2], buffer[3]};
char ob[4] = {buffer[4], buffer[5], buffer[6], buffer[7]};
uint16_t pid = ntohs(*((uint16_t*) pb));
uint16_t eid = ntohs(*((uint16_t*) eb));
uint16_t offset = ntohl(*((uint32_t*) ob));
//std::cout << "Found encoding platformId " << pid << ", encodingId " << eid << std::endl;
if (pid == 0 || (pid == 3 && eid == 1)) {
encodings.emplace_back(pid, eid, offset);
}
}
delete[] buffer;
std::list<Enc>::const_iterator itr = encodings.begin();
std::list<Enc>::const_iterator end = encodings.end();
for (; itr != end; ++itr) {
//std::cout << "Trying platformId " << itr->platformId << ", encodingId " << itr->encodingId << std::endl;
file.seekg(offset + itr->offset);
bool success = true;
MappingTable* table;
try {
table = MappingTable::fromIfStream(file);
} catch (int e) {
success = false;
}
if (success) {
initialized = true;
mt = table;
break;
}
}
file.close();
if (!initialized) {
//std::cout << "Error reading cmap: no supported encoding format" << std::endl;
throw 3;
}
}
uint32_t Cmap::getCID(uint32_t charCode) const
{
return this->mt->getCID(charCode);
}
MappingTable * MappingTable::fromIfStream(std::ifstream& file)
{
uint64_t position = file.tellg();
char * buffer;
buffer = new char[2];
file.read(buffer, 2);
uint16_t format = ntohs(*((uint16_t*) buffer));
MappingTable* table = NULL;
if (format >= 8) {
if (format != 14) {
file.read(buffer, 2); //padded .0 in stupid formats
}
delete[] buffer;
buffer = new char[4];
file.read(buffer, 4);
uint32_t length = ntohl(*((uint32_t*) buffer));
file.seekg(position);
buffer = new char[length];
file.read(buffer, length);
} else {
file.read(buffer, 2);
uint16_t length = ntohs(*((uint16_t*) buffer));
file.seekg(position);
buffer = new char[length];
file.read(buffer, length);
if (format == 4) {
table = new Format4(buffer, length);
}
}
delete[] buffer;
if (table == NULL) {
std::cout << "Unrecognized format " << format << std::endl;
throw 3;
}
return table;
}
MappingTable::MappingTable(uint16_t p_f):
format(p_f)
{
}
MappingTable::~MappingTable()
{
}
Format4::Format4(char * data, uint16_t length):
MappingTable(4),
charCodesEndCode(),
segments(0),
glyphIndexArray(0)
{
char sc[2] = {data[6], data[7]};
uint16_t segCount = ntohs(*((uint16_t*) sc)) / 2;
segments = new std::vector<SegParams>(segCount);
int endCodeShift = 14;
int startCodeShift = endCodeShift + segCount * 2 + 2;
int deltaShift = startCodeShift + segCount * 2;
int rangeShift = deltaShift + segCount * 2;
int giaShift = rangeShift + segCount * 2;
int giaLength = (length - giaShift) / 2;
glyphIndexArray = new std::vector<uint16_t>(giaLength);
// std::cout << "Segments: " << segCount << ", ";
// std::cout << "Glyphs: " << giaLength << "\n";
// std::cout << "******************************************" << "\n";
for (int i = 0; i < segCount; ++i) {
char cc[2] = {data[2 * i + endCodeShift], data[2 * i + endCodeShift + 1]};
char sc[2] = {data[2 * i + startCodeShift], data[2 * i + startCodeShift + 1]};
char dc[2] = {data[2 * i + deltaShift], data[2 * i + deltaShift + 1]};
char rc[2] = {data[2 * i + rangeShift], data[2 * i + rangeShift + 1]};
uint16_t endCharCode = ntohs(*((uint16_t*) cc));
uint16_t startCharCode = ntohs(*((uint16_t*) sc));
int16_t delta = ntohs(*((int16_t*) dc));
uint16_t range = ntohs(*((uint16_t*) rc));
SegParams& sp = segments->at(i);
sp.endCode = endCharCode;
sp.startCode = startCharCode;
sp.idDelta = delta;
sp.idRangeOffset = range;
charCodesEndCode.insert(std::make_pair(endCharCode, i));
// std::cout << "Segment " << i << ",\t";
// std::cout << "Start " << startCharCode << ",\t";
// std::cout << "End " << endCharCode << ",\t";
// std::cout << "Delta " << delta << ",\t";
// std::cout << "Range " << range << "\n";
}
// std::cout << "******************************************" << std::endl;;
for (int i = 0; i < giaLength; ++i) {
char cc[2] = {data[2 * i + giaShift], data[2 * i + giaShift + 1]};
uint16_t glyphIndex = ntohs(*((uint16_t*) cc));
glyphIndexArray->at(i) = glyphIndex;
}
}
Format4::~Format4()
{
delete segments;
delete glyphIndexArray;
}
uint32_t Format4::getCID(uint32_t charCode) const
{
uint16_t cid;
uint16_t c = charCode & 0xffff;
std::map<uint16_t, uint16_t>::const_iterator itr = charCodesEndCode.lower_bound(c);
uint16_t i = itr->second;
SegParams& seg = segments->at(i);
if (seg.startCode > c) {
return 0;
}
if (seg.idRangeOffset == 0) {
cid = c + seg.idDelta;
} else {
cid = i + seg.idRangeOffset - segments->size() + c - seg.startCode;
}
return cid;
}