ref: 17c753b3afc975b1e710c7f5c150f01e5f283b3c
parent: 360b4c5effb93584b29179241df67b725c54d7b2
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Jul 24 21:56:10 EDT 2024
otfglyf: pick the best ppem match
--- a/otf.c.in
+++ b/otf.c.in
@@ -647,99 +647,130 @@
return nil;
}
+static int
+ppemcmp(BitmapGlyph *a, BitmapGlyph *b)
+{
+ if(a->ppemX != b->ppemX)
+ return a->ppemX - b->ppemX;
+ if(a->ppemY != b->ppemY)
+ return a->ppemY - b->ppemY;
+ /* FIXME - is there anything else to compare? bit depth? */
+ return 0;
+}
+
static Glyf *
-bitglyf(Otf *o, Glyf *g)
+bitglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
{
+ BitmapGlyph b, best, want;
IndexSubtableRecord *isr;
+ int i, j, k, n, r, found;
IndexSubtable *is;
BitmapSize *bs;
- int i, j, k, n;
- BitmapGlyph b;
+ want.ppemX = ppemX;
+ want.ppemY = ppemY;
+ found = 0;
+ r = -1;
+
g->type = GLYPH_BITMAP;
bs = o->td.eblc->bitmapSizes;
for(i = 0; i < (int)o->td.eblc->numSizes; i++, bs++){
- if(g->index >= bs->startGlyphIndex && g->index <= bs->endGlyphIndex){
- b.bitDepth = bs->bitDepth;
- b.ppemX = bs->ppemX;
- b.ppemY = bs->ppemY;
- isr = bs->indexSubtableList;
- for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
- if(g->index >= isr->firstGlyphIndex && g->index <= isr->lastGlyphIndex){
- is = isr->indexSubtable;
- if(is == nil)
+ if(g->index < bs->startGlyphIndex)
+ continue;
+ if(g->index > bs->endGlyphIndex)
+ break;
+
+ b.bitDepth = bs->bitDepth;
+ b.ppemX = bs->ppemX;
+ b.ppemY = bs->ppemY;
+ isr = bs->indexSubtableList;
+ for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
+ if(g->index < isr->firstGlyphIndex || (is = isr->indexSubtable) == nil)
+ continue;
+ if(g->index > isr->lastGlyphIndex)
+ break;
+
+ b.format = is->imageFormat;
+ b.offset = is->imageDataOffset;
+ n = g->index - isr->firstGlyphIndex;
+ switch(is->indexFormat){
+ case 1:
+ b.offset += is->sub1.sbitOffsets[n];
+ b.size = is->sub1.sbitOffsets[n+1] - is->sub1.sbitOffsets[n];
+ break;
+ case 2:
+ b.offset += n*is->sub2.imageSize;
+ b.size = is->sub2.imageSize;
+ /* FIXME - vertical metrics are not used here, also this is probably wrong */
+ g->xMin = -is->sub2.bigMetrics.horiBearingX;
+ g->yMin = is->sub2.bigMetrics.horiBearingY - is->sub2.bigMetrics.height;
+ g->xMax = is->sub2.bigMetrics.width + g->xMin;
+ g->yMax = is->sub2.bigMetrics.height + g->yMin;
+ break;
+ case 3:
+ b.offset += is->sub3.sbitOffsets[n];
+ b.size = is->sub3.sbitOffsets[n+1] - is->sub3.sbitOffsets[n];
+ break;
+ case 4:
+ n = is->sub4.numGlyphs;
+ for(k = 0; k < n; k++){
+ if(is->sub4.glyphArray[k].glyphID == g->index)
break;
- b.format = is->imageFormat;
- b.offset = is->imageDataOffset;
- n = g->index - isr->firstGlyphIndex;
- switch(is->indexFormat){
- case 1:
- b.offset += is->sub1.sbitOffsets[n];
- b.size = is->sub1.sbitOffsets[n+1] - is->sub1.sbitOffsets[n];
+ }
+ if(k >= n)
+ continue;
+ b.offset += is->sub4.glyphArray[k].sbitOffset;
+ b.size = is->sub4.glyphArray[k+1].sbitOffset - is->sub4.glyphArray[k].sbitOffset;
+ break;
+ case 5:
+ n = is->sub5.numGlyphs;
+ for(k = 0; k < n; k++){
+ if(is->sub5.glyphIdArray[k] == g->index)
break;
- case 2:
- b.offset += n*is->sub2.imageSize;
- b.size = is->sub2.imageSize;
- /* FIXME - vertical metrics are not used here, also this is probably wrong */
- g->xMin = -is->sub2.bigMetrics.horiBearingX;
- g->yMin = is->sub2.bigMetrics.horiBearingY - is->sub2.bigMetrics.height;
- g->xMax = is->sub2.bigMetrics.width + g->xMin;
- g->yMax = is->sub2.bigMetrics.height + g->yMin;
- break;
- case 3:
- b.offset += is->sub3.sbitOffsets[n];
- b.size = is->sub3.sbitOffsets[n+1] - is->sub3.sbitOffsets[n];
- break;
- case 4:
- n = is->sub4.numGlyphs;
- for(k = 0; k < n; k++){
- if(is->sub4.glyphArray[k].glyphID == g->index)
- break;
- }
- if(k >= n){
- werrstr("not in glyph array");
- goto err;
- }
- b.offset += is->sub4.glyphArray[k].sbitOffset;
- b.size = is->sub4.glyphArray[k+1].sbitOffset - is->sub4.glyphArray[k].sbitOffset;
- break;
- case 5:
- n = is->sub5.numGlyphs;
- for(k = 0; k < n; k++){
- if(is->sub5.glyphIdArray[k] == g->index)
- break;
- }
- if(k >= n){
- werrstr("not in glyph id array");
- goto err;
- }
- b.offset += k*is->sub5.imageSize;
- b.size = is->sub5.imageSize;
- /* FIXME - vertical metrics are not used here, also this is probably wrong */
- g->xMin = -is->sub5.bigMetrics.horiBearingX;
- g->yMin = is->sub5.bigMetrics.horiBearingY - is->sub5.bigMetrics.height;
- g->xMax = is->sub5.bigMetrics.width + g->xMin;
- g->yMax = is->sub5.bigMetrics.height + g->yMin;
- break;
- default:
- werrstr("unsupported index format: %d", is->indexFormat);
- goto err;
- }
-
- return bitebdt(o, g, &b);
}
+ if(k >= n)
+ continue;
+ b.offset += k*is->sub5.imageSize;
+ b.size = is->sub5.imageSize;
+ /* FIXME - vertical metrics are not used here, also this is probably wrong */
+ g->xMin = -is->sub5.bigMetrics.horiBearingX;
+ g->yMin = is->sub5.bigMetrics.horiBearingY - is->sub5.bigMetrics.height;
+ g->xMax = is->sub5.bigMetrics.width + g->xMin;
+ g->yMax = is->sub5.bigMetrics.height + g->yMin;
+ break;
+ default:
+ continue;
}
+
+ /* now try to choose the best match */
+ if((n = ppemcmp(&b, &want)) == 0 || found == 0){
+better:
+ memcpy(&best, &b, sizeof(b));
+ found = 1;
+ if((r = n) == 0)
+ goto done;
+ continue;
+ }
+ /* both better, but the new is closer */
+ if(n > 0 && (r > 0 && ppemcmp(&b, &best) < 0))
+ goto better;
+ /* both worse, but the new one is better so far */
+ if(n < 0 && (r < 0 && ppemcmp(&b, &best) > 0))
+ goto better;
}
}
+done:
+ if(found)
+ return bitebdt(o, g, &best);
+
werrstr("no bitmap");
-err:
free(g);
return nil;
}
Glyf *
-otfglyf(Otf *o, int index)
+otfglyf(Otf *o, int index, double ppemX, double ppemY)
{
int off, len, i;
Glyf *g;
@@ -754,7 +785,7 @@
}
g->index = index;
if(o->td.eblc != nil && o->td.ebdt != nil)
- return bitglyf(o, g);
+ return bitglyf(o, g, ppemX, ppemY);
if(o->td.head == nil){
werrstr("no head table");
--- a/otf.h.in
+++ b/otf.h.in
@@ -131,5 +131,5 @@
int otfrune2glyph(Otf *o, Rune r);
Rune otfglyph2rune(Otf *o, int g);
-Glyf *otfglyf(Otf *o, int index);
+Glyf *otfglyf(Otf *o, int index, double ppemX, double ppemY);
GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY);
--- a/plan9/otf.c
+++ b/plan9/otf.c
@@ -648,99 +648,130 @@
return nil;
}
+static int
+ppemcmp(BitmapGlyph *a, BitmapGlyph *b)
+{
+ if(a->ppemX != b->ppemX)
+ return a->ppemX - b->ppemX;
+ if(a->ppemY != b->ppemY)
+ return a->ppemY - b->ppemY;
+ /* FIXME - is there anything else to compare? bit depth? */
+ return 0;
+}
+
static Glyf *
-bitglyf(Otf *o, Glyf *g)
+bitglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
{
+ BitmapGlyph b, best, want;
IndexSubtableRecord *isr;
+ int i, j, k, n, r, found;
IndexSubtable *is;
BitmapSize *bs;
- int i, j, k, n;
- BitmapGlyph b;
+ want.ppemX = ppemX;
+ want.ppemY = ppemY;
+ found = 0;
+ r = -1;
+
g->type = GLYPH_BITMAP;
bs = o->td.eblc->bitmapSizes;
for(i = 0; i < (int)o->td.eblc->numSizes; i++, bs++){
- if(g->index >= bs->startGlyphIndex && g->index <= bs->endGlyphIndex){
- b.bitDepth = bs->bitDepth;
- b.ppemX = bs->ppemX;
- b.ppemY = bs->ppemY;
- isr = bs->indexSubtableList;
- for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
- if(g->index >= isr->firstGlyphIndex && g->index <= isr->lastGlyphIndex){
- is = isr->indexSubtable;
- if(is == nil)
+ if(g->index < bs->startGlyphIndex)
+ continue;
+ if(g->index > bs->endGlyphIndex)
+ break;
+
+ b.bitDepth = bs->bitDepth;
+ b.ppemX = bs->ppemX;
+ b.ppemY = bs->ppemY;
+ isr = bs->indexSubtableList;
+ for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
+ if(g->index < isr->firstGlyphIndex || (is = isr->indexSubtable) == nil)
+ continue;
+ if(g->index > isr->lastGlyphIndex)
+ break;
+
+ b.format = is->imageFormat;
+ b.offset = is->imageDataOffset;
+ n = g->index - isr->firstGlyphIndex;
+ switch(is->indexFormat){
+ case 1:
+ b.offset += is->sub1.sbitOffsets[n];
+ b.size = is->sub1.sbitOffsets[n+1] - is->sub1.sbitOffsets[n];
+ break;
+ case 2:
+ b.offset += n*is->sub2.imageSize;
+ b.size = is->sub2.imageSize;
+ /* FIXME - vertical metrics are not used here, also this is probably wrong */
+ g->xMin = -is->sub2.bigMetrics.horiBearingX;
+ g->yMin = is->sub2.bigMetrics.horiBearingY - is->sub2.bigMetrics.height;
+ g->xMax = is->sub2.bigMetrics.width + g->xMin;
+ g->yMax = is->sub2.bigMetrics.height + g->yMin;
+ break;
+ case 3:
+ b.offset += is->sub3.sbitOffsets[n];
+ b.size = is->sub3.sbitOffsets[n+1] - is->sub3.sbitOffsets[n];
+ break;
+ case 4:
+ n = is->sub4.numGlyphs;
+ for(k = 0; k < n; k++){
+ if(is->sub4.glyphArray[k].glyphID == g->index)
break;
- b.format = is->imageFormat;
- b.offset = is->imageDataOffset;
- n = g->index - isr->firstGlyphIndex;
- switch(is->indexFormat){
- case 1:
- b.offset += is->sub1.sbitOffsets[n];
- b.size = is->sub1.sbitOffsets[n+1] - is->sub1.sbitOffsets[n];
+ }
+ if(k >= n)
+ continue;
+ b.offset += is->sub4.glyphArray[k].sbitOffset;
+ b.size = is->sub4.glyphArray[k+1].sbitOffset - is->sub4.glyphArray[k].sbitOffset;
+ break;
+ case 5:
+ n = is->sub5.numGlyphs;
+ for(k = 0; k < n; k++){
+ if(is->sub5.glyphIdArray[k] == g->index)
break;
- case 2:
- b.offset += n*is->sub2.imageSize;
- b.size = is->sub2.imageSize;
- /* FIXME - vertical metrics are not used here, also this is probably wrong */
- g->xMin = -is->sub2.bigMetrics.horiBearingX;
- g->yMin = is->sub2.bigMetrics.horiBearingY - is->sub2.bigMetrics.height;
- g->xMax = is->sub2.bigMetrics.width + g->xMin;
- g->yMax = is->sub2.bigMetrics.height + g->yMin;
- break;
- case 3:
- b.offset += is->sub3.sbitOffsets[n];
- b.size = is->sub3.sbitOffsets[n+1] - is->sub3.sbitOffsets[n];
- break;
- case 4:
- n = is->sub4.numGlyphs;
- for(k = 0; k < n; k++){
- if(is->sub4.glyphArray[k].glyphID == g->index)
- break;
- }
- if(k >= n){
- werrstr("not in glyph array");
- goto err;
- }
- b.offset += is->sub4.glyphArray[k].sbitOffset;
- b.size = is->sub4.glyphArray[k+1].sbitOffset - is->sub4.glyphArray[k].sbitOffset;
- break;
- case 5:
- n = is->sub5.numGlyphs;
- for(k = 0; k < n; k++){
- if(is->sub5.glyphIdArray[k] == g->index)
- break;
- }
- if(k >= n){
- werrstr("not in glyph id array");
- goto err;
- }
- b.offset += k*is->sub5.imageSize;
- b.size = is->sub5.imageSize;
- /* FIXME - vertical metrics are not used here, also this is probably wrong */
- g->xMin = -is->sub5.bigMetrics.horiBearingX;
- g->yMin = is->sub5.bigMetrics.horiBearingY - is->sub5.bigMetrics.height;
- g->xMax = is->sub5.bigMetrics.width + g->xMin;
- g->yMax = is->sub5.bigMetrics.height + g->yMin;
- break;
- default:
- werrstr("unsupported index format: %d", is->indexFormat);
- goto err;
- }
-
- return bitebdt(o, g, &b);
}
+ if(k >= n)
+ continue;
+ b.offset += k*is->sub5.imageSize;
+ b.size = is->sub5.imageSize;
+ /* FIXME - vertical metrics are not used here, also this is probably wrong */
+ g->xMin = -is->sub5.bigMetrics.horiBearingX;
+ g->yMin = is->sub5.bigMetrics.horiBearingY - is->sub5.bigMetrics.height;
+ g->xMax = is->sub5.bigMetrics.width + g->xMin;
+ g->yMax = is->sub5.bigMetrics.height + g->yMin;
+ break;
+ default:
+ continue;
}
+
+ /* now try to choose the best match */
+ if((n = ppemcmp(&b, &want)) == 0 || found == 0){
+better:
+ memcpy(&best, &b, sizeof(b));
+ found = 1;
+ if((r = n) == 0)
+ goto done;
+ continue;
+ }
+ /* both better, but the new is closer */
+ if(n > 0 && (r > 0 && ppemcmp(&b, &best) < 0))
+ goto better;
+ /* both worse, but the new one is better so far */
+ if(n < 0 && (r < 0 && ppemcmp(&b, &best) > 0))
+ goto better;
}
}
+done:
+ if(found)
+ return bitebdt(o, g, &best);
+
werrstr("no bitmap");
-err:
free(g);
return nil;
}
Glyf *
-otfglyf(Otf *o, int index)
+otfglyf(Otf *o, int index, double ppemX, double ppemY)
{
int off, len, i;
Glyf *g;
@@ -755,7 +786,7 @@
}
g->index = index;
if(o->td.eblc != nil && o->td.ebdt != nil)
- return bitglyf(o, g);
+ return bitglyf(o, g, ppemX, ppemY);
if(o->td.head == nil){
werrstr("no head table");
--- a/plan9/otf.h
+++ b/plan9/otf.h
@@ -132,7 +132,7 @@
int otfrune2glyph(Otf *o, Rune r);
Rune otfglyph2rune(Otf *o, int g);
-Glyf *otfglyf(Otf *o, int index);
+Glyf *otfglyf(Otf *o, int index, double ppemX, double ppemY);
GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY);
typedef struct SubHeader SubHeader;
--- a/rast.c
+++ b/rast.c
@@ -566,7 +566,7 @@
gs[ngs++] = g;
}else{
for(cg = g->component; cg != nil && ngs < nelem(gs); cg = cg->next, ngs++){
- if((gs[ngs] = otfglyf(o, cg->glyphIndex)) == nil)
+ if((gs[ngs] = otfglyf(o, cg->glyphIndex, ppemX, ppemY)) == nil)
goto done;
}
}
@@ -749,7 +749,7 @@
bb[3] = g->yMax;
for(ngs = 0; ngs < nelem(gs) && ngs < bg->numComponents;){
c = bg->components + ngs;
- if((cg = gs[ngs] = otfglyf(o, c->glyphID)) == nil)
+ if((cg = gs[ngs] = otfglyf(o, c->glyphID, ppemX, ppemY)) == nil)
goto err;
ngs++;
if(cg->type != GLYPH_BITMAP){
--- a/test.h
+++ b/test.h
@@ -151,7 +151,7 @@
for(i = 0; i < n; i++){
if(runesonly && otfglyph2rune(o, i) == NoRune)
continue;
- if((g = otfglyf(o, i)) == nil){
+ if((g = otfglyf(o, i, ppemX, ppemY)) == nil){
gind = i;
glypherr:
werrstr("glyph %d: %r", gind);
@@ -178,7 +178,7 @@
}else if(gind < 0){
otfprint(o, out, indentΔ);
}else{
- if((g = otfglyf(o, gind)) == nil){
+ if((g = otfglyf(o, gind, ppemX, ppemY)) == nil){
goto glypherr;
}else if(ppemX > 0){
GlyfImage *im;