glyf.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. /* Copyright 2021 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. const ON_CURVE_POINT = 1 << 0;
  16. const X_SHORT_VECTOR = 1 << 1;
  17. const Y_SHORT_VECTOR = 1 << 2;
  18. const REPEAT_FLAG = 1 << 3;
  19. const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1 << 4;
  20. const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1 << 5;
  21. const OVERLAP_SIMPLE = 1 << 6;
  22. const ARG_1_AND_2_ARE_WORDS = 1 << 0;
  23. const ARGS_ARE_XY_VALUES = 1 << 1;
  24. // const ROUND_XY_TO_GRID = 1 << 2;
  25. const WE_HAVE_A_SCALE = 1 << 3;
  26. const MORE_COMPONENTS = 1 << 5;
  27. const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
  28. const WE_HAVE_A_TWO_BY_TWO = 1 << 7;
  29. const WE_HAVE_INSTRUCTIONS = 1 << 8;
  30. // const USE_MY_METRICS = 1 << 9;
  31. // const OVERLAP_COMPOUND = 1 << 10;
  32. // const SCALED_COMPONENT_OFFSET = 1 << 11;
  33. // const UNSCALED_COMPONENT_OFFSET = 1 << 12;
  34. /**
  35. * GlyfTable object represents a glyf table containing glyph information:
  36. * - glyph header (xMin, yMin, xMax, yMax);
  37. * - contours if any;
  38. * - components if the glyph is a composite.
  39. *
  40. * It's possible to re-scale each glyph in order to have a new font which
  41. * exactly fits an other one: the goal is to be able to build some substitution
  42. * font for well-known fonts (Myriad, Arial, ...).
  43. *
  44. * A full description of glyf table can be found here
  45. * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
  46. */
  47. class GlyfTable {
  48. constructor({ glyfTable, isGlyphLocationsLong, locaTable, numGlyphs }) {
  49. this.glyphs = [];
  50. const loca = new DataView(
  51. locaTable.buffer,
  52. locaTable.byteOffset,
  53. locaTable.byteLength
  54. );
  55. const glyf = new DataView(
  56. glyfTable.buffer,
  57. glyfTable.byteOffset,
  58. glyfTable.byteLength
  59. );
  60. const offsetSize = isGlyphLocationsLong ? 4 : 2;
  61. let prev = isGlyphLocationsLong ? loca.getUint32(0) : 2 * loca.getUint16(0);
  62. let pos = 0;
  63. for (let i = 0; i < numGlyphs; i++) {
  64. pos += offsetSize;
  65. const next = isGlyphLocationsLong
  66. ? loca.getUint32(pos)
  67. : 2 * loca.getUint16(pos);
  68. if (next === prev) {
  69. this.glyphs.push(new Glyph({}));
  70. continue;
  71. }
  72. const glyph = Glyph.parse(prev, glyf);
  73. this.glyphs.push(glyph);
  74. prev = next;
  75. }
  76. }
  77. getSize() {
  78. return this.glyphs.reduce((a, g) => {
  79. const size = g.getSize();
  80. // Round to next multiple of 4 if needed.
  81. return a + ((size + 3) & ~3);
  82. }, 0);
  83. }
  84. write() {
  85. const totalSize = this.getSize();
  86. const glyfTable = new DataView(new ArrayBuffer(totalSize));
  87. const isLocationLong = totalSize > /* 0xffff * 2 */ 0x1fffe;
  88. const offsetSize = isLocationLong ? 4 : 2;
  89. const locaTable = new DataView(
  90. new ArrayBuffer((this.glyphs.length + 1) * offsetSize)
  91. );
  92. if (isLocationLong) {
  93. locaTable.setUint32(0, 0);
  94. } else {
  95. locaTable.setUint16(0, 0);
  96. }
  97. let pos = 0;
  98. let locaIndex = 0;
  99. for (const glyph of this.glyphs) {
  100. pos += glyph.write(pos, glyfTable);
  101. // Round to next multiple of 4 if needed.
  102. pos = (pos + 3) & ~3;
  103. locaIndex += offsetSize;
  104. if (isLocationLong) {
  105. locaTable.setUint32(locaIndex, pos);
  106. } else {
  107. locaTable.setUint16(locaIndex, pos >> 1);
  108. }
  109. }
  110. return {
  111. isLocationLong,
  112. loca: new Uint8Array(locaTable.buffer),
  113. glyf: new Uint8Array(glyfTable.buffer),
  114. };
  115. }
  116. scale(factors) {
  117. for (let i = 0, ii = this.glyphs.length; i < ii; i++) {
  118. this.glyphs[i].scale(factors[i]);
  119. }
  120. }
  121. }
  122. class Glyph {
  123. constructor({ header = null, simple = null, composites = null }) {
  124. this.header = header;
  125. this.simple = simple;
  126. this.composites = composites;
  127. }
  128. static parse(pos, glyf) {
  129. const [read, header] = GlyphHeader.parse(pos, glyf);
  130. pos += read;
  131. if (header.numberOfContours < 0) {
  132. // Composite glyph.
  133. const composites = [];
  134. while (true) {
  135. const [n, composite] = CompositeGlyph.parse(pos, glyf);
  136. pos += n;
  137. composites.push(composite);
  138. if (!(composite.flags & MORE_COMPONENTS)) {
  139. break;
  140. }
  141. }
  142. return new Glyph({ header, composites });
  143. }
  144. const simple = SimpleGlyph.parse(pos, glyf, header.numberOfContours);
  145. return new Glyph({ header, simple });
  146. }
  147. getSize() {
  148. if (!this.header) {
  149. return 0;
  150. }
  151. const size = this.simple
  152. ? this.simple.getSize()
  153. : this.composites.reduce((a, c) => a + c.getSize(), 0);
  154. return this.header.getSize() + size;
  155. }
  156. write(pos, buf) {
  157. if (!this.header) {
  158. return 0;
  159. }
  160. const spos = pos;
  161. pos += this.header.write(pos, buf);
  162. if (this.simple) {
  163. pos += this.simple.write(pos, buf);
  164. } else {
  165. for (const composite of this.composites) {
  166. pos += composite.write(pos, buf);
  167. }
  168. }
  169. return pos - spos;
  170. }
  171. scale(factor) {
  172. if (!this.header) {
  173. return;
  174. }
  175. const xMiddle = (this.header.xMin + this.header.xMax) / 2;
  176. this.header.scale(xMiddle, factor);
  177. if (this.simple) {
  178. this.simple.scale(xMiddle, factor);
  179. } else {
  180. for (const composite of this.composites) {
  181. composite.scale(xMiddle, factor);
  182. }
  183. }
  184. }
  185. }
  186. class GlyphHeader {
  187. constructor({ numberOfContours, xMin, yMin, xMax, yMax }) {
  188. this.numberOfContours = numberOfContours;
  189. this.xMin = xMin;
  190. this.yMin = yMin;
  191. this.xMax = xMax;
  192. this.yMax = yMax;
  193. }
  194. static parse(pos, glyf) {
  195. return [
  196. 10,
  197. new GlyphHeader({
  198. numberOfContours: glyf.getInt16(pos),
  199. xMin: glyf.getInt16(pos + 2),
  200. yMin: glyf.getInt16(pos + 4),
  201. xMax: glyf.getInt16(pos + 6),
  202. yMax: glyf.getInt16(pos + 8),
  203. }),
  204. ];
  205. }
  206. getSize() {
  207. return 10;
  208. }
  209. write(pos, buf) {
  210. buf.setInt16(pos, this.numberOfContours);
  211. buf.setInt16(pos + 2, this.xMin);
  212. buf.setInt16(pos + 4, this.yMin);
  213. buf.setInt16(pos + 6, this.xMax);
  214. buf.setInt16(pos + 8, this.yMax);
  215. return 10;
  216. }
  217. scale(x, factor) {
  218. this.xMin = Math.round(x + (this.xMin - x) * factor);
  219. this.xMax = Math.round(x + (this.xMax - x) * factor);
  220. }
  221. }
  222. class Contour {
  223. constructor({ flags, xCoordinates, yCoordinates }) {
  224. this.xCoordinates = xCoordinates;
  225. this.yCoordinates = yCoordinates;
  226. this.flags = flags;
  227. }
  228. }
  229. class SimpleGlyph {
  230. constructor({ contours, instructions }) {
  231. this.contours = contours;
  232. this.instructions = instructions;
  233. }
  234. static parse(pos, glyf, numberOfContours) {
  235. const endPtsOfContours = [];
  236. for (let i = 0; i < numberOfContours; i++) {
  237. const endPt = glyf.getUint16(pos);
  238. pos += 2;
  239. endPtsOfContours.push(endPt);
  240. }
  241. const numberOfPt = endPtsOfContours[numberOfContours - 1] + 1;
  242. const instructionLength = glyf.getUint16(pos);
  243. pos += 2;
  244. const instructions = new Uint8Array(glyf).slice(
  245. pos,
  246. pos + instructionLength
  247. );
  248. pos += instructionLength;
  249. const flags = [];
  250. for (let i = 0; i < numberOfPt; pos++, i++) {
  251. let flag = glyf.getUint8(pos);
  252. flags.push(flag);
  253. if (flag & REPEAT_FLAG) {
  254. const count = glyf.getUint8(++pos);
  255. flag ^= REPEAT_FLAG;
  256. for (let m = 0; m < count; m++) {
  257. flags.push(flag);
  258. }
  259. i += count;
  260. }
  261. }
  262. const allXCoordinates = [];
  263. let xCoordinates = [];
  264. let yCoordinates = [];
  265. let pointFlags = [];
  266. const contours = [];
  267. let endPtsOfContoursIndex = 0;
  268. let lastCoordinate = 0;
  269. // Get x coordinates.
  270. for (let i = 0; i < numberOfPt; i++) {
  271. const flag = flags[i];
  272. if (flag & X_SHORT_VECTOR) {
  273. // 8-bits unsigned value.
  274. const x = glyf.getUint8(pos++);
  275. lastCoordinate += flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR ? x : -x;
  276. xCoordinates.push(lastCoordinate);
  277. } else if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {
  278. // IS_SAME.
  279. xCoordinates.push(lastCoordinate);
  280. } else {
  281. lastCoordinate += glyf.getInt16(pos);
  282. pos += 2;
  283. xCoordinates.push(lastCoordinate);
  284. }
  285. if (endPtsOfContours[endPtsOfContoursIndex] === i) {
  286. // Next entry is the first one of a new contour.
  287. endPtsOfContoursIndex++;
  288. allXCoordinates.push(xCoordinates);
  289. xCoordinates = [];
  290. }
  291. }
  292. lastCoordinate = 0;
  293. endPtsOfContoursIndex = 0;
  294. for (let i = 0; i < numberOfPt; i++) {
  295. const flag = flags[i];
  296. if (flag & Y_SHORT_VECTOR) {
  297. // 8-bits unsigned value.
  298. const y = glyf.getUint8(pos++);
  299. lastCoordinate += flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR ? y : -y;
  300. yCoordinates.push(lastCoordinate);
  301. } else if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) {
  302. // IS_SAME.
  303. yCoordinates.push(lastCoordinate);
  304. } else {
  305. lastCoordinate += glyf.getInt16(pos);
  306. pos += 2;
  307. yCoordinates.push(lastCoordinate);
  308. }
  309. pointFlags.push((flag & ON_CURVE_POINT) | (flag & OVERLAP_SIMPLE));
  310. if (endPtsOfContours[endPtsOfContoursIndex] === i) {
  311. // Next entry is the first one of a new contour.
  312. xCoordinates = allXCoordinates[endPtsOfContoursIndex];
  313. endPtsOfContoursIndex++;
  314. contours.push(
  315. new Contour({
  316. flags: pointFlags,
  317. xCoordinates,
  318. yCoordinates,
  319. })
  320. );
  321. yCoordinates = [];
  322. pointFlags = [];
  323. }
  324. }
  325. return new SimpleGlyph({
  326. contours,
  327. instructions,
  328. });
  329. }
  330. getSize() {
  331. let size = this.contours.length * 2 + 2 + this.instructions.length;
  332. let lastX = 0;
  333. let lastY = 0;
  334. for (const contour of this.contours) {
  335. size += contour.flags.length;
  336. for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
  337. const x = contour.xCoordinates[i];
  338. const y = contour.yCoordinates[i];
  339. let abs = Math.abs(x - lastX);
  340. if (abs > 255) {
  341. size += 2;
  342. } else if (abs > 0) {
  343. size += 1;
  344. }
  345. lastX = x;
  346. abs = Math.abs(y - lastY);
  347. if (abs > 255) {
  348. size += 2;
  349. } else if (abs > 0) {
  350. size += 1;
  351. }
  352. lastY = y;
  353. }
  354. }
  355. return size;
  356. }
  357. write(pos, buf) {
  358. const spos = pos;
  359. const xCoordinates = [];
  360. const yCoordinates = [];
  361. const flags = [];
  362. let lastX = 0;
  363. let lastY = 0;
  364. for (const contour of this.contours) {
  365. for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
  366. let flag = contour.flags[i];
  367. const x = contour.xCoordinates[i];
  368. let delta = x - lastX;
  369. if (delta === 0) {
  370. flag |= X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR;
  371. xCoordinates.push(0);
  372. } else {
  373. const abs = Math.abs(delta);
  374. if (abs <= 255) {
  375. flag |=
  376. delta >= 0
  377. ? X_SHORT_VECTOR | X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
  378. : X_SHORT_VECTOR;
  379. xCoordinates.push(abs);
  380. } else {
  381. xCoordinates.push(delta);
  382. }
  383. }
  384. lastX = x;
  385. const y = contour.yCoordinates[i];
  386. delta = y - lastY;
  387. if (delta === 0) {
  388. flag |= Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR;
  389. yCoordinates.push(0);
  390. } else {
  391. const abs = Math.abs(delta);
  392. if (abs <= 255) {
  393. flag |=
  394. delta >= 0
  395. ? Y_SHORT_VECTOR | Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR
  396. : Y_SHORT_VECTOR;
  397. yCoordinates.push(abs);
  398. } else {
  399. yCoordinates.push(delta);
  400. }
  401. }
  402. lastY = y;
  403. flags.push(flag);
  404. }
  405. // Write endPtsOfContours entry.
  406. buf.setUint16(pos, xCoordinates.length - 1);
  407. pos += 2;
  408. }
  409. // Write instructionLength.
  410. buf.setUint16(pos, this.instructions.length);
  411. pos += 2;
  412. if (this.instructions.length) {
  413. // Write instructions.
  414. new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(
  415. this.instructions,
  416. pos
  417. );
  418. pos += this.instructions.length;
  419. }
  420. // Write flags.
  421. for (const flag of flags) {
  422. buf.setUint8(pos++, flag);
  423. }
  424. // Write xCoordinates.
  425. for (let i = 0, ii = xCoordinates.length; i < ii; i++) {
  426. const x = xCoordinates[i];
  427. const flag = flags[i];
  428. if (flag & X_SHORT_VECTOR) {
  429. buf.setUint8(pos++, x);
  430. } else if (!(flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)) {
  431. buf.setInt16(pos, x);
  432. pos += 2;
  433. }
  434. }
  435. // Write yCoordinates.
  436. for (let i = 0, ii = yCoordinates.length; i < ii; i++) {
  437. const y = yCoordinates[i];
  438. const flag = flags[i];
  439. if (flag & Y_SHORT_VECTOR) {
  440. buf.setUint8(pos++, y);
  441. } else if (!(flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)) {
  442. buf.setInt16(pos, y);
  443. pos += 2;
  444. }
  445. }
  446. return pos - spos;
  447. }
  448. scale(x, factor) {
  449. for (const contour of this.contours) {
  450. if (contour.xCoordinates.length === 0) {
  451. continue;
  452. }
  453. for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
  454. contour.xCoordinates[i] = Math.round(
  455. x + (contour.xCoordinates[i] - x) * factor
  456. );
  457. }
  458. }
  459. }
  460. }
  461. class CompositeGlyph {
  462. constructor({
  463. flags,
  464. glyphIndex,
  465. argument1,
  466. argument2,
  467. transf,
  468. instructions,
  469. }) {
  470. this.flags = flags;
  471. this.glyphIndex = glyphIndex;
  472. this.argument1 = argument1;
  473. this.argument2 = argument2;
  474. this.transf = transf;
  475. this.instructions = instructions;
  476. }
  477. static parse(pos, glyf) {
  478. const spos = pos;
  479. const transf = [];
  480. let flags = glyf.getUint16(pos);
  481. const glyphIndex = glyf.getUint16(pos + 2);
  482. pos += 4;
  483. let argument1, argument2;
  484. if (flags & ARG_1_AND_2_ARE_WORDS) {
  485. if (flags & ARGS_ARE_XY_VALUES) {
  486. argument1 = glyf.getInt16(pos);
  487. argument2 = glyf.getInt16(pos + 2);
  488. } else {
  489. argument1 = glyf.getUint16(pos);
  490. argument2 = glyf.getUint16(pos + 2);
  491. }
  492. pos += 4;
  493. flags ^= ARG_1_AND_2_ARE_WORDS;
  494. } else {
  495. if (flags & ARGS_ARE_XY_VALUES) {
  496. argument1 = glyf.getInt8(pos);
  497. argument2 = glyf.getInt8(pos + 1);
  498. } else {
  499. argument1 = glyf.getUint8(pos);
  500. argument2 = glyf.getUint8(pos + 1);
  501. }
  502. pos += 2;
  503. }
  504. if (flags & WE_HAVE_A_SCALE) {
  505. // Single F2.14.
  506. transf.push(glyf.getUint16(pos));
  507. pos += 2;
  508. } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
  509. // Two F2.14.
  510. transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2));
  511. pos += 4;
  512. } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
  513. // Four F2.14.
  514. transf.push(
  515. glyf.getUint16(pos),
  516. glyf.getUint16(pos + 2),
  517. glyf.getUint16(pos + 4),
  518. glyf.getUint16(pos + 6)
  519. );
  520. pos += 8;
  521. }
  522. let instructions = null;
  523. if (flags & WE_HAVE_INSTRUCTIONS) {
  524. const instructionLength = glyf.getUint16(pos);
  525. pos += 2;
  526. instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength);
  527. pos += instructionLength;
  528. }
  529. return [
  530. pos - spos,
  531. new CompositeGlyph({
  532. flags,
  533. glyphIndex,
  534. argument1,
  535. argument2,
  536. transf,
  537. instructions,
  538. }),
  539. ];
  540. }
  541. getSize() {
  542. let size = 2 + 2 + this.transf.length * 2;
  543. if (this.flags & WE_HAVE_INSTRUCTIONS) {
  544. size += 2 + this.instructions.length;
  545. }
  546. size += 2;
  547. if (this.flags & 2) {
  548. // Arguments are signed.
  549. if (
  550. !(
  551. this.argument1 >= -128 &&
  552. this.argument1 <= 127 &&
  553. this.argument2 >= -128 &&
  554. this.argument2 <= 127
  555. )
  556. ) {
  557. size += 2;
  558. }
  559. } else {
  560. if (
  561. !(
  562. this.argument1 >= 0 &&
  563. this.argument1 <= 255 &&
  564. this.argument2 >= 0 &&
  565. this.argument2 <= 255
  566. )
  567. ) {
  568. size += 2;
  569. }
  570. }
  571. return size;
  572. }
  573. write(pos, buf) {
  574. const spos = pos;
  575. if (this.flags & ARGS_ARE_XY_VALUES) {
  576. // Arguments are signed.
  577. if (
  578. !(
  579. this.argument1 >= -128 &&
  580. this.argument1 <= 127 &&
  581. this.argument2 >= -128 &&
  582. this.argument2 <= 127
  583. )
  584. ) {
  585. this.flags |= ARG_1_AND_2_ARE_WORDS;
  586. }
  587. } else {
  588. if (
  589. !(
  590. this.argument1 >= 0 &&
  591. this.argument1 <= 255 &&
  592. this.argument2 >= 0 &&
  593. this.argument2 <= 255
  594. )
  595. ) {
  596. this.flags |= ARG_1_AND_2_ARE_WORDS;
  597. }
  598. }
  599. buf.setUint16(pos, this.flags);
  600. buf.setUint16(pos + 2, this.glyphIndex);
  601. pos += 4;
  602. if (this.flags & ARG_1_AND_2_ARE_WORDS) {
  603. if (this.flags & ARGS_ARE_XY_VALUES) {
  604. buf.setInt16(pos, this.argument1);
  605. buf.setInt16(pos + 2, this.argument2);
  606. } else {
  607. buf.setUint16(pos, this.argument1);
  608. buf.setUint16(pos + 2, this.argument2);
  609. }
  610. pos += 4;
  611. } else {
  612. buf.setUint8(pos, this.argument1);
  613. buf.setUint8(pos + 1, this.argument2);
  614. pos += 2;
  615. }
  616. if (this.flags & WE_HAVE_INSTRUCTIONS) {
  617. buf.setUint16(pos, this.instructions.length);
  618. pos += 2;
  619. // Write instructions.
  620. if (this.instructions.length) {
  621. new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(
  622. this.instructions,
  623. pos
  624. );
  625. pos += this.instructions.length;
  626. }
  627. }
  628. return pos - spos;
  629. }
  630. scale(x, factor) {}
  631. }
  632. export { GlyfTable };