summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mfluadir/otfcc/lib/otf-writer/stat.c
diff options
context:
space:
mode:
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.c706
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'));
+}