diff options
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.c | 707 |
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, ¤tContour, ¤tContourPointIndex)->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, ¤tContour, ¤tContourPointIndex)->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, ¤tContour, ¤tContourPointIndex)->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, ¤tContour, ¤tContourPointIndex)->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; +} |