Add basic GPOS kerning

pull/495/head
Daniel Ribeiro Maciel 2017-08-24 15:22:52 -03:00
parent f2974f6c1c
commit e460d1a8d8
1 changed files with 238 additions and 3 deletions

View File

@ -22,6 +22,7 @@
// Mikko Mononen: compound shape support, more cmap formats
// Tor Andersson: kerning, subpixel rendering
// Dougall Johnson: OpenType / Type 2 font handling
// Daniel Ribeiro Maciel: basic GPOS-based kerning
//
// Misc other:
// Ryan Gordon
@ -695,7 +696,7 @@ struct stbtt_fontinfo
int numGlyphs; // number of glyphs, needed for range checking
int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf
int index_map; // a cmap mapping for our chosen character encoding
int indexToLocFormat; // format needed to map from glyph index to glyph
@ -1338,6 +1339,7 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in
info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
if (!cmap || !info->head || !info->hhea || !info->hmtx)
return 0;
@ -2256,7 +2258,7 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde
}
}
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@ -2286,9 +2288,242 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1,
return 0;
}
static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
{
stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
switch(coverageFormat) {
case 1: {
stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
// Binary search.
stbtt_int32 l=0, r=glyphCount-1, m;
int straw, needle=glyph;
while (l <= r) {
m = (l + r) >> 1;
stbtt_uint8 *glyphArray = coverageTable + 4;
stbtt_uint16 glyphID = ttUSHORT(glyphArray + 2 * m);
straw = glyphID;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
return m;
}
}
} break;
case 2: {
stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
stbtt_uint8 *rangeArray = coverageTable + 4;
// Binary search.
stbtt_int32 l=0, r=rangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
m = (l + r) >> 1;
stbtt_uint8 *rangeRecord = rangeArray + 6 * m;
strawStart = ttUSHORT(rangeRecord);
strawEnd = ttUSHORT(rangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else {
stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
return startCoverageIndex + glyph - strawStart;
}
}
} break;
default: {
// There are no other cases.
STBTT_assert(false);
} break;
}
return -1;
}
static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
{
stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
switch(classDefFormat)
{
case 1: {
stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
classDefTable = classDef1ValueArray + 2 * glyphCount;
} break;
case 2: {
stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
stbtt_uint8 *classRangeRecords = classDefTable + 4;
// Binary search.
stbtt_int32 l=0, r=classRangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
m = (l + r) >> 1;
stbtt_uint8 *classRangeRecord = classRangeRecords + 6 * m;
strawStart = ttUSHORT(classRangeRecord);
strawEnd = ttUSHORT(classRangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else
return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
}
classDefTable = classRangeRecords + 6 * classRangeCount;
} break;
default: {
// There are no other cases.
STBTT_assert(false);
} break;
}
return -1;
}
// Define to STBTT_assert(x) if you want to break on unimplemented formats.
#define STBTT_GPOS_TODO_assert(x)
static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
if (!info->gpos) return 0;
stbtt_uint8 *data = info->data + info->gpos;
if (ttUSHORT(data+0) != 1) return 0; // Major version 1
if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
stbtt_uint16 lookupListOffset = ttUSHORT(data+8);
stbtt_uint8 *lookupList = data + lookupListOffset;
stbtt_uint16 lookupCount = ttUSHORT(lookupList);
for (stbtt_int32 i=0; i<lookupCount; ++i) {
stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
stbtt_uint8 *lookupTable = lookupList + lookupOffset;
stbtt_uint16 lookupType = ttUSHORT(lookupTable);
stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
stbtt_uint8 *subTableOffsets = lookupTable + 6;
switch(lookupType) {
case 2: { // Pair Adjustment Positioning Subtable
for (stbtt_int32 sti=0; sti<subTableCount; sti++) {
stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
stbtt_uint8 *table = lookupTable + subtableOffset;
stbtt_uint16 posFormat = ttUSHORT(table);
stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
if (coverageIndex == -1) continue;
switch (posFormat) {
case 1: {
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
// TODO: Support more formats.
STBTT_GPOS_TODO_assert(valueFormat1 == 4);
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
stbtt_int32 valueRecordPairSizeInBytes = 2;
stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
STBTT_assert(coverageIndex < pairSetCount);
stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
stbtt_uint8 *pairValueTable = table + pairPosOffset;
stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
stbtt_uint8 *pairValueArray = pairValueTable + 2;
// Binary search.
stbtt_int32 l=0, r=pairValueCount-1, m;
int straw, needle=glyph2;
while (l <= r) {
m = (l + r) >> 1;
stbtt_uint8 *pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
stbtt_uint16 secondGlyph = ttUSHORT(pairValue);
straw = secondGlyph;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
return xAdvance;
}
}
} break;
case 2: {
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
// TODO: Support more formats.
STBTT_GPOS_TODO_assert(valueFormat1 == 4);
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
stbtt_uint16 class1Count = ttUSHORT(table + 12);
stbtt_uint16 class2Count = ttUSHORT(table + 14);
STBTT_assert(glyph1class < class1Count);
STBTT_assert(glyph2class < class2Count);
if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
stbtt_uint8 *class1Records = table + 16;
stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
return xAdvance;
}
} break;
default: {
// There are no other cases.
STBTT_assert(false);
} break;
}
}
} break;
default: {
// TODO: Implement other stuff.
} break;
}
}
return 0;
}
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
{
int xAdvance = 0;
if (info->gpos)
xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
if (info->kern)
xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
return xAdvance;
}
STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
{
if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
return 0;
return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
}