summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mfluadir/otfcc/lib/table/glyf/read.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/lib/table/glyf/read.c')
-rw-r--r--Build/source/texk/web2c/mfluadir/otfcc/lib/table/glyf/read.c707
1 files changed, 707 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/lib/table/glyf/read.c b/Build/source/texk/web2c/mfluadir/otfcc/lib/table/glyf/read.c
new file mode 100644
index 00000000000..4a88d7becc3
--- /dev/null
+++ b/Build/source/texk/web2c/mfluadir/otfcc/lib/table/glyf/read.c
@@ -0,0 +1,707 @@
+#include "../glyf.h"
+
+#include "support/util.h"
+#include "support/ttinstr/ttinstr.h"
+
+static glyf_Point *next_point(glyf_ContourList *contours, shapeid_t *cc, shapeid_t *cp) {
+ if (*cp >= contours->items[*cc].length) {
+ *cp = 0;
+ *cc += 1;
+ }
+ return &contours->items[*cc].items[(*cp)++];
+}
+
+static glyf_Glyph *otfcc_read_simple_glyph(font_file_pointer start, shapeid_t numberOfContours,
+ const otfcc_Options *options) {
+ glyf_Glyph *g = otfcc_newGlyf_glyph();
+ glyf_ContourList *contours = &g->contours;
+
+ shapeid_t pointsInGlyph = 0;
+ for (shapeid_t j = 0; j < numberOfContours; j++) {
+ shapeid_t lastPointInCurrentContour = read_16u(start + 2 * j);
+ glyf_Contour contour;
+ glyf_iContour.init(&contour);
+ glyf_iContour.fill(&contour, lastPointInCurrentContour - pointsInGlyph + 1);
+ glyf_iContourList.push(contours, contour);
+ pointsInGlyph = lastPointInCurrentContour + 1;
+ }
+ uint16_t instructionLength = read_16u(start + 2 * numberOfContours);
+ uint8_t *instructions = NULL;
+ if (instructionLength > 0) {
+ NEW(instructions, instructionLength);
+ memcpy(instructions, start + 2 * numberOfContours + 2, sizeof(uint8_t) * instructionLength);
+ }
+ g->instructionsLength = instructionLength;
+ g->instructions = instructions;
+
+ // read flags
+ // There are repeating entries in the flags list, we will fill out the
+ // result
+ font_file_pointer flags;
+ NEW(flags, pointsInGlyph);
+ font_file_pointer flagStart = start + 2 * numberOfContours + 2 + instructionLength;
+ shapeid_t flagsReadSofar = 0;
+ shapeid_t flagBytesReadSofar = 0;
+
+ shapeid_t currentContour = 0;
+ shapeid_t currentContourPointIndex = 0;
+ while (flagsReadSofar < pointsInGlyph) {
+ uint8_t flag = flagStart[flagBytesReadSofar];
+ flags[flagsReadSofar] = flag;
+ flagBytesReadSofar += 1;
+ flagsReadSofar += 1;
+ next_point(contours, &currentContour, &currentContourPointIndex)->onCurve =
+ (flag & GLYF_FLAG_ON_CURVE);
+ if (flag & GLYF_FLAG_REPEAT) { // repeating flag
+ uint8_t repeat = flagStart[flagBytesReadSofar];
+ flagBytesReadSofar += 1;
+ for (uint8_t j = 0; j < repeat; j++) {
+ flags[flagsReadSofar + j] = flag;
+ next_point(contours, &currentContour, &currentContourPointIndex)->onCurve =
+ (flag & GLYF_FLAG_ON_CURVE);
+ }
+ flagsReadSofar += repeat;
+ }
+ }
+
+ // read X coordinates
+ font_file_pointer coordinatesStart = flagStart + flagBytesReadSofar;
+ uint32_t coordinatesOffset = 0;
+ shapeid_t coordinatesRead = 0;
+ currentContour = 0;
+ currentContourPointIndex = 0;
+ while (coordinatesRead < pointsInGlyph) {
+ uint8_t flag = flags[coordinatesRead];
+ int16_t x;
+ if (flag & GLYF_FLAG_X_SHORT) {
+ x = (flag & GLYF_FLAG_POSITIVE_X ? 1 : -1) *
+ read_8u(coordinatesStart + coordinatesOffset);
+ coordinatesOffset += 1;
+ } else {
+ if (flag & GLYF_FLAG_SAME_X) {
+ x = 0;
+ } else {
+ x = read_16s(coordinatesStart + coordinatesOffset);
+ coordinatesOffset += 2;
+ }
+ }
+ iVQ.replace(&(next_point(contours, &currentContour, &currentContourPointIndex)->x),
+ iVQ.createStill(x));
+ coordinatesRead += 1;
+ }
+ // read Y, identical to X
+ coordinatesRead = 0;
+ currentContour = 0;
+ currentContourPointIndex = 0;
+ while (coordinatesRead < pointsInGlyph) {
+ uint8_t flag = flags[coordinatesRead];
+ int16_t y;
+ if (flag & GLYF_FLAG_Y_SHORT) {
+ y = (flag & GLYF_FLAG_POSITIVE_Y ? 1 : -1) *
+ read_8u(coordinatesStart + coordinatesOffset);
+ coordinatesOffset += 1;
+ } else {
+ if (flag & GLYF_FLAG_SAME_Y) {
+ y = 0;
+ } else {
+ y = read_16s(coordinatesStart + coordinatesOffset);
+ coordinatesOffset += 2;
+ }
+ }
+ iVQ.replace(&(next_point(contours, &currentContour, &currentContourPointIndex)->y),
+ iVQ.createStill(y));
+ coordinatesRead += 1;
+ }
+ FREE(flags);
+
+ // turn deltas to absolute coordiantes
+ VQ cx = iVQ.neutral(), cy = iVQ.neutral();
+ for (shapeid_t j = 0; j < numberOfContours; j++) {
+ for (shapeid_t k = 0; k < contours->items[j].length; k++) {
+ glyf_Point *z = &contours->items[j].items[k];
+ iVQ.inplacePlus(&cx, z->x);
+ iVQ.inplacePlus(&cy, z->y);
+ iVQ.copyReplace(&z->x, cx);
+ iVQ.copyReplace(&z->y, cy);
+ }
+ glyf_iContour.shrinkToFit(&contours->items[j]);
+ }
+ glyf_iContourList.shrinkToFit(contours);
+ iVQ.dispose(&cx), iVQ.dispose(&cy);
+ return g;
+}
+
+static glyf_Glyph *otfcc_read_composite_glyph(font_file_pointer start,
+ const otfcc_Options *options) {
+ glyf_Glyph *g = otfcc_newGlyf_glyph();
+
+ // pass 1, read references quantity
+ uint16_t flags = 0;
+ uint32_t offset = 0;
+ bool glyphHasInstruction = false;
+ do {
+ flags = read_16u(start + offset);
+ glyphid_t index = read_16u(start + offset + 2);
+
+ glyf_ComponentReference ref = glyf_iComponentReference.empty();
+ ref.glyph = Handle.fromIndex(index);
+
+ offset += 4; // flags & index
+ if (flags & ARGS_ARE_XY_VALUES) {
+ ref.isAnchored = REF_XY;
+ if (flags & ARG_1_AND_2_ARE_WORDS) {
+ ref.x = iVQ.createStill(read_16s(start + offset));
+ ref.y = iVQ.createStill(read_16s(start + offset + 2));
+ offset += 4;
+ } else {
+ ref.x = iVQ.createStill(read_8s(start + offset));
+ ref.y = iVQ.createStill(read_8s(start + offset + 1));
+ offset += 2;
+ }
+ } else {
+ ref.isAnchored = REF_ANCHOR_ANCHOR;
+ if (flags & ARG_1_AND_2_ARE_WORDS) {
+ ref.outer = read_16u(start + offset);
+ ref.inner = read_16u(start + offset + 2);
+ offset += 4;
+ } else {
+ ref.outer = read_8u(start + offset);
+ ref.inner = read_8u(start + offset + 1);
+ offset += 2;
+ }
+ }
+ if (flags & WE_HAVE_A_SCALE) {
+ ref.a = ref.d = otfcc_from_f2dot14(read_16s(start + offset));
+ offset += 2;
+ } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
+ ref.a = otfcc_from_f2dot14(read_16s(start + offset));
+ ref.d = otfcc_from_f2dot14(read_16s(start + offset + 2));
+ offset += 4;
+ } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
+ ref.a = otfcc_from_f2dot14(read_16s(start + offset));
+ ref.b = otfcc_from_f2dot14(read_16s(start + offset + 2));
+ ref.c = otfcc_from_f2dot14(read_16s(start + offset + 4));
+ ref.d = otfcc_from_f2dot14(read_16s(start + offset + 6));
+ offset += 8;
+ }
+ ref.roundToGrid = flags & ROUND_XY_TO_GRID;
+ ref.useMyMetrics = flags & USE_MY_METRICS;
+ if (flags & SCALED_COMPONENT_OFFSET &&
+ (flags & WE_HAVE_AN_X_AND_Y_SCALE || flags & WE_HAVE_A_TWO_BY_TWO)) {
+ logWarning("glyf: SCALED_COMPONENT_OFFSET is not supported.")
+ }
+ if (flags & WE_HAVE_INSTRUCTIONS) { glyphHasInstruction = true; }
+ glyf_iReferenceList.push(&g->references, ref);
+ } while (flags & MORE_COMPONENTS);
+
+ if (glyphHasInstruction) {
+ uint16_t instructionLength = read_16u(start + offset);
+ font_file_pointer instructions = NULL;
+ if (instructionLength > 0) {
+ NEW(instructions, instructionLength);
+ memcpy(instructions, start + offset + 2, sizeof(uint8_t) * instructionLength);
+ }
+ g->instructionsLength = instructionLength;
+ g->instructions = instructions;
+ } else {
+ g->instructionsLength = 0;
+ g->instructions = NULL;
+ }
+
+ return g;
+}
+
+static glyf_Glyph *otfcc_read_glyph(font_file_pointer data, uint32_t offset,
+ const otfcc_Options *options) {
+ font_file_pointer start = data + offset;
+ int16_t numberOfContours = read_16u(start);
+ glyf_Glyph *g;
+ if (numberOfContours > 0) {
+ g = otfcc_read_simple_glyph(start + 10, numberOfContours, options);
+ } else {
+ g = otfcc_read_composite_glyph(start + 10, options);
+ }
+ g->stat.xMin = read_16s(start + 2);
+ g->stat.yMin = read_16s(start + 4);
+ g->stat.xMax = read_16s(start + 6);
+ g->stat.yMax = read_16s(start + 8);
+ return g;
+}
+
+// common states of tuple polymorphizer
+
+typedef struct {
+ table_fvar *fvar;
+ uint16_t dimensions;
+ uint16_t sharedTupleCount;
+ f2dot14 *sharedTuples;
+ uint8_t coordDimensions;
+ bool allowIUP;
+ shapeid_t nPhantomPoints;
+} TuplePolymorphizerCtx;
+
+// GVAR header
+#pragma pack(1)
+struct GVARHeader {
+ uint16_t majorVersion;
+ uint16_t minorVersion;
+ uint16_t axisCount;
+ uint16_t sharedTupleCount;
+ uint32_t sharedTuplesOffset;
+ uint16_t glyphCount;
+ uint16_t flags;
+ uint32_t glyphVariationDataArrayOffset;
+};
+
+struct TupleVariationHeader {
+ uint16_t variationDataSize;
+ uint16_t tupleIndex;
+};
+struct GlyphVariationData {
+ uint16_t tupleVariationCount;
+ uint16_t dataOffset;
+ struct TupleVariationHeader tvhs[];
+};
+#pragma pack()
+
+#define GVAR_OFFSETS_ARE_LONG 1
+#define EMBEDDED_PEAK_TUPLE 0x8000
+#define INTERMEDIATE_REGION 0x4000
+#define PRIVATE_POINT_NUMBERS 0x2000
+#define TUPLE_INDEX_MASK 0xFFF
+
+static INLINE struct TupleVariationHeader *nextTVH(struct TupleVariationHeader *currentHeader,
+ const TuplePolymorphizerCtx *ctx) {
+ uint32_t bump = 2 * sizeof(uint16_t);
+ uint16_t tupleIndex = be16(currentHeader->tupleIndex);
+ if (tupleIndex & EMBEDDED_PEAK_TUPLE) bump += ctx->dimensions * sizeof(f2dot14);
+ if (tupleIndex & INTERMEDIATE_REGION) bump += 2 * ctx->dimensions * sizeof(f2dot14);
+ return (struct TupleVariationHeader *)((font_file_pointer)currentHeader + bump);
+}
+
+#define POINT_COUNT_IS_WORD 0x80
+#define POINT_COUNT_LONG_MASK 0x7FFF
+#define POINT_RUN_COUNT_MASK 0x7F
+#define POINTS_ARE_WORDS 0x80
+
+static INLINE font_file_pointer parsePointNumbers(font_file_pointer data, shapeid_t **pointIndeces,
+ shapeid_t *pc, shapeid_t totalPoints) {
+ uint16_t nPoints = 0;
+ uint8_t firstByte = *data;
+ if (firstByte & POINT_COUNT_IS_WORD) {
+ nPoints = (data[0] << 8 | data[1]) & POINT_COUNT_LONG_MASK;
+ data += 2;
+ } else {
+ nPoints = firstByte;
+ data++;
+ }
+ if (nPoints > 0) {
+ struct {
+ shapeid_t length;
+ bool wide;
+ } run = {0, false};
+ shapeid_t filled = 0;
+ shapeid_t jPoint = 0;
+ NEW_CLEAN_N(*pointIndeces, nPoints);
+ while (filled < nPoints) {
+ if (run.length == 0) {
+ uint8_t runHeader = *data++;
+ run.wide = !!(runHeader & POINTS_ARE_WORDS);
+ run.length = (runHeader & POINT_RUN_COUNT_MASK) + 1;
+ }
+ int16_t pointNumber = jPoint;
+ if (run.wide) {
+ pointNumber += *((uint16_t *)data);
+ data += 2;
+ } else {
+ pointNumber += *data++;
+ }
+ (*pointIndeces)[filled] = pointNumber;
+ filled++;
+ jPoint = pointNumber;
+ --run.length;
+ }
+ *pc = nPoints;
+ } else {
+ NEW_CLEAN_N(*pointIndeces, totalPoints);
+ for (shapeid_t j = 0; j < totalPoints; j++) {
+ (*pointIndeces)[j] = j;
+ }
+ *pc = totalPoints;
+ }
+
+ return data;
+}
+
+#define DELTAS_ARE_ZERO 0x80
+#define DELTAS_ARE_WORDS 0x40
+#define DELTA_RUN_COUNT_MASK 0x3F
+
+static INLINE font_file_pointer readPackedDelta(font_file_pointer data, shapeid_t nPoints,
+ pos_t *deltas) {
+ struct {
+ shapeid_t length;
+ bool wide;
+ bool zero;
+ } run = {0, false, false};
+ shapeid_t filled = 0;
+ while (filled < nPoints) {
+ int16_t delta = 0;
+ if (run.length == 0) {
+ uint8_t runHeader = *data++;
+ run.zero = runHeader & DELTAS_ARE_ZERO;
+ run.wide = runHeader & DELTAS_ARE_WORDS;
+ run.length = (runHeader & DELTA_RUN_COUNT_MASK) + 1;
+ }
+ if (!run.zero) {
+ if (run.wide) {
+ delta = (int16_t)be16(*(uint16_t *)data);
+ data += 2;
+ } else {
+ delta = (int8_t)*data;
+ data++;
+ }
+ }
+ deltas[filled] = (pos_t)delta;
+ filled++;
+ --run.length;
+ }
+ return data;
+}
+
+typedef VQ *(*CoordPartGetter)(glyf_Point *z);
+
+static VQ *getX(glyf_Point *z) {
+ return &z->x;
+}
+static VQ *getY(glyf_Point *z) {
+ return &z->y;
+}
+
+static INLINE void fillTheGaps(shapeid_t jMin, shapeid_t jMax, vq_Segment *nudges,
+ glyf_Point **glyphRefs, CoordPartGetter getter) {
+ for (shapeid_t j = jMin; j < jMax; j++) {
+ if (nudges[j].val.delta.touched) continue;
+ // get next knot
+ shapeid_t jNext = j;
+ while (!nudges[jNext].val.delta.touched) {
+ if (jNext == jMax - 1) {
+ jNext = jMin;
+ } else {
+ jNext += 1;
+ }
+ if (jNext == j) break;
+ }
+ // get pre knot
+ shapeid_t jPrev = j;
+ while (!nudges[jPrev].val.delta.touched) {
+ if (jPrev == jMin) {
+ jPrev = jMax - 1;
+ } else {
+ jPrev -= 1;
+ }
+ if (jPrev == j) break;
+ }
+ if (nudges[jNext].val.delta.touched && nudges[jPrev].val.delta.touched) {
+ f16dot16 untouchJ = otfcc_to_fixed(getter(glyphRefs[j])->kernel);
+ f16dot16 untouchPrev = otfcc_to_fixed(getter(glyphRefs[jPrev])->kernel);
+ f16dot16 untouchNext = otfcc_to_fixed(getter(glyphRefs[jNext])->kernel);
+ f16dot16 deltaPrev = otfcc_to_fixed(nudges[jPrev].val.delta.quantity);
+ f16dot16 deltaNext = otfcc_to_fixed(nudges[jNext].val.delta.quantity);
+
+ f16dot16 uMin = untouchPrev;
+ f16dot16 uMax = untouchNext;
+ f16dot16 dMin = deltaPrev;
+ f16dot16 dMax = deltaNext;
+ if (untouchPrev > untouchNext) {
+ uMin = untouchNext;
+ uMax = untouchPrev;
+ dMin = deltaNext;
+ dMax = deltaPrev;
+ }
+ if (untouchJ <= uMin) {
+ nudges[j].val.delta.quantity = otfcc_from_fixed(dMin);
+ } else if (untouchJ >= uMax) {
+ nudges[j].val.delta.quantity = otfcc_from_fixed(dMax);
+ } else {
+ nudges[j].val.delta.quantity =
+ otfcc_from_fixed(otfcc_f1616_muldiv(dMax - dMin, untouchJ - uMin, uMax - uMin));
+ }
+ }
+ }
+}
+
+static void applyCoords(const shapeid_t totalPoints, glyf_Glyph *glyph,
+ glyf_Point **glyphRefs, // target
+ const shapeid_t nTouchedPoints, const pos_t *tupleDelta,
+ const shapeid_t *points,
+ const vq_Region *r, // data
+ CoordPartGetter getter) {
+ vq_Segment *nudges;
+ NEW_CLEAN_N(nudges, totalPoints);
+ for (shapeid_t j = 0; j < totalPoints; j++) {
+ nudges[j].type = VQ_DELTA;
+ nudges[j].val.delta.touched = false;
+ nudges[j].val.delta.quantity = 0;
+ nudges[j].val.delta.region = r;
+ }
+ for (shapeid_t j = 0; j < nTouchedPoints; j++) {
+ if (points[j] >= totalPoints) continue;
+ nudges[points[j]].val.delta.touched = true;
+ nudges[points[j]].val.delta.quantity += tupleDelta[j];
+ }
+ // fill the gaps
+ shapeid_t jFirst = 0;
+ foreach (glyf_Contour *c, glyph->contours) {
+ fillTheGaps(jFirst, jFirst + c->length, nudges, glyphRefs, getX);
+ jFirst += c->length;
+ }
+ for (shapeid_t j = 0; j < totalPoints; j++) {
+ if (!nudges[j].val.delta.quantity && nudges[j].val.delta.touched) continue;
+ VQ *coordinatePart = getter(glyphRefs[j]);
+ vq_iSegList.push(&(coordinatePart->shift), nudges[j]);
+ }
+ FREE(nudges);
+}
+
+static INLINE void applyPolymorphism(const shapeid_t totalPoints, glyf_GlyphPtr glyph, // target
+ const shapeid_t nTouchedPoints, const shapeid_t *points,
+ const pos_t *deltaX, const pos_t *deltaY,
+ const vq_Region *r // delta data
+) {
+ glyf_Point **glyphRefs;
+ NEW_CLEAN_N(glyphRefs, totalPoints);
+ {
+ shapeid_t j = 0;
+ foreach (glyf_Contour *c, glyph->contours) {
+ foreach (glyf_Point *g, *c) { glyphRefs[j++] = g; }
+ }
+ foreach (glyf_ComponentReference *r, glyph->references) {
+ // in glyf_ComponentReference, we also have a X and a Y entry
+ // so a trick of conversion
+ glyphRefs[j++] = (glyf_Point *)&(r->x);
+ }
+ }
+ applyCoords(totalPoints, glyph, glyphRefs, nTouchedPoints, deltaX, points, r, getX);
+ applyCoords(totalPoints, glyph, glyphRefs, nTouchedPoints, deltaY, points, r, getY);
+ // Horizontal phantom point
+ if (totalPoints + 1 < nTouchedPoints) {
+ iVQ.addDelta(&(glyph->horizontalOrigin), true, r, deltaX[totalPoints]);
+ iVQ.addDelta(&(glyph->advanceWidth), true, r,
+ deltaX[totalPoints + 1] - deltaX[totalPoints]);
+ }
+ // Vertical phantom point
+ if (totalPoints + 3 < nTouchedPoints) {
+ iVQ.addDelta(&(glyph->verticalOrigin), true, r, deltaY[totalPoints + 2]);
+ iVQ.addDelta(&(glyph->advanceHeight), true, r,
+ deltaY[totalPoints + 2] - deltaY[totalPoints + 3]);
+ }
+
+ FREE(glyphRefs);
+}
+
+static vq_Region *createRegionFromTuples(uint16_t dimensions, f2dot14 *peak, f2dot14 *start,
+ f2dot14 *end) {
+ vq_Region *r = vq_createRegion(dimensions);
+ for (uint16_t d = 0; d < dimensions; d++) {
+ pos_t peakVal = otfcc_from_f2dot14(be16(peak[d]));
+ vq_AxisSpan span = {peakVal <= 0 ? -1 : 0, peakVal, peakVal >= 0 ? 1 : 0};
+ if (start && end) {
+ span.start = otfcc_from_f2dot14(be16(start[d]));
+ span.end = otfcc_from_f2dot14(be16(end[d]));
+ }
+ r->spans[d] = span;
+ }
+ return r;
+}
+
+static INLINE void polymorphizeGlyph(glyphid_t gid, glyf_GlyphPtr glyph,
+ const TuplePolymorphizerCtx *ctx,
+ struct GlyphVariationData *gvd, const otfcc_Options *options) {
+
+ shapeid_t totalPoints = 0;
+ foreach (glyf_Contour *c, glyph->contours) { totalPoints += c->length; }
+ totalPoints += glyph->references.length;
+ shapeid_t totalDeltaEntries = totalPoints + ctx->nPhantomPoints;
+
+ uint16_t nTuples = be16(gvd->tupleVariationCount) & 0xFFF;
+ struct TupleVariationHeader *tvh = gvd->tvhs;
+
+ bool hasSharedPointNumbers = be16(gvd->tupleVariationCount) & 0x8000;
+ shapeid_t sharedPointCount = 0;
+ shapeid_t *sharedPointIndeces = NULL;
+ font_file_pointer data = (font_file_pointer)gvd + be16(gvd->dataOffset);
+ if (hasSharedPointNumbers) {
+ data = parsePointNumbers(data, &sharedPointIndeces, &sharedPointCount, totalDeltaEntries);
+ }
+
+ size_t tsdStart = 0;
+
+ for (uint16_t j = 0; j < nTuples; j++) {
+
+ // Tuple options
+ shapeid_t tupleIndex = be16(tvh->tupleIndex) & TUPLE_INDEX_MASK;
+ bool hasEmbeddedPeak = be16(tvh->tupleIndex) & EMBEDDED_PEAK_TUPLE;
+ bool hasIntermediate = be16(tvh->tupleIndex) & INTERMEDIATE_REGION;
+
+ // Peak tuple
+ f2dot14 *peak = NULL;
+ if (hasEmbeddedPeak) {
+ peak = (f2dot14 *)(((font_file_pointer)tvh) + 4);
+ } else {
+ peak = ctx->sharedTuples + ctx->dimensions * tupleIndex;
+ }
+
+ // Intermediate tuple -- if present
+ f2dot14 *start = NULL;
+ f2dot14 *end = NULL;
+ if (hasIntermediate) {
+ start = (f2dot14 *)(((font_file_pointer)tvh) + 4 +
+ 2 * (hasEmbeddedPeak ? 1 : 0) * ctx->dimensions);
+ end = (f2dot14 *)(((font_file_pointer)tvh) + 4 +
+ 2 * (hasEmbeddedPeak ? 2 : 1) * ctx->dimensions);
+ }
+
+ const vq_Region *r = table_iFvar.registerRegion(
+ ctx->fvar, createRegionFromTuples(ctx->dimensions, peak, start, end));
+
+ // Pointer of tuple serialized data
+ font_file_pointer tsd = data + tsdStart;
+ // Point number term
+ shapeid_t nPoints = sharedPointCount;
+ shapeid_t *pointIndeces = sharedPointIndeces;
+
+ if (be16(tvh->tupleIndex) & PRIVATE_POINT_NUMBERS) {
+ shapeid_t privatePointCount = 0;
+ shapeid_t *privatePointNumbers = NULL;
+ tsd =
+ parsePointNumbers(tsd, &privatePointNumbers, &privatePointCount, totalDeltaEntries);
+ nPoints = privatePointCount;
+ pointIndeces = privatePointNumbers;
+ }
+ if (pointIndeces) {
+ // Delta term
+ pos_t *deltaX = 0;
+ pos_t *deltaY = 0;
+ NEW_CLEAN_N(deltaX, nPoints);
+ NEW_CLEAN_N(deltaY, nPoints);
+
+ tsd = readPackedDelta(tsd, nPoints, deltaX);
+ tsd = readPackedDelta(tsd, nPoints, deltaY);
+
+ // Do polymorphize
+ applyPolymorphism(totalPoints, glyph, nPoints, pointIndeces, deltaX, deltaY, r);
+ FREE(deltaX);
+ FREE(deltaY);
+ }
+ // Cleanup
+ if (be16(tvh->tupleIndex) & PRIVATE_POINT_NUMBERS) { FREE(pointIndeces); }
+ tsdStart += be16(tvh->variationDataSize);
+
+ tvh = nextTVH(tvh, ctx);
+ }
+ FREE(sharedPointIndeces);
+}
+
+// NOTE: for polymorphize, we would
+// TODO: polymorphize advanceWidth, verticalOrigin and advanceHeight
+static INLINE void polymorphize(const otfcc_Packet packet, const otfcc_Options *options,
+ table_glyf *glyf, const GlyfIOContext *ctx) {
+ if (!ctx->fvar || !ctx->fvar->axes.length) return;
+ FOR_TABLE(OTFCC_CHR('g','v','a','r'), table) {
+ font_file_pointer data = table.data;
+ if (table.length < sizeof(struct GVARHeader)) return;
+ struct GVARHeader *header = (struct GVARHeader *)data;
+ if (be16(header->axisCount) != ctx->fvar->axes.length) {
+ logWarning("Axes number in GVAR and FVAR are inequal");
+ return;
+ };
+ for (glyphid_t j = 0; j < glyf->length; j++) {
+ TuplePolymorphizerCtx tpctx = {.fvar = ctx->fvar,
+ .dimensions = ctx->fvar->axes.length,
+ .nPhantomPoints = ctx->nPhantomPoints,
+ .sharedTupleCount = be16(header->sharedTupleCount),
+ .sharedTuples =
+ (f2dot14 *)(data + be32(header->sharedTuplesOffset)),
+ .coordDimensions = 2,
+ .allowIUP = glyf->items[j]->contours.length > 0};
+ uint32_t glyphVariationDataOffset = 0;
+ if (be16(header->flags) & GVAR_OFFSETS_ARE_LONG) {
+ glyphVariationDataOffset =
+ be32(((uint32_t *)(data + sizeof(struct GVARHeader)))[j]);
+ } else {
+ glyphVariationDataOffset =
+ 2 * be16(((uint16_t *)(data + sizeof(struct GVARHeader)))[j]);
+ }
+ struct GlyphVariationData *gvd =
+ (struct GlyphVariationData *)(data + be32(header->glyphVariationDataArrayOffset) +
+ glyphVariationDataOffset);
+ polymorphizeGlyph(j, glyf->items[j], &tpctx, gvd, options);
+ }
+ }
+}
+
+table_glyf *otfcc_readGlyf(const otfcc_Packet packet, const otfcc_Options *options,
+ const GlyfIOContext *ctx) {
+ uint32_t *offsets = NULL;
+ table_glyf *glyf = NULL;
+
+ NEW_CLEAN_N(offsets, (ctx->numGlyphs + 1));
+ if (!offsets) goto ABSENT;
+ bool foundLoca = false;
+
+ // read loca
+ FOR_TABLE(OTFCC_CHR('l','o','c','a'), table) {
+ font_file_pointer data = table.data;
+ uint32_t length = table.length;
+ if (length < 2 * ctx->numGlyphs + 2) goto LOCA_CORRUPTED;
+ for (uint32_t j = 0; j < ctx->numGlyphs + 1; j++) {
+ if (ctx->locaIsLong) {
+ offsets[j] = read_32u(data + j * 4);
+ } else {
+ offsets[j] = read_16u(data + j * 2) * 2;
+ }
+ if (j > 0 && offsets[j] < offsets[j - 1]) goto LOCA_CORRUPTED;
+ }
+ foundLoca = true;
+ break;
+ LOCA_CORRUPTED:
+ logWarning("table 'loca' corrupted.\n");
+ if (offsets) { FREE(offsets), offsets = NULL; }
+ continue;
+ }
+ if (!foundLoca) goto ABSENT;
+
+ // read glyf
+ FOR_TABLE(OTFCC_CHR('g','l','y','f'), table) {
+ font_file_pointer data = table.data;
+ uint32_t length = table.length;
+ if (length < offsets[ctx->numGlyphs]) goto GLYF_CORRUPTED;
+
+ glyf = table_iGlyf.create();
+
+ for (glyphid_t j = 0; j < ctx->numGlyphs; j++) {
+ if (offsets[j] < offsets[j + 1]) { // non-space glyph
+ table_iGlyf.push(glyf, otfcc_read_glyph(data, offsets[j], options));
+ } else { // space glyph
+ table_iGlyf.push(glyf, otfcc_newGlyf_glyph());
+ }
+ }
+ goto PRESENT;
+ GLYF_CORRUPTED:
+ logWarning("table 'glyf' corrupted.\n");
+ if (glyf) { DELETE(table_iGlyf.free, glyf), glyf = NULL; }
+ }
+ goto ABSENT;
+
+PRESENT:
+ if (offsets) { FREE(offsets), offsets = NULL; }
+ // We have glyf table read. Do polymorphize
+ polymorphize(packet, options, glyf, ctx);
+ return glyf;
+
+ABSENT:
+ if (offsets) { FREE(offsets), offsets = NULL; }
+ if (glyf) { FREE(glyf), glyf = NULL; }
+ return NULL;
+}