74f25d2755
- it aims to fix https://bugzilla.mozilla.org/show_bug.cgi?id=1749563; - use some helper functions to get (u|i)int** values in buffer: it helps to have a clearer code; - in composite glyphes the translations values with a transformations are signed so consequently get some int8 instead of uint8; - add few TODOs.
705 lines
18 KiB
JavaScript
705 lines
18 KiB
JavaScript
/* Copyright 2021 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
const ON_CURVE_POINT = 1 << 0;
|
|
const X_SHORT_VECTOR = 1 << 1;
|
|
const Y_SHORT_VECTOR = 1 << 2;
|
|
const REPEAT_FLAG = 1 << 3;
|
|
const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1 << 4;
|
|
const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1 << 5;
|
|
const OVERLAP_SIMPLE = 1 << 6;
|
|
|
|
const ARG_1_AND_2_ARE_WORDS = 1 << 0;
|
|
const ARGS_ARE_XY_VALUES = 1 << 1;
|
|
// const ROUND_XY_TO_GRID = 1 << 2;
|
|
const WE_HAVE_A_SCALE = 1 << 3;
|
|
const MORE_COMPONENTS = 1 << 5;
|
|
const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
|
|
const WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
|
const WE_HAVE_INSTRUCTIONS = 1 << 8;
|
|
// const USE_MY_METRICS = 1 << 9;
|
|
// const OVERLAP_COMPOUND = 1 << 10;
|
|
// const SCALED_COMPONENT_OFFSET = 1 << 11;
|
|
// const UNSCALED_COMPONENT_OFFSET = 1 << 12;
|
|
|
|
/**
|
|
* GlyfTable object represents a glyf table containing glyph information:
|
|
* - glyph header (xMin, yMin, xMax, yMax);
|
|
* - contours if any;
|
|
* - components if the glyph is a composite.
|
|
*
|
|
* It's possible to re-scale each glyph in order to have a new font which
|
|
* exactly fits an other one: the goal is to be able to build some substitution
|
|
* font for well-known fonts (Myriad, Arial, ...).
|
|
*
|
|
* A full description of glyf table can be found here
|
|
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
|
|
*/
|
|
class GlyfTable {
|
|
constructor({ glyfTable, isGlyphLocationsLong, locaTable, numGlyphs }) {
|
|
this.glyphs = [];
|
|
const loca = new DataView(
|
|
locaTable.buffer,
|
|
locaTable.byteOffset,
|
|
locaTable.byteLength
|
|
);
|
|
const glyf = new DataView(
|
|
glyfTable.buffer,
|
|
glyfTable.byteOffset,
|
|
glyfTable.byteLength
|
|
);
|
|
const offsetSize = isGlyphLocationsLong ? 4 : 2;
|
|
let prev = isGlyphLocationsLong ? loca.getUint32(0) : 2 * loca.getUint16(0);
|
|
let pos = 0;
|
|
for (let i = 0; i < numGlyphs; i++) {
|
|
pos += offsetSize;
|
|
const next = isGlyphLocationsLong
|
|
? loca.getUint32(pos)
|
|
: 2 * loca.getUint16(pos);
|
|
if (next === prev) {
|
|
this.glyphs.push(new Glyph({}));
|
|
continue;
|
|
}
|
|
|
|
const glyph = Glyph.parse(prev, glyf);
|
|
this.glyphs.push(glyph);
|
|
|
|
prev = next;
|
|
}
|
|
}
|
|
|
|
getSize() {
|
|
return this.glyphs.reduce((a, g) => {
|
|
const size = g.getSize();
|
|
// Round to next multiple of 4 if needed.
|
|
return a + ((size + 3) & ~3);
|
|
}, 0);
|
|
}
|
|
|
|
write() {
|
|
const totalSize = this.getSize();
|
|
const glyfTable = new DataView(new ArrayBuffer(totalSize));
|
|
const isLocationLong = totalSize > /* 0xffff * 2 */ 0x1fffe;
|
|
const offsetSize = isLocationLong ? 4 : 2;
|
|
const locaTable = new DataView(
|
|
new ArrayBuffer((this.glyphs.length + 1) * offsetSize)
|
|
);
|
|
|
|
if (isLocationLong) {
|
|
locaTable.setUint32(0, 0);
|
|
} else {
|
|
locaTable.setUint16(0, 0);
|
|
}
|
|
|
|
let pos = 0;
|
|
let locaIndex = 0;
|
|
for (const glyph of this.glyphs) {
|
|
pos += glyph.write(pos, glyfTable);
|
|
// Round to next multiple of 4 if needed.
|
|
pos = (pos + 3) & ~3;
|
|
|
|
locaIndex += offsetSize;
|
|
if (isLocationLong) {
|
|
locaTable.setUint32(locaIndex, pos);
|
|
} else {
|
|
locaTable.setUint16(locaIndex, pos >> 1);
|
|
}
|
|
}
|
|
|
|
return {
|
|
isLocationLong,
|
|
loca: new Uint8Array(locaTable.buffer),
|
|
glyf: new Uint8Array(glyfTable.buffer),
|
|
};
|
|
}
|
|
|
|
scale(factors) {
|
|
for (let i = 0, ii = this.glyphs.length; i < ii; i++) {
|
|
this.glyphs[i].scale(factors[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
class Glyph {
|
|
constructor({ header = null, simple = null, composites = null }) {
|
|
this.header = header;
|
|
this.simple = simple;
|
|
this.composites = composites;
|
|
}
|
|
|
|
static parse(pos, glyf) {
|
|
const [read, header] = GlyphHeader.parse(pos, glyf);
|
|
pos += read;
|
|
|
|
if (header.numberOfContours < 0) {
|
|
// Composite glyph.
|
|
const composites = [];
|
|
while (true) {
|
|
const [n, composite] = CompositeGlyph.parse(pos, glyf);
|
|
pos += n;
|
|
composites.push(composite);
|
|
if (!(composite.flags & MORE_COMPONENTS)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new Glyph({ header, composites });
|
|
}
|
|
|
|
const simple = SimpleGlyph.parse(pos, glyf, header.numberOfContours);
|
|
|
|
return new Glyph({ header, simple });
|
|
}
|
|
|
|
getSize() {
|
|
if (!this.header) {
|
|
return 0;
|
|
}
|
|
const size = this.simple
|
|
? this.simple.getSize()
|
|
: this.composites.reduce((a, c) => a + c.getSize(), 0);
|
|
return this.header.getSize() + size;
|
|
}
|
|
|
|
write(pos, buf) {
|
|
if (!this.header) {
|
|
return 0;
|
|
}
|
|
|
|
const spos = pos;
|
|
pos += this.header.write(pos, buf);
|
|
if (this.simple) {
|
|
pos += this.simple.write(pos, buf);
|
|
} else {
|
|
for (const composite of this.composites) {
|
|
pos += composite.write(pos, buf);
|
|
}
|
|
}
|
|
|
|
return pos - spos;
|
|
}
|
|
|
|
scale(factor) {
|
|
if (!this.header) {
|
|
return;
|
|
}
|
|
|
|
const xMiddle = (this.header.xMin + this.header.xMax) / 2;
|
|
this.header.scale(xMiddle, factor);
|
|
if (this.simple) {
|
|
this.simple.scale(xMiddle, factor);
|
|
} else {
|
|
for (const composite of this.composites) {
|
|
composite.scale(xMiddle, factor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class GlyphHeader {
|
|
constructor({ numberOfContours, xMin, yMin, xMax, yMax }) {
|
|
this.numberOfContours = numberOfContours;
|
|
this.xMin = xMin;
|
|
this.yMin = yMin;
|
|
this.xMax = xMax;
|
|
this.yMax = yMax;
|
|
}
|
|
|
|
static parse(pos, glyf) {
|
|
return [
|
|
10,
|
|
new GlyphHeader({
|
|
numberOfContours: glyf.getInt16(pos),
|
|
xMin: glyf.getInt16(pos + 2),
|
|
yMin: glyf.getInt16(pos + 4),
|
|
xMax: glyf.getInt16(pos + 6),
|
|
yMax: glyf.getInt16(pos + 8),
|
|
}),
|
|
];
|
|
}
|
|
|
|
getSize() {
|
|
return 10;
|
|
}
|
|
|
|
write(pos, buf) {
|
|
buf.setInt16(pos, this.numberOfContours);
|
|
buf.setInt16(pos + 2, this.xMin);
|
|
buf.setInt16(pos + 4, this.yMin);
|
|
buf.setInt16(pos + 6, this.xMax);
|
|
buf.setInt16(pos + 8, this.yMax);
|
|
|
|
return 10;
|
|
}
|
|
|
|
scale(x, factor) {
|
|
this.xMin = Math.round(x + (this.xMin - x) * factor);
|
|
this.xMax = Math.round(x + (this.xMax - x) * factor);
|
|
}
|
|
}
|
|
|
|
class Contour {
|
|
constructor({ flags, xCoordinates, yCoordinates }) {
|
|
this.xCoordinates = xCoordinates;
|
|
this.yCoordinates = yCoordinates;
|
|
this.flags = flags;
|
|
}
|
|
}
|
|
|
|
class SimpleGlyph {
|
|
constructor({ contours, instructions }) {
|
|
this.contours = contours;
|
|
this.instructions = instructions;
|
|
}
|
|
|
|
static parse(pos, glyf, numberOfContours) {
|
|
const endPtsOfContours = [];
|
|
for (let i = 0; i < numberOfContours; i++) {
|
|
const endPt = glyf.getUint16(pos);
|
|
pos += 2;
|
|
endPtsOfContours.push(endPt);
|
|
}
|
|
const numberOfPt = endPtsOfContours[numberOfContours - 1] + 1;
|
|
const instructionLength = glyf.getUint16(pos);
|
|
pos += 2;
|
|
const instructions = new Uint8Array(glyf).slice(
|
|
pos,
|
|
pos + instructionLength
|
|
);
|
|
pos += instructionLength;
|
|
|
|
const flags = [];
|
|
for (let i = 0; i < numberOfPt; pos++, i++) {
|
|
let flag = glyf.getUint8(pos);
|
|
flags.push(flag);
|
|
if (flag & REPEAT_FLAG) {
|
|
const count = glyf.getUint8(++pos);
|
|
flag ^= REPEAT_FLAG;
|
|
for (let m = 0; m < count; m++) {
|
|
flags.push(flag);
|
|
}
|
|
i += count;
|
|
}
|
|
}
|
|
|
|
const allXCoordinates = [];
|
|
let xCoordinates = [];
|
|
let yCoordinates = [];
|
|
let pointFlags = [];
|
|
const contours = [];
|
|
let endPtsOfContoursIndex = 0;
|
|
let lastCoordinate = 0;
|
|
|
|
// Get x coordinates.
|
|
for (let i = 0; i < numberOfPt; i++) {
|
|
const flag = flags[i];
|
|
if (flag & X_SHORT_VECTOR) {
|
|
// 8-bits unsigned value.
|
|
const x = glyf.getUint8(pos++);
|
|
lastCoordinate += flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR ? x : -x;
|
|
xCoordinates.push(lastCoordinate);
|
|
} else if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {
|
|
// IS_SAME.
|
|
xCoordinates.push(lastCoordinate);
|
|
} else {
|
|
lastCoordinate += glyf.getInt16(pos);
|
|
pos += 2;
|
|
xCoordinates.push(lastCoordinate);
|
|
}
|
|
|
|
if (endPtsOfContours[endPtsOfContoursIndex] === i) {
|
|
// Next entry is the first one of a new contour.
|
|
endPtsOfContoursIndex++;
|
|
allXCoordinates.push(xCoordinates);
|
|
xCoordinates = [];
|
|
}
|
|
}
|
|
|
|
lastCoordinate = 0;
|
|
endPtsOfContoursIndex = 0;
|
|
for (let i = 0; i < numberOfPt; i++) {
|
|
const flag = flags[i];
|
|
if (flag & Y_SHORT_VECTOR) {
|
|
// 8-bits unsigned value.
|
|
const y = glyf.getUint8(pos++);
|
|
lastCoordinate += flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR ? y : -y;
|
|
yCoordinates.push(lastCoordinate);
|
|
} else if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) {
|
|
// IS_SAME.
|
|
yCoordinates.push(lastCoordinate);
|
|
} else {
|
|
lastCoordinate += glyf.getInt16(pos);
|
|
pos += 2;
|
|
yCoordinates.push(lastCoordinate);
|
|
}
|
|
|
|
pointFlags.push((flag & ON_CURVE_POINT) | (flag & OVERLAP_SIMPLE));
|
|
|
|
if (endPtsOfContours[endPtsOfContoursIndex] === i) {
|
|
// Next entry is the first one of a new contour.
|
|
xCoordinates = allXCoordinates[endPtsOfContoursIndex];
|
|
endPtsOfContoursIndex++;
|
|
contours.push(
|
|
new Contour({
|
|
flags: pointFlags,
|
|
xCoordinates,
|
|
yCoordinates,
|
|
})
|
|
);
|
|
yCoordinates = [];
|
|
pointFlags = [];
|
|
}
|
|
}
|
|
|
|
return new SimpleGlyph({
|
|
contours,
|
|
instructions,
|
|
});
|
|
}
|
|
|
|
getSize() {
|
|
let size = this.contours.length * 2 + 2 + this.instructions.length;
|
|
let lastX = 0;
|
|
let lastY = 0;
|
|
for (const contour of this.contours) {
|
|
size += contour.flags.length;
|
|
for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
|
|
const x = contour.xCoordinates[i];
|
|
const y = contour.yCoordinates[i];
|
|
let abs = Math.abs(x - lastX);
|
|
if (abs > 255) {
|
|
size += 2;
|
|
} else if (abs > 0) {
|
|
size += 1;
|
|
}
|
|
lastX = x;
|
|
|
|
abs = Math.abs(y - lastY);
|
|
if (abs > 255) {
|
|
size += 2;
|
|
} else if (abs > 0) {
|
|
size += 1;
|
|
}
|
|
lastY = y;
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
write(pos, buf) {
|
|
const spos = pos;
|
|
const xCoordinates = [];
|
|
const yCoordinates = [];
|
|
const flags = [];
|
|
let lastX = 0;
|
|
let lastY = 0;
|
|
|
|
for (const contour of this.contours) {
|
|
for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
|
|
let flag = contour.flags[i];
|
|
const x = contour.xCoordinates[i];
|
|
let delta = x - lastX;
|
|
if (delta === 0) {
|
|
flag |= X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR;
|
|
xCoordinates.push(0);
|
|
} else {
|
|
const abs = Math.abs(delta);
|
|
if (abs <= 255) {
|
|
flag |=
|
|
delta >= 0
|
|
? X_SHORT_VECTOR | X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
|
|
: X_SHORT_VECTOR;
|
|
xCoordinates.push(abs);
|
|
} else {
|
|
xCoordinates.push(delta);
|
|
}
|
|
}
|
|
lastX = x;
|
|
|
|
const y = contour.yCoordinates[i];
|
|
delta = y - lastY;
|
|
if (delta === 0) {
|
|
flag |= Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR;
|
|
yCoordinates.push(0);
|
|
} else {
|
|
const abs = Math.abs(delta);
|
|
if (abs <= 255) {
|
|
flag |=
|
|
delta >= 0
|
|
? Y_SHORT_VECTOR | Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR
|
|
: Y_SHORT_VECTOR;
|
|
yCoordinates.push(abs);
|
|
} else {
|
|
yCoordinates.push(delta);
|
|
}
|
|
}
|
|
lastY = y;
|
|
|
|
flags.push(flag);
|
|
}
|
|
|
|
// Write endPtsOfContours entry.
|
|
buf.setUint16(pos, xCoordinates.length - 1);
|
|
pos += 2;
|
|
}
|
|
|
|
// Write instructionLength.
|
|
buf.setUint16(pos, this.instructions.length);
|
|
pos += 2;
|
|
if (this.instructions.length) {
|
|
// Write instructions.
|
|
new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(
|
|
this.instructions,
|
|
pos
|
|
);
|
|
pos += this.instructions.length;
|
|
}
|
|
|
|
// Write flags.
|
|
for (const flag of flags) {
|
|
buf.setUint8(pos++, flag);
|
|
}
|
|
|
|
// Write xCoordinates.
|
|
for (let i = 0, ii = xCoordinates.length; i < ii; i++) {
|
|
const x = xCoordinates[i];
|
|
const flag = flags[i];
|
|
if (flag & X_SHORT_VECTOR) {
|
|
buf.setUint8(pos++, x);
|
|
} else if (!(flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)) {
|
|
buf.setInt16(pos, x);
|
|
pos += 2;
|
|
}
|
|
}
|
|
|
|
// Write yCoordinates.
|
|
for (let i = 0, ii = yCoordinates.length; i < ii; i++) {
|
|
const y = yCoordinates[i];
|
|
const flag = flags[i];
|
|
if (flag & Y_SHORT_VECTOR) {
|
|
buf.setUint8(pos++, y);
|
|
} else if (!(flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)) {
|
|
buf.setInt16(pos, y);
|
|
pos += 2;
|
|
}
|
|
}
|
|
|
|
return pos - spos;
|
|
}
|
|
|
|
scale(x, factor) {
|
|
for (const contour of this.contours) {
|
|
if (contour.xCoordinates.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
|
|
contour.xCoordinates[i] = Math.round(
|
|
x + (contour.xCoordinates[i] - x) * factor
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class CompositeGlyph {
|
|
constructor({
|
|
flags,
|
|
glyphIndex,
|
|
argument1,
|
|
argument2,
|
|
transf,
|
|
instructions,
|
|
}) {
|
|
this.flags = flags;
|
|
this.glyphIndex = glyphIndex;
|
|
this.argument1 = argument1;
|
|
this.argument2 = argument2;
|
|
this.transf = transf;
|
|
this.instructions = instructions;
|
|
}
|
|
|
|
static parse(pos, glyf) {
|
|
const spos = pos;
|
|
const transf = [];
|
|
let flags = glyf.getUint16(pos);
|
|
const glyphIndex = glyf.getUint16(pos + 2);
|
|
pos += 4;
|
|
|
|
let argument1, argument2;
|
|
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
|
if (flags & ARGS_ARE_XY_VALUES) {
|
|
argument1 = glyf.getInt16(pos);
|
|
argument2 = glyf.getInt16(pos + 2);
|
|
} else {
|
|
argument1 = glyf.getUint16(pos);
|
|
argument2 = glyf.getUint16(pos + 2);
|
|
}
|
|
pos += 4;
|
|
flags ^= ARG_1_AND_2_ARE_WORDS;
|
|
} else {
|
|
if (flags & ARGS_ARE_XY_VALUES) {
|
|
argument1 = glyf.getInt8(pos);
|
|
argument2 = glyf.getInt8(pos + 1);
|
|
} else {
|
|
argument1 = glyf.getUint8(pos);
|
|
argument2 = glyf.getUint8(pos + 1);
|
|
}
|
|
pos += 2;
|
|
}
|
|
|
|
if (flags & WE_HAVE_A_SCALE) {
|
|
// Single F2.14.
|
|
transf.push(glyf.getUint16(pos));
|
|
pos += 2;
|
|
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
|
// Two F2.14.
|
|
transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2));
|
|
pos += 4;
|
|
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
|
// Four F2.14.
|
|
transf.push(
|
|
glyf.getUint16(pos),
|
|
glyf.getUint16(pos + 2),
|
|
glyf.getUint16(pos + 4),
|
|
glyf.getUint16(pos + 6)
|
|
);
|
|
pos += 8;
|
|
}
|
|
|
|
let instructions = null;
|
|
if (flags & WE_HAVE_INSTRUCTIONS) {
|
|
const instructionLength = glyf.getUint16(pos);
|
|
pos += 2;
|
|
instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength);
|
|
pos += instructionLength;
|
|
}
|
|
|
|
return [
|
|
pos - spos,
|
|
new CompositeGlyph({
|
|
flags,
|
|
glyphIndex,
|
|
argument1,
|
|
argument2,
|
|
transf,
|
|
instructions,
|
|
}),
|
|
];
|
|
}
|
|
|
|
getSize() {
|
|
let size = 2 + 2 + this.transf.length * 2;
|
|
if (this.flags & WE_HAVE_INSTRUCTIONS) {
|
|
size += 2 + this.instructions.length;
|
|
}
|
|
|
|
size += 2;
|
|
if (this.flags & 2) {
|
|
// Arguments are signed.
|
|
if (
|
|
!(
|
|
this.argument1 >= -128 &&
|
|
this.argument1 <= 127 &&
|
|
this.argument2 >= -128 &&
|
|
this.argument2 <= 127
|
|
)
|
|
) {
|
|
size += 2;
|
|
}
|
|
} else {
|
|
if (
|
|
!(
|
|
this.argument1 >= 0 &&
|
|
this.argument1 <= 255 &&
|
|
this.argument2 >= 0 &&
|
|
this.argument2 <= 255
|
|
)
|
|
) {
|
|
size += 2;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
write(pos, buf) {
|
|
const spos = pos;
|
|
|
|
if (this.flags & ARGS_ARE_XY_VALUES) {
|
|
// Arguments are signed.
|
|
if (
|
|
!(
|
|
this.argument1 >= -128 &&
|
|
this.argument1 <= 127 &&
|
|
this.argument2 >= -128 &&
|
|
this.argument2 <= 127
|
|
)
|
|
) {
|
|
this.flags |= ARG_1_AND_2_ARE_WORDS;
|
|
}
|
|
} else {
|
|
if (
|
|
!(
|
|
this.argument1 >= 0 &&
|
|
this.argument1 <= 255 &&
|
|
this.argument2 >= 0 &&
|
|
this.argument2 <= 255
|
|
)
|
|
) {
|
|
this.flags |= ARG_1_AND_2_ARE_WORDS;
|
|
}
|
|
}
|
|
|
|
buf.setUint16(pos, this.flags);
|
|
buf.setUint16(pos + 2, this.glyphIndex);
|
|
pos += 4;
|
|
|
|
if (this.flags & ARG_1_AND_2_ARE_WORDS) {
|
|
if (this.flags & ARGS_ARE_XY_VALUES) {
|
|
buf.setInt16(pos, this.argument1);
|
|
buf.setInt16(pos + 2, this.argument2);
|
|
} else {
|
|
buf.setUint16(pos, this.argument1);
|
|
buf.setUint16(pos + 2, this.argument2);
|
|
}
|
|
pos += 4;
|
|
} else {
|
|
buf.setUint8(pos, this.argument1);
|
|
buf.setUint8(pos + 1, this.argument2);
|
|
pos += 2;
|
|
}
|
|
|
|
if (this.flags & WE_HAVE_INSTRUCTIONS) {
|
|
buf.setUint16(pos, this.instructions.length);
|
|
pos += 2;
|
|
// Write instructions.
|
|
if (this.instructions.length) {
|
|
new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(
|
|
this.instructions,
|
|
pos
|
|
);
|
|
pos += this.instructions.length;
|
|
}
|
|
}
|
|
|
|
return pos - spos;
|
|
}
|
|
|
|
scale(x, factor) {}
|
|
}
|
|
|
|
export { GlyfTable };
|