diff options
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/lib/otf-writer/stat.c')
-rw-r--r-- | Build/source/texk/web2c/mfluadir/otfcc/lib/otf-writer/stat.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/lib/otf-writer/stat.c b/Build/source/texk/web2c/mfluadir/otfcc/lib/otf-writer/stat.c new file mode 100644 index 00000000000..e94b2a2f551 --- /dev/null +++ b/Build/source/texk/web2c/mfluadir/otfcc/lib/otf-writer/stat.c @@ -0,0 +1,706 @@ +#include "stat.h" + +#include <time.h> +#include <float.h> +#include "support/util.h" + +#define OTFCC_CHR(a,b,c,d) ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) ) + +// Stating +// Calculate necessary values for SFNT + +typedef enum { stat_not_started = 0, stat_doing = 1, stat_completed = 2 } stat_status; + +static glyf_GlyphStat stat_single_glyph(table_glyf *table, glyf_ComponentReference *gr, + stat_status *stated, uint8_t depth, glyphid_t topj, + const otfcc_Options *options) { + glyf_GlyphStat stat = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + glyphid_t j = gr->glyph.index; + if (depth >= 0xFF) return stat; + if (stated[j] == stat_doing) { + // We have a circular reference + logWarning("[Stat] Circular glyph reference found in gid %d to gid %d. The reference will " + "be dropped.\n", + topj, j); + stated[j] = stat_completed; + return stat; + } + + glyf_Glyph *g = table->items[j]; + stated[j] = stat_doing; + pos_t xmin = POS_MAX; + pos_t xmax = -POS_MAX; + pos_t ymin = POS_MAX; + pos_t ymax = -POS_MAX; + uint16_t nestDepth = 0; + uint16_t nPoints = 0; + uint16_t nCompositePoints = 0; + uint16_t nCompositeContours = 0; + // Stat xmin, xmax, ymin, ymax + for (shapeid_t c = 0; c < g->contours.length; c++) { + for (shapeid_t pj = 0; pj < g->contours.items[c].length; pj++) { + // Stat point coordinates USING the matrix transformation + glyf_Point *p = &(g->contours.items[c].items[pj]); + pos_t x = round(iVQ.getStill(gr->x) + gr->a * iVQ.getStill(p->x) + + gr->b * iVQ.getStill(p->y)); + pos_t y = round(iVQ.getStill(gr->y) + gr->c * iVQ.getStill(p->x) + + gr->d * iVQ.getStill(p->y)); + if (x < xmin) xmin = x; + if (x > xmax) xmax = x; + if (y < ymin) ymin = y; + if (y > ymax) ymax = y; + nPoints += 1; + } + } + nCompositePoints = nPoints; + nCompositeContours = g->contours.length; + for (shapeid_t r = 0; r < g->references.length; r++) { + glyf_ComponentReference ref; + glyf_iComponentReference.init(&ref); + glyf_ComponentReference *rr = &(g->references.items[r]); + // composite affine transformations + Handle.replace(&ref.glyph, Handle.fromIndex(g->references.items[r].glyph.index)); + ref.a = gr->a * rr->a + rr->b * gr->c; + ref.b = rr->a * gr->b + rr->b * gr->d; + ref.c = gr->a * rr->c + gr->c * rr->d; + ref.d = gr->b * rr->c + rr->d * gr->d; + iVQ.replace(&ref.x, iVQ.createStill(iVQ.getStill(rr->x) + rr->a * iVQ.getStill(gr->x) + + rr->b * iVQ.getStill(gr->y))); + iVQ.replace(&ref.y, iVQ.createStill(iVQ.getStill(rr->y) + rr->c * iVQ.getStill(gr->x) + + rr->d * iVQ.getStill(gr->y))); + + glyf_GlyphStat thatstat = stat_single_glyph(table, &ref, stated, depth + 1, topj, options); + if (thatstat.xMin < xmin) xmin = thatstat.xMin; + if (thatstat.xMax > xmax) xmax = thatstat.xMax; + if (thatstat.yMin < ymin) ymin = thatstat.yMin; + if (thatstat.yMax > ymax) ymax = thatstat.yMax; + if (thatstat.nestDepth + 1 > nestDepth) nestDepth = thatstat.nestDepth + 1; + nCompositePoints += thatstat.nCompositePoints; + nCompositeContours += thatstat.nCompositeContours; + } + // deal with space glyphs + if (xmin > xmax) xmin = xmax = 0; + if (ymin > ymax) ymin = ymax = 0; + + // write back + stat.xMin = xmin; + stat.xMax = xmax; + stat.yMin = ymin; + stat.yMax = ymax; + stat.nestDepth = nestDepth; + stat.nPoints = nPoints; + stat.nContours = g->contours.length; + stat.nCompositePoints = nCompositePoints; + stat.nCompositeContours = nCompositeContours; + stated[j] = stat_completed; + return stat; +} + +static void statGlyf(otfcc_Font *font, const otfcc_Options *options) { + stat_status *stated; + NEW(stated, font->glyf->length); + pos_t xmin = 0xFFFFFFFF; + pos_t xmax = -0xFFFFFFFF; + pos_t ymin = 0xFFFFFFFF; + pos_t ymax = -0xFFFFFFFF; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + glyf_ComponentReference gr; + gr.glyph = Handle.fromIndex(j); + gr.x = iVQ.createStill(0); + gr.y = iVQ.createStill(0); + gr.a = 1; + gr.b = 0; + gr.c = 0; + gr.d = 1; + glyf_GlyphStat thatstat = font->glyf->items[j]->stat = + stat_single_glyph(font->glyf, &gr, stated, 0, j, options); + if (thatstat.xMin < xmin) xmin = thatstat.xMin; + if (thatstat.xMax > xmax) xmax = thatstat.xMax; + if (thatstat.yMin < ymin) ymin = thatstat.yMin; + if (thatstat.yMax > ymax) ymax = thatstat.yMax; + } + font->head->xMin = xmin; + font->head->xMax = xmax; + font->head->yMin = ymin; + font->head->yMax = ymax; + FREE(stated); +} + +static void statMaxp(otfcc_Font *font) { + uint16_t nestDepth = 0; + uint16_t nPoints = 0; + uint16_t nContours = 0; + uint16_t nComponents = 0; + uint16_t nCompositePoints = 0; + uint16_t nCompositeContours = 0; + uint16_t instSize = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + glyf_Glyph *g = font->glyf->items[j]; + if (g->contours.length > 0) { + if (g->stat.nPoints > nPoints) nPoints = g->stat.nPoints; + if (g->stat.nContours > nContours) nContours = g->stat.nContours; + } else if (g->references.length > 0) { + if (g->stat.nCompositePoints > nCompositePoints) + nCompositePoints = g->stat.nCompositePoints; + if (g->stat.nCompositeContours > nCompositeContours) + nCompositeContours = g->stat.nCompositeContours; + if (g->stat.nestDepth > nestDepth) nestDepth = g->stat.nestDepth; + if (g->references.length > nComponents) nComponents = g->references.length; + } + if (g->instructionsLength > instSize) instSize = g->instructionsLength; + } + font->maxp->maxPoints = nPoints; + font->maxp->maxContours = nContours; + font->maxp->maxCompositePoints = nCompositePoints; + font->maxp->maxCompositeContours = nCompositeContours; + font->maxp->maxComponentDepth = nestDepth; + font->maxp->maxComponentElements = nComponents; + font->maxp->maxSizeOfInstructions = instSize; +} + +static void statHmtx(otfcc_Font *font, const otfcc_Options *options) { + if (!font->glyf) return; + table_hmtx *hmtx; + NEW(hmtx); + glyphid_t count_a = font->glyf->length; + glyphid_t count_k = 0; + bool lsbAtX_0 = true; + if (font->subtype == FONTTYPE_CFF) { + // pass + } else { + while (count_a > 2 && iVQ.getStill(font->glyf->items[count_a - 1]->advanceWidth) == + iVQ.getStill(font->glyf->items[count_a - 2]->advanceWidth)) { + count_a--; + } + count_k = font->glyf->length - count_a; + } + NEW(hmtx->metrics, count_a); + NEW(hmtx->leftSideBearing, count_k); + + pos_t minLSB = 0x7FFF; + pos_t minRSB = 0x7FFF; + pos_t maxExtent = -0x8000; + length_t maxWidth = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + glyf_Glyph *g = font->glyf->items[j]; + if (iVQ.isZero(g->horizontalOrigin, 1.0 / 1000.0)) { + iVQ.replace(&g->horizontalOrigin, iVQ.neutral()); + } else { + lsbAtX_0 = false; + } + const pos_t hori = iVQ.getStill(g->horizontalOrigin); + const pos_t advw = iVQ.getStill(g->advanceWidth); + const pos_t lsb = (g->stat.xMin) - hori; + const pos_t rsb = advw + hori - (g->stat.xMax); + + if (j < count_a) { + hmtx->metrics[j].advanceWidth = advw; + hmtx->metrics[j].lsb = lsb; + } else { + hmtx->leftSideBearing[j - count_a] = lsb; + } + + if (advw > maxWidth) maxWidth = advw; + if (lsb < minLSB) minLSB = lsb; + if (rsb < minRSB) minRSB = rsb; + if (g->stat.xMax - hori > maxExtent) { maxExtent = g->stat.xMax - hori; } + } + font->hhea->numberOfMetrics = count_a; + font->hhea->minLeftSideBearing = minLSB; + font->hhea->minRightSideBearing = minRSB; + font->hhea->xMaxExtent = maxExtent; + font->hhea->advanceWidthMax = maxWidth; + font->hmtx = hmtx; + // set bit 1 in head.flags + font->head->flags = (font->head->flags & (~0x2)) | (lsbAtX_0 ? 0x2 : 0); +} +static void statVmtx(otfcc_Font *font, const otfcc_Options *options) { + if (!font->glyf) return; + table_vmtx *vmtx; + NEW(vmtx); + glyphid_t count_a = font->glyf->length; + glyphid_t count_k = 0; + if (font->subtype == FONTTYPE_CFF && !options->cff_short_vmtx) { + // pass + } else { + while (count_a > 2 && iVQ.getStill(font->glyf->items[count_a - 1]->advanceHeight) == + iVQ.getStill(font->glyf->items[count_a - 2]->advanceHeight)) { + count_a--; + } + count_k = font->glyf->length - count_a; + } + NEW(vmtx->metrics, count_a); + NEW(vmtx->topSideBearing, count_k); + + pos_t minTSB = 0x7FFF; + pos_t minBSB = 0x7FFF; + pos_t maxExtent = -0x8000; + length_t maxHeight = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + glyf_Glyph *g = font->glyf->items[j]; + const pos_t vori = iVQ.getStill(g->verticalOrigin); + const pos_t advh = iVQ.getStill(g->advanceHeight); + const pos_t tsb = (vori) - (g->stat.yMax); + const pos_t bsb = (g->stat.yMin) - (vori) + (advh); + if (j < count_a) { + vmtx->metrics[j].advanceHeight = advh; + vmtx->metrics[j].tsb = tsb; + } else { + vmtx->topSideBearing[j - count_a] = tsb; + } + if (advh > maxHeight) maxHeight = advh; + if (tsb < minTSB) minTSB = tsb; + if (bsb < minBSB) minBSB = bsb; + if (vori - g->stat.yMin > maxExtent) { maxExtent = vori - g->stat.yMin; } + } + font->vhea->numOfLongVerMetrics = count_a; + font->vhea->minTop = minTSB; + font->vhea->minBottom = minBSB; + font->vhea->yMaxExtent = maxExtent; + font->vhea->advanceHeightMax = maxHeight; + font->vmtx = vmtx; +} +static void statOS_2UnicodeRanges(otfcc_Font *font, const otfcc_Options *options) { + cmap_Entry *item; + // Stat for OS/2.ulUnicodeRange. + uint32_t u1 = 0; + uint32_t u2 = 0; + uint32_t u3 = 0; + uint32_t u4 = 0; + int32_t minUnicode = 0xFFFF; + int32_t maxUnicode = 0; + foreach_hash(item, font->cmap->unicodes) { + int u = item->unicode; + // Stat for minimium and maximium unicode + if (u < minUnicode) minUnicode = u; + if (u > maxUnicode) maxUnicode = u; + // Reference: https://www.microsoft.com/typography/otspec/os2.htm#ur + if ((u >= 0x0000 && u <= 0x007F)) { u1 |= (1 << 0); } + if ((u >= 0x0080 && u <= 0x00FF)) { u1 |= (1 << 1); } + if ((u >= 0x0100 && u <= 0x017F)) { u1 |= (1 << 2); } + if ((u >= 0x0180 && u <= 0x024F)) { u1 |= (1 << 3); } + if ((u >= 0x0250 && u <= 0x02AF) || (u >= 0x1D00 && u <= 0x1D7F) || + (u >= 0x1D80 && u <= 0x1DBF)) { + u1 |= (1 << 4); + } + if ((u >= 0x02B0 && u <= 0x02FF) || (u >= 0xA700 && u <= 0xA71F)) { u1 |= (1 << 5); } + if ((u >= 0x0300 && u <= 0x036F) || (u >= 0x1DC0 && u <= 0x1DFF)) { u1 |= (1 << 6); } + if ((u >= 0x0370 && u <= 0x03FF)) { u1 |= (1 << 7); } + if ((u >= 0x2C80 && u <= 0x2CFF)) { u1 |= (1 << 8); } + if ((u >= 0x0400 && u <= 0x04FF) || (u >= 0x0500 && u <= 0x052F) || + (u >= 0x2DE0 && u <= 0x2DFF) || (u >= 0xA640 && u <= 0xA69F)) { + u1 |= (1 << 9); + } + if ((u >= 0x0530 && u <= 0x058F)) { u1 |= (1 << 10); } + if ((u >= 0x0590 && u <= 0x05FF)) { u1 |= (1 << 11); } + if ((u >= 0xA500 && u <= 0xA63F)) { u1 |= (1 << 12); } + if ((u >= 0x0600 && u <= 0x06FF) || (u >= 0x0750 && u <= 0x077F)) { u1 |= (1 << 13); } + if ((u >= 0x07C0 && u <= 0x07FF)) { u1 |= (1 << 14); } + if ((u >= 0x0900 && u <= 0x097F)) { u1 |= (1 << 15); } + if ((u >= 0x0980 && u <= 0x09FF)) { u1 |= (1 << 16); } + if ((u >= 0x0A00 && u <= 0x0A7F)) { u1 |= (1 << 17); } + if ((u >= 0x0A80 && u <= 0x0AFF)) { u1 |= (1 << 18); } + if ((u >= 0x0B00 && u <= 0x0B7F)) { u1 |= (1 << 19); } + if ((u >= 0x0B80 && u <= 0x0BFF)) { u1 |= (1 << 20); } + if ((u >= 0x0C00 && u <= 0x0C7F)) { u1 |= (1 << 21); } + if ((u >= 0x0C80 && u <= 0x0CFF)) { u1 |= (1 << 22); } + if ((u >= 0x0D00 && u <= 0x0D7F)) { u1 |= (1 << 23); } + if ((u >= 0x0E00 && u <= 0x0E7F)) { u1 |= (1 << 24); } + if ((u >= 0x0E80 && u <= 0x0EFF)) { u1 |= (1 << 25); } + if ((u >= 0x10A0 && u <= 0x10FF) || (u >= 0x2D00 && u <= 0x2D2F)) { u1 |= (1 << 26); } + if ((u >= 0x1B00 && u <= 0x1B7F)) { u1 |= (1 << 27); } + if ((u >= 0x1100 && u <= 0x11FF)) { u1 |= (1 << 28); } + if ((u >= 0x1E00 && u <= 0x1EFF) || (u >= 0x2C60 && u <= 0x2C7F) || + (u >= 0xA720 && u <= 0xA7FF)) { + u1 |= (1 << 29); + } + if ((u >= 0x1F00 && u <= 0x1FFF)) { u1 |= (1 << 30); } + if ((u >= 0x2000 && u <= 0x206F) || (u >= 0x2E00 && u <= 0x2E7F)) { u1 |= (1 << 31); } + if ((u >= 0x2070 && u <= 0x209F)) { u2 |= (1 << 0); } + if ((u >= 0x20A0 && u <= 0x20CF)) { u2 |= (1 << 1); } + if ((u >= 0x20D0 && u <= 0x20FF)) { u2 |= (1 << 2); } + if ((u >= 0x2100 && u <= 0x214F)) { u2 |= (1 << 3); } + if ((u >= 0x2150 && u <= 0x218F)) { u2 |= (1 << 4); } + if ((u >= 0x2190 && u <= 0x21FF) || (u >= 0x27F0 && u <= 0x27FF) || + (u >= 0x2900 && u <= 0x297F) || (u >= 0x2B00 && u <= 0x2BFF)) { + u2 |= (1 << 5); + } + if ((u >= 0x2200 && u <= 0x22FF) || (u >= 0x2A00 && u <= 0x2AFF) || + (u >= 0x27C0 && u <= 0x27EF) || (u >= 0x2980 && u <= 0x29FF)) { + u2 |= (1 << 6); + } + if ((u >= 0x2300 && u <= 0x23FF)) { u2 |= (1 << 7); } + if ((u >= 0x2400 && u <= 0x243F)) { u2 |= (1 << 8); } + if ((u >= 0x2440 && u <= 0x245F)) { u2 |= (1 << 9); } + if ((u >= 0x2460 && u <= 0x24FF)) { u2 |= (1 << 10); } + if ((u >= 0x2500 && u <= 0x257F)) { u2 |= (1 << 11); } + if ((u >= 0x2580 && u <= 0x259F)) { u2 |= (1 << 12); } + if ((u >= 0x25A0 && u <= 0x25FF)) { u2 |= (1 << 13); } + if ((u >= 0x2600 && u <= 0x26FF)) { u2 |= (1 << 14); } + if ((u >= 0x2700 && u <= 0x27BF)) { u2 |= (1 << 15); } + if ((u >= 0x3000 && u <= 0x303F)) { u2 |= (1 << 16); } + if ((u >= 0x3040 && u <= 0x309F)) { u2 |= (1 << 17); } + if ((u >= 0x30A0 && u <= 0x30FF) || (u >= 0x31F0 && u <= 0x31FF)) { u2 |= (1 << 18); } + if ((u >= 0x3100 && u <= 0x312F) || (u >= 0x31A0 && u <= 0x31BF)) { u2 |= (1 << 19); } + if ((u >= 0x3130 && u <= 0x318F)) { u2 |= (1 << 20); } + if ((u >= 0xA840 && u <= 0xA87F)) { u2 |= (1 << 21); } + if ((u >= 0x3200 && u <= 0x32FF)) { u2 |= (1 << 22); } + if ((u >= 0x3300 && u <= 0x33FF)) { u2 |= (1 << 23); } + if ((u >= 0xAC00 && u <= 0xD7AF)) { u2 |= (1 << 24); } + if ((u >= 0xD800 && u <= 0xDFFF) || u > 0xFFFF) { u2 |= (1 << 25); } + if ((u >= 0x10900 && u <= 0x1091F)) { u2 |= (1 << 26); } + if ((u >= 0x4E00 && u <= 0x9FFF) || (u >= 0x2E80 && u <= 0x2EFF) || + (u >= 0x2F00 && u <= 0x2FDF) || (u >= 0x2FF0 && u <= 0x2FFF) || + (u >= 0x3400 && u <= 0x4DBF) || (u >= 0x20000 && u <= 0x2F7FF) || + (u >= 0x3190 && u <= 0x319F)) { + u2 |= (1 << 27); + } + if ((u >= 0xE000 && u <= 0xF8FF)) { u2 |= (1 << 28); } + if ((u >= 0x31C0 && u <= 0x31EF) || (u >= 0xF900 && u <= 0xFAFF) || + (u >= 0x2F800 && u <= 0x2FA1F)) { + u2 |= (1 << 29); + } + if ((u >= 0xFB00 && u <= 0xFB4F)) { u2 |= (1 << 30); } + if ((u >= 0xFB50 && u <= 0xFDFF)) { u2 |= (1 << 31); } + if ((u >= 0xFE20 && u <= 0xFE2F)) { u3 |= (1 << 0); } + if ((u >= 0xFE10 && u <= 0xFE1F) || (u >= 0xFE30 && u <= 0xFE4F)) { u3 |= (1 << 1); } + if ((u >= 0xFE50 && u <= 0xFE6F)) { u3 |= (1 << 2); } + if ((u >= 0xFE70 && u <= 0xFEFF)) { u3 |= (1 << 3); } + if ((u >= 0xFF00 && u <= 0xFFEF)) { u3 |= (1 << 4); } + if ((u >= 0xFFF0 && u <= 0xFFFF)) { u3 |= (1 << 5); } + if ((u >= 0x0F00 && u <= 0x0FFF)) { u3 |= (1 << 6); } + if ((u >= 0x0700 && u <= 0x074F)) { u3 |= (1 << 7); } + if ((u >= 0x0780 && u <= 0x07BF)) { u3 |= (1 << 8); } + if ((u >= 0x0D80 && u <= 0x0DFF)) { u3 |= (1 << 9); } + if ((u >= 0x1000 && u <= 0x109F)) { u3 |= (1 << 10); } + if ((u >= 0x1200 && u <= 0x137F) || (u >= 0x1380 && u <= 0x139F) || + (u >= 0x2D80 && u <= 0x2DDF)) { + u3 |= (1 << 11); + } + if ((u >= 0x13A0 && u <= 0x13FF)) { u3 |= (1 << 12); } + if ((u >= 0x1400 && u <= 0x167F)) { u3 |= (1 << 13); } + if ((u >= 0x1680 && u <= 0x169F)) { u3 |= (1 << 14); } + if ((u >= 0x16A0 && u <= 0x16FF)) { u3 |= (1 << 15); } + if ((u >= 0x1780 && u <= 0x17FF) || (u >= 0x19E0 && u <= 0x19FF)) { u3 |= (1 << 16); } + if ((u >= 0x1800 && u <= 0x18AF)) { u3 |= (1 << 17); } + if ((u >= 0x2800 && u <= 0x28FF)) { u3 |= (1 << 18); } + if ((u >= 0xA000 && u <= 0xA48F) || (u >= 0xA490 && u <= 0xA4CF)) { u3 |= (1 << 19); } + if ((u >= 0x1700 && u <= 0x171F) || (u >= 0x1720 && u <= 0x173F) || + (u >= 0x1740 && u <= 0x175F) || (u >= 0x1760 && u <= 0x177F)) { + u3 |= (1 << 20); + } + if ((u >= 0x10300 && u <= 0x1032F)) { u3 |= (1 << 21); } + if ((u >= 0x10330 && u <= 0x1034F)) { u3 |= (1 << 22); } + if ((u >= 0x10400 && u <= 0x1044F)) { u3 |= (1 << 23); } + if ((u >= 0x1D000 && u <= 0x1D0FF) || (u >= 0x1D100 && u <= 0x1D1FF) || + (u >= 0x1D200 && u <= 0x1D24F)) { + u3 |= (1 << 24); + } + if ((u >= 0x1D400 && u <= 0x1D7FF)) { u3 |= (1 << 25); } + if ((u >= 0xFF000 && u <= 0xFFFFD) || (u >= 0x100000 && u <= 0x10FFFD)) { u3 |= (1 << 26); } + if ((u >= 0xFE00 && u <= 0xFE0F) || (u >= 0xE0100 && u <= 0xE01EF)) { u3 |= (1 << 27); } + if ((u >= 0xE0000 && u <= 0xE007F)) { u3 |= (1 << 28); } + if ((u >= 0x1900 && u <= 0x194F)) { u3 |= (1 << 29); } + if ((u >= 0x1950 && u <= 0x197F)) { u3 |= (1 << 30); } + if ((u >= 0x1980 && u <= 0x19DF)) { u3 |= (1 << 31); } + if ((u >= 0x1A00 && u <= 0x1A1F)) { u4 |= (1 << 0); } + if ((u >= 0x2C00 && u <= 0x2C5F)) { u4 |= (1 << 1); } + if ((u >= 0x2D30 && u <= 0x2D7F)) { u4 |= (1 << 2); } + if ((u >= 0x4DC0 && u <= 0x4DFF)) { u4 |= (1 << 3); } + if ((u >= 0xA800 && u <= 0xA82F)) { u4 |= (1 << 4); } + if ((u >= 0x10000 && u <= 0x1007F) || (u >= 0x10080 && u <= 0x100FF) || + (u >= 0x10100 && u <= 0x1013F)) { + u4 |= (1 << 5); + } + if ((u >= 0x10140 && u <= 0x1018F)) { u4 |= (1 << 6); } + if ((u >= 0x10380 && u <= 0x1039F)) { u4 |= (1 << 7); } + if ((u >= 0x103A0 && u <= 0x103DF)) { u4 |= (1 << 8); } + if ((u >= 0x10450 && u <= 0x1047F)) { u4 |= (1 << 9); } + if ((u >= 0x10480 && u <= 0x104AF)) { u4 |= (1 << 10); } + if ((u >= 0x10800 && u <= 0x1083F)) { u4 |= (1 << 11); } + if ((u >= 0x10A00 && u <= 0x10A5F)) { u4 |= (1 << 12); } + if ((u >= 0x1D300 && u <= 0x1D35F)) { u4 |= (1 << 13); } + if ((u >= 0x12000 && u <= 0x123FF) || (u >= 0x12400 && u <= 0x1247F)) { u4 |= (1 << 14); } + if ((u >= 0x1D360 && u <= 0x1D37F)) { u4 |= (1 << 15); } + if ((u >= 0x1B80 && u <= 0x1BBF)) { u4 |= (1 << 16); } + if ((u >= 0x1C00 && u <= 0x1C4F)) { u4 |= (1 << 17); } + if ((u >= 0x1C50 && u <= 0x1C7F)) { u4 |= (1 << 18); } + if ((u >= 0xA880 && u <= 0xA8DF)) { u4 |= (1 << 19); } + if ((u >= 0xA900 && u <= 0xA92F)) { u4 |= (1 << 20); } + if ((u >= 0xA930 && u <= 0xA95F)) { u4 |= (1 << 21); } + if ((u >= 0xAA00 && u <= 0xAA5F)) { u4 |= (1 << 22); } + if ((u >= 0x10190 && u <= 0x101CF)) { u4 |= (1 << 23); } + if ((u >= 0x101D0 && u <= 0x101FF)) { u4 |= (1 << 24); } + if ((u >= 0x102A0 && u <= 0x102DF) || (u >= 0x10280 && u <= 0x1029F) || + (u >= 0x10920 && u <= 0x1093F)) { + u4 |= (1 << 25); + } + if ((u >= 0x1F030 && u <= 0x1F09F) || (u >= 0x1F000 && u <= 0x1F02F)) { u4 |= (1 << 26); } + } + if (!options->keep_unicode_ranges) { + font->OS_2->ulUnicodeRange1 = u1; + font->OS_2->ulUnicodeRange2 = u2; + font->OS_2->ulUnicodeRange3 = u3; + font->OS_2->ulUnicodeRange4 = u4; + } + if (minUnicode < 0x10000) { + font->OS_2->usFirstCharIndex = minUnicode; + } else { + font->OS_2->usFirstCharIndex = 0xFFFF; + } + if (maxUnicode < 0x10000) { + font->OS_2->usLastCharIndex = maxUnicode; + } else { + font->OS_2->usLastCharIndex = 0xFFFF; + } +} +static void statOS_2AverageWidth(otfcc_Font *font, const otfcc_Options *options) { + if (options->keep_average_char_width) return; + uint32_t totalWidth = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + const pos_t adw = iVQ.getStill(font->glyf->items[j]->advanceWidth); + if (adw > 0) { totalWidth += adw; } + } + font->OS_2->xAvgCharWidth = totalWidth / font->glyf->length; +} +static uint16_t statMaxContextOTL(const table_OTL *table) { + uint16_t maxc = 1; + foreach (otl_Lookup **lookup_, table->lookups) { + otl_Lookup *lookup = *lookup_; + switch (lookup->type) { + case otl_type_gpos_pair: + case otl_type_gpos_markToBase: + case otl_type_gpos_markToLigature: + case otl_type_gpos_markToMark: + if (maxc < 2) maxc = 2; + break; + case otl_type_gsub_ligature: + foreach (otl_Subtable **subtable_, lookup->subtables) { + subtable_gsub_ligature *subtable = (subtable_gsub_ligature *)*subtable_; + foreach (otl_GsubLigatureEntry *entry, *subtable) { + if (maxc < entry->from->numGlyphs) { maxc = entry->from->numGlyphs; } + }; + } + break; + case otl_type_gsub_chaining: + case otl_type_gpos_chaining: + foreach (otl_Subtable **subtable_, lookup->subtables) { + subtable_chaining *subtable = (subtable_chaining *)*subtable_; + if (maxc < subtable->rule.matchCount) maxc = subtable->rule.matchCount; + } + break; + case otl_type_gsub_reverse: + foreach (otl_Subtable **subtable_, lookup->subtables) { + subtable_gsub_reverse *subtable = (subtable_gsub_reverse *)*subtable_; + if (maxc < subtable->matchCount) maxc = subtable->matchCount; + } + break; + default:; + } + } + return maxc; +} +static void statMaxContext(otfcc_Font *font, const otfcc_Options *options) { + uint16_t maxc = 1; + if (font->GSUB) { + uint16_t maxc_gsub = statMaxContextOTL(font->GSUB); + if (maxc_gsub > maxc) maxc = maxc_gsub; + } + if (font->GPOS) { + uint16_t maxc_gpos = statMaxContextOTL(font->GPOS); + if (maxc_gpos > maxc) maxc = maxc_gpos; + } + font->OS_2->usMaxContext = maxc; +} +static void statOS_2(otfcc_Font *font, const otfcc_Options *options) { + statOS_2UnicodeRanges(font, options); + statOS_2AverageWidth(font, options); + statMaxContext(font, options); +} + +#define MAX_STAT_METRIC 4096 +static void statCFFWidths(otfcc_Font *font) { + if (!font->glyf || !font->CFF_) return; + // Stat the most frequent character width + uint32_t *frequency; + NEW(frequency, MAX_STAT_METRIC); + for (glyphid_t j = 0; j < font->glyf->length; j++) { + uint16_t intWidth = (uint16_t)iVQ.getStill(font->glyf->items[j]->advanceWidth); + if (intWidth < MAX_STAT_METRIC) { frequency[intWidth] += 1; } + } + uint16_t maxfreq = 0; + uint16_t maxj = 0; + for (uint16_t j = 0; j < MAX_STAT_METRIC; j++) { + if (frequency[j] > maxfreq) { + maxfreq = frequency[j]; + maxj = j; + } + } + // stat nominalWidthX + uint16_t nn = 0; + uint32_t nnsum = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + const pos_t adw = iVQ.getStill(font->glyf->items[j]->advanceWidth); + if (adw != maxj) { nn += 1, nnsum += adw; } + } + int16_t nominalWidthX = 0; + if (nn > 0) nominalWidthX = nnsum / nn; + if (font->CFF_->privateDict) { + font->CFF_->privateDict->defaultWidthX = maxj; + if (nn != 0) { font->CFF_->privateDict->nominalWidthX = nominalWidthX; } + } + if (font->CFF_->fdArray) { + for (tableid_t j = 0; j < font->CFF_->fdArrayCount; j++) { + font->CFF_->fdArray[j]->privateDict->defaultWidthX = maxj; + font->CFF_->fdArray[j]->privateDict->nominalWidthX = nominalWidthX; + } + } + FREE(frequency); +} + +static void statVORG(otfcc_Font *font) { + if (!font->glyf || !font->CFF_ || !font->vhea || !font->vmtx) return; + uint32_t *frequency; + NEW(frequency, MAX_STAT_METRIC); + for (glyphid_t j = 0; j < font->glyf->length; j++) { + const pos_t vori = iVQ.getStill(font->glyf->items[j]->verticalOrigin); + if (vori >= 0 && vori < MAX_STAT_METRIC) { frequency[(uint16_t)(vori)] += 1; } + } + // stat VORG.defaultVerticalOrigin + uint32_t maxfreq = 0; + glyphid_t maxj = 0; + for (glyphid_t j = 0; j < MAX_STAT_METRIC; j++) { + if (frequency[j] > maxfreq) { + maxfreq = frequency[j]; + maxj = j; + } + } + + table_VORG *vorg; + NEW(vorg); + vorg->defaultVerticalOrigin = maxj; + + glyphid_t nVertOrigs = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + const pos_t vori = iVQ.getStill(font->glyf->items[j]->verticalOrigin); + if (vori != maxj) { nVertOrigs += 1; } + } + vorg->numVertOriginYMetrics = nVertOrigs; + NEW(vorg->entries, nVertOrigs); + + glyphid_t jj = 0; + for (glyphid_t j = 0; j < font->glyf->length; j++) { + const pos_t vori = iVQ.getStill(font->glyf->items[j]->verticalOrigin); + if (vori != maxj) { + vorg->entries[jj].gid = j; + vorg->entries[jj].verticalOrigin = vori; + jj += 1; + } + } + FREE(frequency); + font->VORG = vorg; +} + +static void statLTSH(otfcc_Font *font) { + if (!font->glyf) return; + bool needLTSH = false; + for (glyphid_t j = 0; j < font->glyf->length; j++) + if (font->glyf->items[j]->yPel > 1) { needLTSH = true; } + if (!needLTSH) return; + + table_LTSH *ltsh; + NEW(ltsh); + ltsh->numGlyphs = font->glyf->length; + NEW(ltsh->yPels, ltsh->numGlyphs); + for (glyphid_t j = 0; j < font->glyf->length; j++) { + ltsh->yPels[j] = font->glyf->items[j]->yPel; + } + font->LTSH = ltsh; +} + +void otfcc_statFont(otfcc_Font *font, const otfcc_Options *options) { + if (font->glyf && font->head) { + statGlyf(font, options); + if (!options->keep_modified_time) { + font->head->modified = 2082844800 + (int64_t)time(NULL); + } + } + if (font->head && font->CFF_) { + table_CFF *cff = font->CFF_; + if (cff->fontBBoxBottom > font->head->yMin) cff->fontBBoxBottom = font->head->yMin; + if (cff->fontBBoxTop < font->head->yMax) cff->fontBBoxTop = font->head->yMax; + if (cff->fontBBoxLeft < font->head->xMin) cff->fontBBoxLeft = font->head->xMin; + if (cff->fontBBoxRight < font->head->xMax) cff->fontBBoxRight = font->head->xMax; + if (font->glyf && cff->isCID) { cff->cidCount = (uint32_t)font->glyf->length; } + if (cff->isCID) { + if (cff->fontMatrix) { + iVQ.dispose(&cff->fontMatrix->x); + iVQ.dispose(&cff->fontMatrix->y); + FREE(cff->fontMatrix); + cff->fontMatrix = NULL; + } + for (tableid_t j = 0; j < cff->fdArrayCount; j++) { + table_CFF *fd = cff->fdArray[j]; + if (fd->fontMatrix) { + iVQ.dispose(&fd->fontMatrix->x); + iVQ.dispose(&fd->fontMatrix->y); + FREE(fd->fontMatrix); + fd->fontMatrix = NULL; + } + if (font->head->unitsPerEm == 1000) { + fd->fontMatrix = NULL; + } else { + NEW(fd->fontMatrix); + fd->fontMatrix->a = 1.0 / font->head->unitsPerEm; + fd->fontMatrix->b = 0.0; + fd->fontMatrix->c = 0.0; + fd->fontMatrix->d = 1.0 / font->head->unitsPerEm; + fd->fontMatrix->x = iVQ.neutral(); + fd->fontMatrix->y = iVQ.neutral(); + } + } + } else { + if (font->head->unitsPerEm == 1000) { + cff->fontMatrix = NULL; + } else { + NEW(cff->fontMatrix); + cff->fontMatrix->a = 1.0 / font->head->unitsPerEm; + cff->fontMatrix->b = 0.0; + cff->fontMatrix->c = 0.0; + cff->fontMatrix->d = 1.0 / font->head->unitsPerEm; + cff->fontMatrix->x = iVQ.neutral(); + cff->fontMatrix->y = iVQ.neutral(); + } + } + + statCFFWidths(font); + } + if (font->glyf && font->maxp) { font->maxp->numGlyphs = (uint16_t)font->glyf->length; } + if (font->glyf && font->post) { font->post->maxMemType42 = (uint32_t)font->glyf->length; } + if (font->glyf && font->maxp && font->maxp->version == 0x10000) { + statMaxp(font); + if (font->fpgm && font->fpgm->length > font->maxp->maxSizeOfInstructions) { + font->maxp->maxSizeOfInstructions = font->fpgm->length; + } + if (font->prep && font->prep->length > font->maxp->maxSizeOfInstructions) { + font->maxp->maxSizeOfInstructions = font->prep->length; + } + } + if (font->OS_2 && font->cmap && font->glyf) statOS_2(font, options); + if (font->subtype == FONTTYPE_TTF) { + if (font->maxp) font->maxp->version = 0x00010000; + } else { + if (font->maxp) font->maxp->version = 0x00005000; + } + if (font->glyf && font->hhea) { statHmtx(font, options); } + if (font->glyf && font->vhea) { statVmtx(font, options), statVORG(font); } + statLTSH(font); +} + +void otfcc_unstatFont(otfcc_Font *font, const otfcc_Options *options) { + otfcc_iFont.deleteTable(font, OTFCC_CHR('h','d','m','x')); + otfcc_iFont.deleteTable(font, OTFCC_CHR('h','m','t','x')); + otfcc_iFont.deleteTable(font, OTFCC_CHR('V','O','R','G')); + otfcc_iFont.deleteTable(font, OTFCC_CHR('v','m','t','x')); + otfcc_iFont.deleteTable(font, OTFCC_CHR('L','T','S','H')); +} |