summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/cff-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/cff-parser.c')
-rw-r--r--Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/cff-parser.c847
1 files changed, 847 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/cff-parser.c b/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/cff-parser.c
new file mode 100644
index 00000000000..14ad9f2cecc
--- /dev/null
+++ b/Build/source/texk/web2c/mfluadir/otfcc/lib/libcff/cff-parser.c
@@ -0,0 +1,847 @@
+/*
+ Parser/Linter of CFF font, notable references:
+ * Technical Note #5176: The Compact Font Format Specification
+ * Technical Note #5177: The Type 2 Charstring Format
+ * Adobe TinTin
+*/
+
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libcff.h"
+
+static void parse_encoding(cff_File *cff, int32_t offset, cff_Encoding *enc) {
+ uint8_t *data = cff->raw_data;
+
+ if (offset == cff_ENC_STANDARD)
+ enc->t = cff_ENC_STANDARD;
+ else if (offset == cff_ENC_EXPERT)
+ enc->t = cff_ENC_EXPERT;
+ else {
+ switch (data[offset]) {
+ case 0:
+ enc->t = cff_ENC_FORMAT0;
+ {
+ enc->f0.format = 0;
+ enc->f0.ncodes = data[offset + 1];
+ NEW(enc->f0.code, enc->f0.ncodes);
+
+ for (uint32_t i = 0; i < enc->f0.ncodes; i++)
+ enc->f0.code[i] = data[offset + 2 + i];
+ }
+ break;
+ case 1:
+ enc->t = cff_ENC_FORMAT1;
+ {
+ enc->f1.format = 1;
+ enc->f1.nranges = data[offset + 1];
+ NEW(enc->f1.range1, enc->f1.nranges);
+
+ for (uint32_t i = 0; i < enc->f1.nranges; i++)
+ enc->f1.range1[i].first = data[offset + 2 + i * 2],
+ enc->f1.range1[i].nleft = data[offset + 3 + i * 2];
+ }
+ break;
+ default:
+ enc->t = cff_ENC_FORMAT_SUPPLEMENT;
+ {
+ enc->ns.nsup = data[offset];
+ NEW(enc->ns.supplement, enc->ns.nsup);
+
+ for (uint32_t i = 0; i < enc->ns.nsup; i++)
+ enc->ns.supplement[i].code = data[offset + 1 + i * 3],
+ enc->ns.supplement[i].glyph = gu2(data, offset + 2 + i * 3);
+ }
+ break;
+ }
+ }
+}
+
+static void parse_cff_bytecode(cff_File *cff, const otfcc_Options *options) {
+ uint32_t pos;
+ int32_t offset;
+
+ /* Header */
+ cff->head.major = gu1(cff->raw_data, 0);
+ cff->head.minor = gu1(cff->raw_data, 1);
+ cff->head.hdrSize = gu1(cff->raw_data, 2);
+ cff->head.offSize = gu1(cff->raw_data, 3);
+
+ /* Name INDEX */
+ pos = cff->head.hdrSize;
+ cff_iIndex.parse(cff->raw_data, pos, &cff->name);
+
+ /* Top Dict INDEX */
+ pos = 4 + cff_iIndex.getLength(&cff->name);
+ cff_iIndex.parse(cff->raw_data, pos, &cff->top_dict);
+
+ /** LINT CFF FONTSET **/
+
+ if (cff->name.count != cff->top_dict.count)
+ logWarning("[libcff] Bad CFF font: (%d, name), (%d, top_dict).\n", cff->name.count,
+ cff->top_dict.count);
+
+ /* String INDEX */
+ pos = 4 + cff_iIndex.getLength(&cff->name) + cff_iIndex.getLength(&cff->top_dict);
+ cff_iIndex.parse(cff->raw_data, pos, &cff->string);
+
+ /* Global Subr INDEX */
+ pos = 4 + cff_iIndex.getLength(&cff->name) + cff_iIndex.getLength(&cff->top_dict) +
+ cff_iIndex.getLength(&cff->string);
+ cff_iIndex.parse(cff->raw_data, pos, &cff->global_subr);
+
+ if (cff->top_dict.data != NULL) {
+ int32_t offset;
+
+ // cff->topdict.dict = calloc(1, sizeof(cff_Dict));
+ // cff->topdict.dict = cff_extract_Dict(cff->top_dict.data, cff->top_dict.offset[1] -
+ // cff->top_dict.offset[0]);
+
+ /* CharStrings INDEX */
+ offset =
+ cff_iDict
+ .parseDictKey(cff->top_dict.data, cff->top_dict.offset[1] - cff->top_dict.offset[0],
+ op_CharStrings, 0)
+ .i;
+
+ if (offset != -1) {
+ cff_iIndex.parse(cff->raw_data, offset, &cff->char_strings);
+ cff->cnt_glyph = cff->char_strings.count;
+ } else {
+ cff_iIndex.empty(&cff->char_strings);
+ logWarning("[libcff] Bad CFF font: no any glyph data.\n");
+ }
+
+ /* Encodings */
+ offset =
+ cff_iDict
+ .parseDictKey(cff->top_dict.data, cff->top_dict.offset[1] - cff->top_dict.offset[0],
+ op_Encoding, 0)
+ .i;
+
+ if (offset != -1) {
+ parse_encoding(cff, offset, &cff->encodings);
+ } else {
+ cff->encodings.t = cff_ENC_UNSPECED;
+ }
+
+ /* Charsets */
+ offset = cff_iDict
+ .parseDictKey(cff->top_dict.data,
+ cff->top_dict.offset[1] - cff->top_dict.offset[0], op_charset, 0)
+ .i;
+
+ if (offset != -1) {
+ cff_extract_Charset(cff->raw_data, offset, cff->char_strings.count, &cff->charsets);
+ } else {
+ cff->charsets.t = cff_CHARSET_UNSPECED;
+ }
+
+ /* FDSelect */
+ offset =
+ cff_iDict
+ .parseDictKey(cff->top_dict.data, cff->top_dict.offset[1] - cff->top_dict.offset[0],
+ op_FDSelect, 0)
+ .i;
+
+ if (cff->char_strings.count && offset != -1) {
+ cff_extract_FDSelect(cff->raw_data, offset, cff->char_strings.count, &cff->fdselect);
+ } else {
+ cff->fdselect.t = cff_FDSELECT_UNSPECED;
+ }
+
+ /* Font Dict INDEX */
+ offset = cff_iDict
+ .parseDictKey(cff->top_dict.data,
+ cff->top_dict.offset[1] - cff->top_dict.offset[0], op_FDArray, 0)
+ .i;
+
+ if (offset != -1) {
+ cff_iIndex.parse(cff->raw_data, offset, &cff->font_dict);
+ } else {
+ cff_iIndex.empty(&cff->font_dict);
+ }
+ }
+
+ /* Private and Local Subr */
+ {
+ int32_t private_len = -1;
+ int32_t private_off = -1;
+
+ if (cff->top_dict.data != NULL) {
+ private_len =
+ cff_iDict
+ .parseDictKey(cff->top_dict.data,
+ cff->top_dict.offset[1] - cff->top_dict.offset[0], op_Private, 0)
+ .i;
+ private_off =
+ cff_iDict
+ .parseDictKey(cff->top_dict.data,
+ cff->top_dict.offset[1] - cff->top_dict.offset[0], op_Private, 1)
+ .i;
+ }
+
+ if (private_off != -1 && private_len != -1) {
+ offset =
+ cff_iDict.parseDictKey(cff->raw_data + private_off, private_len, op_Subrs, 0).i;
+
+ if (offset != -1)
+ cff_iIndex.parse(cff->raw_data, private_off + offset, &cff->local_subr);
+ else
+ cff_iIndex.empty(&cff->local_subr);
+ } else {
+ cff_iIndex.empty(&cff->local_subr);
+ }
+ }
+}
+
+cff_File *cff_openStream(uint8_t *data, uint32_t len, const otfcc_Options *options) {
+ cff_File *file;
+ NEW(file);
+
+ NEW(file->raw_data, len);
+ memcpy(file->raw_data, data, len);
+ file->raw_length = len;
+ file->cnt_glyph = 0;
+ parse_cff_bytecode(file, options);
+
+ return file;
+}
+
+void cff_close(cff_File *file) {
+ if (file != NULL) {
+ if (file->raw_data != NULL) FREE(file->raw_data);
+
+ cff_iIndex.dispose(&file->name);
+ cff_iIndex.dispose(&file->top_dict);
+ cff_iIndex.dispose(&file->string);
+ cff_iIndex.dispose(&file->global_subr);
+ cff_iIndex.dispose(&file->char_strings);
+ cff_iIndex.dispose(&file->font_dict);
+ cff_iIndex.dispose(&file->local_subr);
+
+ switch (file->encodings.t) {
+ case cff_ENC_STANDARD:
+ case cff_ENC_EXPERT:
+ case cff_ENC_UNSPECED:
+ break;
+ case cff_ENC_FORMAT0:
+ if (file->encodings.f0.code != NULL) FREE(file->encodings.f0.code);
+ break;
+ case cff_ENC_FORMAT1:
+ if (file->encodings.f1.range1 != NULL) FREE(file->encodings.f1.range1);
+ break;
+ case cff_ENC_FORMAT_SUPPLEMENT:
+ if (file->encodings.ns.supplement != NULL) FREE(file->encodings.ns.supplement);
+ break;
+ }
+
+ cff_close_Charset(file->charsets);
+ cff_close_FDSelect(file->fdselect);
+
+ FREE(file);
+ }
+}
+
+uint8_t cff_parseSubr(uint16_t idx, uint8_t *raw, cff_Index fdarray, cff_FDSelect select,
+ cff_Index *subr) {
+ uint8_t fd = 0;
+ int32_t off_private, len_private;
+ int32_t off_subr;
+
+ switch (select.t) {
+ case cff_FDSELECT_FORMAT0:
+ fd = select.f0.fds[idx];
+ break;
+ case cff_FDSELECT_FORMAT3:
+ for (int i = 0; i < select.f3.nranges - 1; i++)
+ if (idx >= select.f3.range3[i].first && idx < select.f3.range3[i + 1].first)
+ fd = select.f3.range3[i].fd;
+ if (idx >= select.f3.range3[select.f3.nranges - 1].first && idx < select.f3.sentinel)
+ fd = select.f3.range3[select.f3.nranges - 1].fd;
+ break;
+ case cff_FDSELECT_UNSPECED:
+ fd = 0;
+ break;
+ }
+
+ off_private = cff_iDict
+ .parseDictKey(fdarray.data + fdarray.offset[fd] - 1,
+ fdarray.offset[fd + 1] - fdarray.offset[fd], op_Private, 1)
+ .i;
+ len_private = cff_iDict
+ .parseDictKey(fdarray.data + fdarray.offset[fd] - 1,
+ fdarray.offset[fd + 1] - fdarray.offset[fd], op_Private, 0)
+ .i;
+
+ if (off_private != -1 && len_private != -1) {
+ off_subr = cff_iDict.parseDictKey(raw + off_private, len_private, op_Subrs, 0).i;
+
+ if (off_subr != -1) {
+ cff_iIndex.parse(raw, off_private + off_subr, subr);
+ } else {
+ cff_iIndex.empty(subr);
+ }
+ } else {
+ cff_iIndex.empty(subr);
+ }
+
+ return fd;
+}
+
+static inline uint16_t compute_subr_bias(uint16_t cnt) {
+ if (cnt < 1240)
+ return 107;
+ else if (cnt < 33900)
+ return 1131;
+ else
+ return 32768;
+}
+
+/*
+ CharString program:
+ w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar
+*/
+static void reverseStack(cff_Stack *stack, uint8_t left, uint8_t right) {
+ cff_Value *p1 = stack->stack + left;
+ cff_Value *p2 = stack->stack + right;
+ while (p1 < p2) {
+ cff_Value temp = *p1;
+ *p1 = *p2;
+ *p2 = temp;
+ p1++;
+ p2--;
+ }
+}
+static void callback_nopSetWidth(void *context, double width) {}
+static void callback_nopNewContour(void *context) {}
+static void callback_nopLineTo(void *context, double x1, double y1) {}
+static void callback_nopCurveTo(void *context, double x1, double y1, double x2, double y2,
+ double x3, double y3) {}
+static void callback_nopsetHint(void *context, bool isVertical, double position, double width) {}
+static void callback_nopsetMask(void *context, bool isContourMask, bool *mask) {
+ FREE(mask);
+}
+static double callback_nopgetrand(void *context) {
+ return 0;
+}
+#define CHECK_STACK_TOP(op, n) \
+ { \
+ if (stack->index < n) { \
+ logWarning("[libcff] Stack cannot provide enough parameters for %s (%04x). This " \
+ "operation is ignored.\n", \
+ #op, op); \
+ break; \
+ } \
+ }
+
+// CFF charastring parser
+void cff_parseOutline(uint8_t *data, uint32_t len, cff_Index gsubr, cff_Index lsubr,
+ cff_Stack *stack, void *outline, cff_IOutlineBuilder methods,
+ const otfcc_Options *options) {
+ uint16_t gsubr_bias = compute_subr_bias(gsubr.count);
+ uint16_t lsubr_bias = compute_subr_bias(lsubr.count);
+ uint8_t *start = data;
+ uint32_t advance, i, cnt_bezier;
+ cff_Value val;
+
+ void (*setWidth)(void *context, double width) = methods.setWidth;
+ void (*newContour)(void *context) = methods.newContour;
+ void (*lineTo)(void *context, double x1, double y1) = methods.lineTo;
+ void (*curveTo)(void *context, double x1, double y1, double x2, double y2, double x3,
+ double y3) = methods.curveTo;
+ void (*setHint)(void *context, bool isVertical, double position, double width) =
+ methods.setHint;
+ void (*setMask)(void *context, bool isContourMask, bool *mask) = methods.setMask;
+ double (*getrand)(void *context) = methods.getrand;
+
+ if (!setWidth) setWidth = callback_nopSetWidth;
+ if (!newContour) newContour = callback_nopNewContour;
+ if (!lineTo) lineTo = callback_nopLineTo;
+ if (!curveTo) curveTo = callback_nopCurveTo;
+ if (!setHint) setHint = callback_nopsetHint;
+ if (!setMask) setMask = callback_nopsetMask;
+ if (!getrand) getrand = callback_nopgetrand;
+
+ while (start < data + len) {
+ advance = cff_decodeCS2Token(start, &val);
+
+ switch (val.t) {
+ case CS2_OPERATOR:
+ switch (val.i) {
+ case op_hstem:
+ case op_vstem:
+ case op_hstemhm:
+ case op_vstemhm:
+ if (stack->index % 2) setWidth(outline, stack->stack[0].d);
+ stack->stem += stack->index >> 1;
+ double hintBase = 0;
+ for (uint16_t j = stack->index % 2; j < stack->index; j += 2) {
+ double pos = stack->stack[j].d;
+ double width = stack->stack[j + 1].d;
+ setHint(outline, (val.i == op_vstem || val.i == op_vstemhm),
+ pos + hintBase, width);
+ hintBase += pos + width;
+ }
+ stack->index = 0;
+ break;
+ case op_hintmask:
+ case op_cntrmask: {
+ if (stack->index % 2) setWidth(outline, stack->stack[0].d);
+ bool isVertical = stack->stem > 0;
+ stack->stem += stack->index >> 1;
+ double hintBase = 0;
+ for (uint16_t j = stack->index % 2; j < stack->index; j += 2) {
+ double pos = stack->stack[j].d;
+ double width = stack->stack[j + 1].d;
+ setHint(outline, isVertical, pos + hintBase, width);
+ hintBase += pos + width;
+ }
+ uint32_t maskLength = (stack->stem + 7) >> 3;
+ bool *mask;
+ NEW(mask, stack->stem + 7);
+ for (uint32_t byte = 0; byte < maskLength; byte++) {
+ uint8_t maskByte = start[advance + byte];
+ mask[(byte << 3) + 0] = maskByte >> 7 & 1;
+ mask[(byte << 3) + 1] = maskByte >> 6 & 1;
+ mask[(byte << 3) + 2] = maskByte >> 5 & 1;
+ mask[(byte << 3) + 3] = maskByte >> 4 & 1;
+ mask[(byte << 3) + 4] = maskByte >> 3 & 1;
+ mask[(byte << 3) + 5] = maskByte >> 2 & 1;
+ mask[(byte << 3) + 6] = maskByte >> 1 & 1;
+ mask[(byte << 3) + 7] = maskByte >> 0 & 1;
+ }
+ setMask(outline, (val.i == op_cntrmask), mask);
+ advance += maskLength;
+ stack->index = 0;
+ break;
+ }
+
+ case op_vmoveto: {
+ CHECK_STACK_TOP(op_vmoveto, 1);
+ if (stack->index > 1) setWidth(outline, stack->stack[stack->index - 2].d);
+ newContour(outline);
+ lineTo(outline, 0.0, stack->stack[stack->index - 1].d);
+ stack->index = 0;
+ break;
+ }
+ case op_rmoveto: {
+ CHECK_STACK_TOP(op_rmoveto, 2);
+ if (stack->index > 2) setWidth(outline, stack->stack[stack->index - 3].d);
+ newContour(outline);
+ lineTo(outline, stack->stack[stack->index - 2].d,
+ stack->stack[stack->index - 1].d);
+ stack->index = 0;
+ break;
+ }
+ case op_hmoveto: {
+ CHECK_STACK_TOP(op_hmoveto, 1);
+ if (stack->index > 1) setWidth(outline, stack->stack[stack->index - 2].d);
+ newContour(outline);
+ lineTo(outline, stack->stack[stack->index - 1].d, 0.0);
+ stack->index = 0;
+ break;
+ }
+ case op_endchar: {
+ if (stack->index > 0) setWidth(outline, stack->stack[stack->index - 1].d);
+ break;
+ }
+ case op_rlineto: {
+ for (i = 0; i < stack->index; i += 2)
+ lineTo(outline, stack->stack[i].d, stack->stack[i + 1].d);
+ stack->index = 0;
+ break;
+ }
+ case op_vlineto: {
+ if (stack->index % 2 == 1) {
+ lineTo(outline, 0.0, stack->stack[0].d);
+ for (i = 1; i < stack->index; i += 2) {
+ lineTo(outline, stack->stack[i].d, 0.0);
+ lineTo(outline, 0.0, stack->stack[i + 1].d);
+ }
+ } else {
+ for (i = 0; i < stack->index; i += 2) {
+ lineTo(outline, 0.0, stack->stack[i].d);
+ lineTo(outline, stack->stack[i + 1].d, 0.0);
+ }
+ }
+ stack->index = 0;
+ break;
+ }
+ case op_hlineto: {
+ if (stack->index % 2 == 1) {
+ lineTo(outline, stack->stack[0].d, 0.0);
+ for (i = 1; i < stack->index; i += 2) {
+ lineTo(outline, 0.0, stack->stack[i].d);
+ lineTo(outline, stack->stack[i + 1].d, 0.0);
+ }
+ } else {
+ for (i = 0; i < stack->index; i += 2) {
+ lineTo(outline, stack->stack[i].d, 0.0);
+ lineTo(outline, 0.0, stack->stack[i + 1].d);
+ }
+ }
+ stack->index = 0;
+ break;
+ }
+ case op_rrcurveto: {
+ for (i = 0; i < stack->index; i += 6)
+ curveTo(outline, stack->stack[i].d, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, stack->stack[i + 3].d,
+ stack->stack[i + 4].d, stack->stack[i + 5].d);
+ stack->index = 0;
+ break;
+ }
+ case op_rcurveline: {
+ for (i = 0; i < stack->index - 2; i += 6)
+ curveTo(outline, stack->stack[i].d, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, stack->stack[i + 3].d,
+ stack->stack[i + 4].d, stack->stack[i + 5].d);
+ lineTo(outline, stack->stack[stack->index - 2].d,
+ stack->stack[stack->index - 1].d);
+ stack->index = 0;
+ break;
+ }
+ case op_rlinecurve: {
+ for (i = 0; i < stack->index - 6; i += 2)
+ lineTo(outline, stack->stack[i].d, stack->stack[i + 1].d);
+ curveTo(outline, stack->stack[stack->index - 6].d,
+ stack->stack[stack->index - 5].d, stack->stack[stack->index - 4].d,
+ stack->stack[stack->index - 3].d, stack->stack[stack->index - 2].d,
+ stack->stack[stack->index - 1].d);
+ stack->index = 0;
+ break;
+ }
+ case op_vvcurveto: {
+ if (stack->index % 4 == 1) {
+ curveTo(outline, stack->stack[0].d, stack->stack[1].d,
+ stack->stack[2].d, stack->stack[3].d, 0.0, stack->stack[4].d);
+ for (i = 5; i < stack->index; i += 4)
+ curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d);
+ } else {
+ for (i = 0; i < stack->index; i += 4)
+ curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d);
+ }
+ stack->index = 0;
+ break;
+ }
+ case op_hhcurveto: {
+ if (stack->index % 4 == 1) {
+ curveTo(outline, stack->stack[1].d, stack->stack[0].d,
+ stack->stack[2].d, stack->stack[3].d, stack->stack[4].d, 0.0);
+ for (i = 5; i < stack->index; i += 4)
+ curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0);
+ } else {
+ for (i = 0; i < stack->index; i += 4)
+ curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0);
+ }
+ stack->index = 0;
+ break;
+ }
+ case op_vhcurveto: {
+ if (stack->index % 4 == 1)
+ cnt_bezier = (stack->index - 5) / 4;
+ else
+ cnt_bezier = (stack->index / 4);
+
+ for (i = 0; i < 4 * cnt_bezier; i += 4) {
+ if ((i / 4) % 2 == 0)
+ curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0);
+ else
+ curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d);
+ }
+ if (stack->index % 8 == 5) {
+ curveTo(
+ outline, 0.0, stack->stack[stack->index - 5].d,
+ stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d,
+ stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d);
+ }
+ if (stack->index % 8 == 1) {
+ curveTo(
+ outline, stack->stack[stack->index - 5].d, 0.0,
+ stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d,
+ stack->stack[stack->index - 1].d, stack->stack[stack->index - 2].d);
+ }
+ stack->index = 0;
+ break;
+ }
+ case op_hvcurveto: {
+ if (stack->index % 4 == 1)
+ cnt_bezier = (stack->index - 5) / 4;
+ else
+ cnt_bezier = (stack->index / 4);
+
+ for (i = 0; i < 4 * cnt_bezier; i += 4) {
+ if ((i / 4) % 2 == 0)
+ curveTo(outline, stack->stack[i].d, 0.0, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, 0.0, stack->stack[i + 3].d);
+ else
+ curveTo(outline, 0.0, stack->stack[i].d, stack->stack[i + 1].d,
+ stack->stack[i + 2].d, stack->stack[i + 3].d, 0.0);
+ }
+
+ if (stack->index % 8 == 5) {
+ curveTo(
+ outline, stack->stack[stack->index - 5].d, 0.0,
+ stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d,
+ stack->stack[stack->index - 1].d, stack->stack[stack->index - 2].d);
+ }
+ if (stack->index % 8 == 1) {
+ curveTo(
+ outline, 0.0, stack->stack[stack->index - 5].d,
+ stack->stack[stack->index - 4].d, stack->stack[stack->index - 3].d,
+ stack->stack[stack->index - 2].d, stack->stack[stack->index - 1].d);
+ }
+ stack->index = 0;
+ break;
+ }
+ case op_hflex: {
+ CHECK_STACK_TOP(op_hflex, 7);
+ curveTo(outline, stack->stack[0].d, 0.0, stack->stack[1].d,
+ stack->stack[2].d, stack->stack[3].d, 0.0);
+ curveTo(outline, stack->stack[4].d, 0.0, stack->stack[5].d,
+ -stack->stack[2].d, stack->stack[6].d, 0.0);
+ stack->index = 0;
+ break;
+ }
+ case op_flex: {
+ CHECK_STACK_TOP(op_flex, 12);
+ curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d,
+ stack->stack[3].d, stack->stack[4].d, stack->stack[5].d);
+ curveTo(outline, stack->stack[6].d, stack->stack[7].d, stack->stack[8].d,
+ stack->stack[9].d, stack->stack[10].d, stack->stack[11].d);
+ stack->index = 0;
+ break;
+ }
+ case op_hflex1: {
+ CHECK_STACK_TOP(op_hflex1, 9);
+ curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d,
+ stack->stack[3].d, stack->stack[4].d, 0.0);
+ curveTo(outline, stack->stack[5].d, 0.0, stack->stack[6].d,
+ stack->stack[7].d, stack->stack[8].d,
+ -(stack->stack[1].d + stack->stack[3].d + stack->stack[7].d));
+ stack->index = 0;
+ break;
+ }
+ case op_flex1: {
+ CHECK_STACK_TOP(op_flex1, 11);
+ double dx = stack->stack[0].d + stack->stack[2].d + stack->stack[4].d +
+ stack->stack[6].d + stack->stack[8].d;
+ double dy = stack->stack[1].d + stack->stack[3].d + stack->stack[5].d +
+ stack->stack[7].d + stack->stack[9].d;
+ if (fabs(dx) > fabs(dy)) {
+ dx = stack->stack[10].d;
+ dy = -dy;
+ } else {
+ dx = -dx;
+ dy = stack->stack[10].d;
+ }
+ curveTo(outline, stack->stack[0].d, stack->stack[1].d, stack->stack[2].d,
+ stack->stack[3].d, stack->stack[4].d, stack->stack[5].d);
+ curveTo(outline, stack->stack[6].d, stack->stack[7].d, stack->stack[8].d,
+ stack->stack[9].d, dx, dy);
+ stack->index = 0;
+ break;
+ }
+ case op_and: {
+ CHECK_STACK_TOP(op_and, 2);
+ double num1 = stack->stack[stack->index - 1].d;
+ double num2 = stack->stack[stack->index - 2].d;
+ stack->stack[stack->index - 2].d = (num1 && num2) ? 1.0 : 0.0;
+ stack->index -= 1;
+ break;
+ }
+ case op_or: {
+ CHECK_STACK_TOP(op_or, 2);
+ double num1 = stack->stack[stack->index - 1].d;
+ double num2 = stack->stack[stack->index - 2].d;
+ stack->stack[stack->index - 2].d = (num1 || num2) ? 1.0 : 0.0;
+ stack->index -= 1;
+ break;
+ }
+ case op_not: {
+ CHECK_STACK_TOP(op_not, 1);
+ double num = stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 1].d = num ? 0.0 : 1.0;
+ break;
+ }
+ case op_abs: {
+ CHECK_STACK_TOP(op_abs, 1);
+ double num = stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 1].d = (num < 0.0) ? -num : num;
+ break;
+ }
+ case op_add: {
+ CHECK_STACK_TOP(op_add, 2);
+ double num1 = stack->stack[stack->index - 1].d;
+ double num2 = stack->stack[stack->index - 2].d;
+ stack->stack[stack->index - 2].d = num1 + num2;
+ stack->index -= 1;
+ break;
+ }
+ case op_sub: {
+ CHECK_STACK_TOP(op_sub, 2);
+ double num1 = stack->stack[stack->index - 2].d;
+ double num2 = stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 2].d = num1 - num2;
+ stack->index -= 1;
+ break;
+ }
+ case op_div: {
+ CHECK_STACK_TOP(op_div, 2);
+ double num1 = stack->stack[stack->index - 2].d;
+ double num2 = stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 2].d = num1 / num2;
+ stack->index -= 1;
+ break;
+ }
+ case op_neg: {
+ CHECK_STACK_TOP(op_neg, 1);
+ double num = stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 1].d = -num;
+ break;
+ }
+ case op_eq: {
+ CHECK_STACK_TOP(op_eq, 2);
+ double num1 = stack->stack[stack->index - 1].d;
+ double num2 = stack->stack[stack->index - 2].d;
+ stack->stack[stack->index - 2].d = (num1 == num2) ? 1.0 : 0.0;
+ stack->index -= 1;
+ break;
+ }
+ case op_drop: {
+ CHECK_STACK_TOP(op_drop, 1);
+ stack->index -= 1;
+ break;
+ }
+ case op_put: {
+ CHECK_STACK_TOP(op_put, 2);
+ double val = stack->stack[stack->index - 2].d;
+ int32_t i = (int32_t)stack->stack[stack->index - 1].d;
+ stack->transient[i % type2_transient_array].d = val;
+ stack->index -= 2;
+ break;
+ }
+ case op_get: {
+ CHECK_STACK_TOP(op_get, 1);
+ int32_t i = (int32_t)stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 1].d =
+ stack->transient[i % type2_transient_array].d;
+ break;
+ }
+ case op_ifelse: {
+ CHECK_STACK_TOP(op_ifelse, 4);
+ double v2 = stack->stack[stack->index - 1].d;
+ double v1 = stack->stack[stack->index - 2].d;
+ double s2 = stack->stack[stack->index - 3].d;
+ double s1 = stack->stack[stack->index - 4].d;
+ stack->stack[stack->index - 4].d = (v1 <= v2) ? s1 : s2;
+ stack->index -= 3;
+ break;
+ }
+ case op_random: {
+ // Chosen from a fair dice
+ // TODO: use a real randomizer
+ stack->stack[stack->index].t = cff_DOUBLE;
+ stack->stack[stack->index].d = getrand(outline);
+ stack->index += 1;
+ break;
+ }
+ case op_mul: {
+ CHECK_STACK_TOP(op_mul, 2);
+ double num1 = stack->stack[stack->index - 1].d;
+ double num2 = stack->stack[stack->index - 2].d;
+ stack->stack[stack->index - 2].d = num1 * num2;
+ stack->index -= 1;
+ break;
+ }
+ case op_sqrt: {
+ CHECK_STACK_TOP(op_sqrt, 1);
+ double num = stack->stack[stack->index - 1].d;
+ stack->stack[stack->index - 1].d = sqrt(num);
+ break;
+ }
+ case op_dup: {
+ CHECK_STACK_TOP(op_dup, 1);
+ stack->stack[stack->index] = stack->stack[stack->index - 1];
+ stack->index += 1;
+ break;
+ }
+ case op_exch: {
+ CHECK_STACK_TOP(op_exch, 2);
+ double num1 = stack->stack[stack->index - 1].d;
+ double num2 = stack->stack[stack->index - 2].d;
+ stack->stack[stack->index - 1].d = num2;
+ stack->stack[stack->index - 2].d = num1;
+ break;
+ }
+ case op_index: {
+ CHECK_STACK_TOP(op_index, 2);
+ uint8_t n = stack->index - 1;
+ uint8_t j = n - 1 - (uint8_t)(stack->stack[n].d) % n;
+ stack->stack[n] = stack->stack[j];
+ break;
+ }
+ case op_roll: {
+ CHECK_STACK_TOP(op_roll, 2);
+ int32_t j = stack->stack[stack->index - 1].d;
+ uint32_t n = stack->stack[stack->index - 2].d;
+ CHECK_STACK_TOP(op_roll, 2 + n);
+ j = -j % n;
+ if (j < 0) j += n;
+ if (!j) break;
+ uint8_t last = stack->index - 3;
+ uint8_t first = stack->index - 2 - n;
+
+ reverseStack(stack, first, last);
+ reverseStack(stack, last - j + 1, last);
+ reverseStack(stack, first, last - j);
+ stack->index -= 2;
+ break;
+ }
+ case op_return:
+ return;
+ case op_callsubr: {
+ CHECK_STACK_TOP(op_callsubr, 1);
+ uint32_t subr = (uint32_t)stack->stack[--(stack->index)].d;
+ cff_parseOutline(lsubr.data + lsubr.offset[lsubr_bias + subr] - 1,
+ lsubr.offset[lsubr_bias + subr + 1] -
+ lsubr.offset[lsubr_bias + subr],
+ gsubr, lsubr, stack, outline, methods, options);
+ break;
+ }
+ case op_callgsubr: {
+ CHECK_STACK_TOP(op_callgsubr, 1);
+ uint32_t subr = (uint32_t)stack->stack[--(stack->index)].d;
+ cff_parseOutline(gsubr.data + gsubr.offset[gsubr_bias + subr] - 1,
+ gsubr.offset[gsubr_bias + subr + 1] -
+ gsubr.offset[gsubr_bias + subr],
+ gsubr, lsubr, stack, outline, methods, options);
+ break;
+ }
+ default: {
+ logWarning("Warning: unknown operator %d occurs in Type 2 CharString. It "
+ "may caused by file corruption.",
+ val.i);
+ return;
+ }
+ }
+ break;
+ case CS2_OPERAND:
+ case CS2_FRACTION:
+ stack->stack[(stack->index)++] = val;
+ break;
+ }
+
+ start += advance;
+ }
+}