font_renderer.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /* Copyright 2017 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. 'use strict';
  16. var sharedUtil = require('../shared/util.js');
  17. var coreStream = require('./stream.js');
  18. var coreGlyphList = require('./glyphlist.js');
  19. var coreEncodings = require('./encodings.js');
  20. var coreCFFParser = require('./cff_parser.js');
  21. var Util = sharedUtil.Util;
  22. var bytesToString = sharedUtil.bytesToString;
  23. var error = sharedUtil.error;
  24. var Stream = coreStream.Stream;
  25. var getGlyphsUnicode = coreGlyphList.getGlyphsUnicode;
  26. var StandardEncoding = coreEncodings.StandardEncoding;
  27. var CFFParser = coreCFFParser.CFFParser;
  28. var FontRendererFactory = function FontRendererFactoryClosure() {
  29. function getLong(data, offset) {
  30. return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
  31. }
  32. function getUshort(data, offset) {
  33. return data[offset] << 8 | data[offset + 1];
  34. }
  35. function parseCmap(data, start, end) {
  36. var offset = getUshort(data, start + 2) === 1 ? getLong(data, start + 8) : getLong(data, start + 16);
  37. var format = getUshort(data, start + offset);
  38. var ranges, p, i;
  39. if (format === 4) {
  40. getUshort(data, start + offset + 2);
  41. var segCount = getUshort(data, start + offset + 6) >> 1;
  42. p = start + offset + 14;
  43. ranges = [];
  44. for (i = 0; i < segCount; i++, p += 2) {
  45. ranges[i] = { end: getUshort(data, p) };
  46. }
  47. p += 2;
  48. for (i = 0; i < segCount; i++, p += 2) {
  49. ranges[i].start = getUshort(data, p);
  50. }
  51. for (i = 0; i < segCount; i++, p += 2) {
  52. ranges[i].idDelta = getUshort(data, p);
  53. }
  54. for (i = 0; i < segCount; i++, p += 2) {
  55. var idOffset = getUshort(data, p);
  56. if (idOffset === 0) {
  57. continue;
  58. }
  59. ranges[i].ids = [];
  60. for (var j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
  61. ranges[i].ids[j] = getUshort(data, p + idOffset);
  62. idOffset += 2;
  63. }
  64. }
  65. return ranges;
  66. } else if (format === 12) {
  67. getLong(data, start + offset + 4);
  68. var groups = getLong(data, start + offset + 12);
  69. p = start + offset + 16;
  70. ranges = [];
  71. for (i = 0; i < groups; i++) {
  72. ranges.push({
  73. start: getLong(data, p),
  74. end: getLong(data, p + 4),
  75. idDelta: getLong(data, p + 8) - getLong(data, p)
  76. });
  77. p += 12;
  78. }
  79. return ranges;
  80. }
  81. error('not supported cmap: ' + format);
  82. }
  83. function parseCff(data, start, end, seacAnalysisEnabled) {
  84. var properties = {};
  85. var parser = new CFFParser(new Stream(data, start, end - start), properties, seacAnalysisEnabled);
  86. var cff = parser.parse();
  87. return {
  88. glyphs: cff.charStrings.objects,
  89. subrs: cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex && cff.topDict.privateDict.subrsIndex.objects,
  90. gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects
  91. };
  92. }
  93. function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
  94. var itemSize, itemDecode;
  95. if (isGlyphLocationsLong) {
  96. itemSize = 4;
  97. itemDecode = function fontItemDecodeLong(data, offset) {
  98. return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
  99. };
  100. } else {
  101. itemSize = 2;
  102. itemDecode = function fontItemDecode(data, offset) {
  103. return data[offset] << 9 | data[offset + 1] << 1;
  104. };
  105. }
  106. var glyphs = [];
  107. var startOffset = itemDecode(loca, 0);
  108. for (var j = itemSize; j < loca.length; j += itemSize) {
  109. var endOffset = itemDecode(loca, j);
  110. glyphs.push(glyf.subarray(startOffset, endOffset));
  111. startOffset = endOffset;
  112. }
  113. return glyphs;
  114. }
  115. function lookupCmap(ranges, unicode) {
  116. var code = unicode.charCodeAt(0),
  117. gid = 0;
  118. var l = 0,
  119. r = ranges.length - 1;
  120. while (l < r) {
  121. var c = l + r + 1 >> 1;
  122. if (code < ranges[c].start) {
  123. r = c - 1;
  124. } else {
  125. l = c;
  126. }
  127. }
  128. if (ranges[l].start <= code && code <= ranges[l].end) {
  129. gid = ranges[l].idDelta + (ranges[l].ids ? ranges[l].ids[code - ranges[l].start] : code) & 0xFFFF;
  130. }
  131. return {
  132. charCode: code,
  133. glyphId: gid
  134. };
  135. }
  136. function compileGlyf(code, cmds, font) {
  137. function moveTo(x, y) {
  138. cmds.push({
  139. cmd: 'moveTo',
  140. args: [x, y]
  141. });
  142. }
  143. function lineTo(x, y) {
  144. cmds.push({
  145. cmd: 'lineTo',
  146. args: [x, y]
  147. });
  148. }
  149. function quadraticCurveTo(xa, ya, x, y) {
  150. cmds.push({
  151. cmd: 'quadraticCurveTo',
  152. args: [xa, ya, x, y]
  153. });
  154. }
  155. var i = 0;
  156. var numberOfContours = (code[i] << 24 | code[i + 1] << 16) >> 16;
  157. var flags;
  158. var x = 0,
  159. y = 0;
  160. i += 10;
  161. if (numberOfContours < 0) {
  162. do {
  163. flags = code[i] << 8 | code[i + 1];
  164. var glyphIndex = code[i + 2] << 8 | code[i + 3];
  165. i += 4;
  166. var arg1, arg2;
  167. if (flags & 0x01) {
  168. arg1 = (code[i] << 24 | code[i + 1] << 16) >> 16;
  169. arg2 = (code[i + 2] << 24 | code[i + 3] << 16) >> 16;
  170. i += 4;
  171. } else {
  172. arg1 = code[i++];
  173. arg2 = code[i++];
  174. }
  175. if (flags & 0x02) {
  176. x = arg1;
  177. y = arg2;
  178. } else {
  179. x = 0;
  180. y = 0;
  181. }
  182. var scaleX = 1,
  183. scaleY = 1,
  184. scale01 = 0,
  185. scale10 = 0;
  186. if (flags & 0x08) {
  187. scaleX = scaleY = (code[i] << 24 | code[i + 1] << 16) / 1073741824;
  188. i += 2;
  189. } else if (flags & 0x40) {
  190. scaleX = (code[i] << 24 | code[i + 1] << 16) / 1073741824;
  191. scaleY = (code[i + 2] << 24 | code[i + 3] << 16) / 1073741824;
  192. i += 4;
  193. } else if (flags & 0x80) {
  194. scaleX = (code[i] << 24 | code[i + 1] << 16) / 1073741824;
  195. scale01 = (code[i + 2] << 24 | code[i + 3] << 16) / 1073741824;
  196. scale10 = (code[i + 4] << 24 | code[i + 5] << 16) / 1073741824;
  197. scaleY = (code[i + 6] << 24 | code[i + 7] << 16) / 1073741824;
  198. i += 8;
  199. }
  200. var subglyph = font.glyphs[glyphIndex];
  201. if (subglyph) {
  202. cmds.push({ cmd: 'save' });
  203. cmds.push({
  204. cmd: 'transform',
  205. args: [scaleX, scale01, scale10, scaleY, x, y]
  206. });
  207. compileGlyf(subglyph, cmds, font);
  208. cmds.push({ cmd: 'restore' });
  209. }
  210. } while (flags & 0x20);
  211. } else {
  212. var endPtsOfContours = [];
  213. var j, jj;
  214. for (j = 0; j < numberOfContours; j++) {
  215. endPtsOfContours.push(code[i] << 8 | code[i + 1]);
  216. i += 2;
  217. }
  218. var instructionLength = code[i] << 8 | code[i + 1];
  219. i += 2 + instructionLength;
  220. var numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1;
  221. var points = [];
  222. while (points.length < numberOfPoints) {
  223. flags = code[i++];
  224. var repeat = 1;
  225. if (flags & 0x08) {
  226. repeat += code[i++];
  227. }
  228. while (repeat-- > 0) {
  229. points.push({ flags: flags });
  230. }
  231. }
  232. for (j = 0; j < numberOfPoints; j++) {
  233. switch (points[j].flags & 0x12) {
  234. case 0x00:
  235. x += (code[i] << 24 | code[i + 1] << 16) >> 16;
  236. i += 2;
  237. break;
  238. case 0x02:
  239. x -= code[i++];
  240. break;
  241. case 0x12:
  242. x += code[i++];
  243. break;
  244. }
  245. points[j].x = x;
  246. }
  247. for (j = 0; j < numberOfPoints; j++) {
  248. switch (points[j].flags & 0x24) {
  249. case 0x00:
  250. y += (code[i] << 24 | code[i + 1] << 16) >> 16;
  251. i += 2;
  252. break;
  253. case 0x04:
  254. y -= code[i++];
  255. break;
  256. case 0x24:
  257. y += code[i++];
  258. break;
  259. }
  260. points[j].y = y;
  261. }
  262. var startPoint = 0;
  263. for (i = 0; i < numberOfContours; i++) {
  264. var endPoint = endPtsOfContours[i];
  265. var contour = points.slice(startPoint, endPoint + 1);
  266. if (contour[0].flags & 1) {
  267. contour.push(contour[0]);
  268. } else if (contour[contour.length - 1].flags & 1) {
  269. contour.unshift(contour[contour.length - 1]);
  270. } else {
  271. var p = {
  272. flags: 1,
  273. x: (contour[0].x + contour[contour.length - 1].x) / 2,
  274. y: (contour[0].y + contour[contour.length - 1].y) / 2
  275. };
  276. contour.unshift(p);
  277. contour.push(p);
  278. }
  279. moveTo(contour[0].x, contour[0].y);
  280. for (j = 1, jj = contour.length; j < jj; j++) {
  281. if (contour[j].flags & 1) {
  282. lineTo(contour[j].x, contour[j].y);
  283. } else if (contour[j + 1].flags & 1) {
  284. quadraticCurveTo(contour[j].x, contour[j].y, contour[j + 1].x, contour[j + 1].y);
  285. j++;
  286. } else {
  287. quadraticCurveTo(contour[j].x, contour[j].y, (contour[j].x + contour[j + 1].x) / 2, (contour[j].y + contour[j + 1].y) / 2);
  288. }
  289. }
  290. startPoint = endPoint + 1;
  291. }
  292. }
  293. }
  294. function compileCharString(code, cmds, font) {
  295. var stack = [];
  296. var x = 0,
  297. y = 0;
  298. var stems = 0;
  299. function moveTo(x, y) {
  300. cmds.push({
  301. cmd: 'moveTo',
  302. args: [x, y]
  303. });
  304. }
  305. function lineTo(x, y) {
  306. cmds.push({
  307. cmd: 'lineTo',
  308. args: [x, y]
  309. });
  310. }
  311. function bezierCurveTo(x1, y1, x2, y2, x, y) {
  312. cmds.push({
  313. cmd: 'bezierCurveTo',
  314. args: [x1, y1, x2, y2, x, y]
  315. });
  316. }
  317. function parse(code) {
  318. var i = 0;
  319. while (i < code.length) {
  320. var stackClean = false;
  321. var v = code[i++];
  322. var xa, xb, ya, yb, y1, y2, y3, n, subrCode;
  323. switch (v) {
  324. case 1:
  325. stems += stack.length >> 1;
  326. stackClean = true;
  327. break;
  328. case 3:
  329. stems += stack.length >> 1;
  330. stackClean = true;
  331. break;
  332. case 4:
  333. y += stack.pop();
  334. moveTo(x, y);
  335. stackClean = true;
  336. break;
  337. case 5:
  338. while (stack.length > 0) {
  339. x += stack.shift();
  340. y += stack.shift();
  341. lineTo(x, y);
  342. }
  343. break;
  344. case 6:
  345. while (stack.length > 0) {
  346. x += stack.shift();
  347. lineTo(x, y);
  348. if (stack.length === 0) {
  349. break;
  350. }
  351. y += stack.shift();
  352. lineTo(x, y);
  353. }
  354. break;
  355. case 7:
  356. while (stack.length > 0) {
  357. y += stack.shift();
  358. lineTo(x, y);
  359. if (stack.length === 0) {
  360. break;
  361. }
  362. x += stack.shift();
  363. lineTo(x, y);
  364. }
  365. break;
  366. case 8:
  367. while (stack.length > 0) {
  368. xa = x + stack.shift();
  369. ya = y + stack.shift();
  370. xb = xa + stack.shift();
  371. yb = ya + stack.shift();
  372. x = xb + stack.shift();
  373. y = yb + stack.shift();
  374. bezierCurveTo(xa, ya, xb, yb, x, y);
  375. }
  376. break;
  377. case 10:
  378. n = stack.pop() + font.subrsBias;
  379. subrCode = font.subrs[n];
  380. if (subrCode) {
  381. parse(subrCode);
  382. }
  383. break;
  384. case 11:
  385. return;
  386. case 12:
  387. v = code[i++];
  388. switch (v) {
  389. case 34:
  390. xa = x + stack.shift();
  391. xb = xa + stack.shift();
  392. y1 = y + stack.shift();
  393. x = xb + stack.shift();
  394. bezierCurveTo(xa, y, xb, y1, x, y1);
  395. xa = x + stack.shift();
  396. xb = xa + stack.shift();
  397. x = xb + stack.shift();
  398. bezierCurveTo(xa, y1, xb, y, x, y);
  399. break;
  400. case 35:
  401. xa = x + stack.shift();
  402. ya = y + stack.shift();
  403. xb = xa + stack.shift();
  404. yb = ya + stack.shift();
  405. x = xb + stack.shift();
  406. y = yb + stack.shift();
  407. bezierCurveTo(xa, ya, xb, yb, x, y);
  408. xa = x + stack.shift();
  409. ya = y + stack.shift();
  410. xb = xa + stack.shift();
  411. yb = ya + stack.shift();
  412. x = xb + stack.shift();
  413. y = yb + stack.shift();
  414. bezierCurveTo(xa, ya, xb, yb, x, y);
  415. stack.pop();
  416. break;
  417. case 36:
  418. xa = x + stack.shift();
  419. y1 = y + stack.shift();
  420. xb = xa + stack.shift();
  421. y2 = y1 + stack.shift();
  422. x = xb + stack.shift();
  423. bezierCurveTo(xa, y1, xb, y2, x, y2);
  424. xa = x + stack.shift();
  425. xb = xa + stack.shift();
  426. y3 = y2 + stack.shift();
  427. x = xb + stack.shift();
  428. bezierCurveTo(xa, y2, xb, y3, x, y);
  429. break;
  430. case 37:
  431. var x0 = x,
  432. y0 = y;
  433. xa = x + stack.shift();
  434. ya = y + stack.shift();
  435. xb = xa + stack.shift();
  436. yb = ya + stack.shift();
  437. x = xb + stack.shift();
  438. y = yb + stack.shift();
  439. bezierCurveTo(xa, ya, xb, yb, x, y);
  440. xa = x + stack.shift();
  441. ya = y + stack.shift();
  442. xb = xa + stack.shift();
  443. yb = ya + stack.shift();
  444. x = xb;
  445. y = yb;
  446. if (Math.abs(x - x0) > Math.abs(y - y0)) {
  447. x += stack.shift();
  448. } else {
  449. y += stack.shift();
  450. }
  451. bezierCurveTo(xa, ya, xb, yb, x, y);
  452. break;
  453. default:
  454. error('unknown operator: 12 ' + v);
  455. }
  456. break;
  457. case 14:
  458. if (stack.length >= 4) {
  459. var achar = stack.pop();
  460. var bchar = stack.pop();
  461. y = stack.pop();
  462. x = stack.pop();
  463. cmds.push({ cmd: 'save' });
  464. cmds.push({
  465. cmd: 'translate',
  466. args: [x, y]
  467. });
  468. var cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[achar]]));
  469. compileCharString(font.glyphs[cmap.glyphId], cmds, font);
  470. cmds.push({ cmd: 'restore' });
  471. cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[StandardEncoding[bchar]]));
  472. compileCharString(font.glyphs[cmap.glyphId], cmds, font);
  473. }
  474. return;
  475. case 18:
  476. stems += stack.length >> 1;
  477. stackClean = true;
  478. break;
  479. case 19:
  480. stems += stack.length >> 1;
  481. i += stems + 7 >> 3;
  482. stackClean = true;
  483. break;
  484. case 20:
  485. stems += stack.length >> 1;
  486. i += stems + 7 >> 3;
  487. stackClean = true;
  488. break;
  489. case 21:
  490. y += stack.pop();
  491. x += stack.pop();
  492. moveTo(x, y);
  493. stackClean = true;
  494. break;
  495. case 22:
  496. x += stack.pop();
  497. moveTo(x, y);
  498. stackClean = true;
  499. break;
  500. case 23:
  501. stems += stack.length >> 1;
  502. stackClean = true;
  503. break;
  504. case 24:
  505. while (stack.length > 2) {
  506. xa = x + stack.shift();
  507. ya = y + stack.shift();
  508. xb = xa + stack.shift();
  509. yb = ya + stack.shift();
  510. x = xb + stack.shift();
  511. y = yb + stack.shift();
  512. bezierCurveTo(xa, ya, xb, yb, x, y);
  513. }
  514. x += stack.shift();
  515. y += stack.shift();
  516. lineTo(x, y);
  517. break;
  518. case 25:
  519. while (stack.length > 6) {
  520. x += stack.shift();
  521. y += stack.shift();
  522. lineTo(x, y);
  523. }
  524. xa = x + stack.shift();
  525. ya = y + stack.shift();
  526. xb = xa + stack.shift();
  527. yb = ya + stack.shift();
  528. x = xb + stack.shift();
  529. y = yb + stack.shift();
  530. bezierCurveTo(xa, ya, xb, yb, x, y);
  531. break;
  532. case 26:
  533. if (stack.length % 2) {
  534. x += stack.shift();
  535. }
  536. while (stack.length > 0) {
  537. xa = x;
  538. ya = y + stack.shift();
  539. xb = xa + stack.shift();
  540. yb = ya + stack.shift();
  541. x = xb;
  542. y = yb + stack.shift();
  543. bezierCurveTo(xa, ya, xb, yb, x, y);
  544. }
  545. break;
  546. case 27:
  547. if (stack.length % 2) {
  548. y += stack.shift();
  549. }
  550. while (stack.length > 0) {
  551. xa = x + stack.shift();
  552. ya = y;
  553. xb = xa + stack.shift();
  554. yb = ya + stack.shift();
  555. x = xb + stack.shift();
  556. y = yb;
  557. bezierCurveTo(xa, ya, xb, yb, x, y);
  558. }
  559. break;
  560. case 28:
  561. stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);
  562. i += 2;
  563. break;
  564. case 29:
  565. n = stack.pop() + font.gsubrsBias;
  566. subrCode = font.gsubrs[n];
  567. if (subrCode) {
  568. parse(subrCode);
  569. }
  570. break;
  571. case 30:
  572. while (stack.length > 0) {
  573. xa = x;
  574. ya = y + stack.shift();
  575. xb = xa + stack.shift();
  576. yb = ya + stack.shift();
  577. x = xb + stack.shift();
  578. y = yb + (stack.length === 1 ? stack.shift() : 0);
  579. bezierCurveTo(xa, ya, xb, yb, x, y);
  580. if (stack.length === 0) {
  581. break;
  582. }
  583. xa = x + stack.shift();
  584. ya = y;
  585. xb = xa + stack.shift();
  586. yb = ya + stack.shift();
  587. y = yb + stack.shift();
  588. x = xb + (stack.length === 1 ? stack.shift() : 0);
  589. bezierCurveTo(xa, ya, xb, yb, x, y);
  590. }
  591. break;
  592. case 31:
  593. while (stack.length > 0) {
  594. xa = x + stack.shift();
  595. ya = y;
  596. xb = xa + stack.shift();
  597. yb = ya + stack.shift();
  598. y = yb + stack.shift();
  599. x = xb + (stack.length === 1 ? stack.shift() : 0);
  600. bezierCurveTo(xa, ya, xb, yb, x, y);
  601. if (stack.length === 0) {
  602. break;
  603. }
  604. xa = x;
  605. ya = y + stack.shift();
  606. xb = xa + stack.shift();
  607. yb = ya + stack.shift();
  608. x = xb + stack.shift();
  609. y = yb + (stack.length === 1 ? stack.shift() : 0);
  610. bezierCurveTo(xa, ya, xb, yb, x, y);
  611. }
  612. break;
  613. default:
  614. if (v < 32) {
  615. error('unknown operator: ' + v);
  616. }
  617. if (v < 247) {
  618. stack.push(v - 139);
  619. } else if (v < 251) {
  620. stack.push((v - 247) * 256 + code[i++] + 108);
  621. } else if (v < 255) {
  622. stack.push(-(v - 251) * 256 - code[i++] - 108);
  623. } else {
  624. stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536);
  625. i += 4;
  626. }
  627. break;
  628. }
  629. if (stackClean) {
  630. stack.length = 0;
  631. }
  632. }
  633. }
  634. parse(code);
  635. }
  636. var noop = '';
  637. function CompiledFont(fontMatrix) {
  638. this.compiledGlyphs = Object.create(null);
  639. this.compiledCharCodeToGlyphId = Object.create(null);
  640. this.fontMatrix = fontMatrix;
  641. }
  642. CompiledFont.prototype = {
  643. getPathJs: function getPathJs(unicode) {
  644. var cmap = lookupCmap(this.cmap, unicode);
  645. var fn = this.compiledGlyphs[cmap.glyphId];
  646. if (!fn) {
  647. fn = this.compileGlyph(this.glyphs[cmap.glyphId]);
  648. this.compiledGlyphs[cmap.glyphId] = fn;
  649. }
  650. if (this.compiledCharCodeToGlyphId[cmap.charCode] === undefined) {
  651. this.compiledCharCodeToGlyphId[cmap.charCode] = cmap.glyphId;
  652. }
  653. return fn;
  654. },
  655. compileGlyph: function compileGlyph(code) {
  656. if (!code || code.length === 0 || code[0] === 14) {
  657. return noop;
  658. }
  659. var cmds = [];
  660. cmds.push({ cmd: 'save' });
  661. cmds.push({
  662. cmd: 'transform',
  663. args: this.fontMatrix.slice()
  664. });
  665. cmds.push({
  666. cmd: 'scale',
  667. args: ['size', '-size']
  668. });
  669. this.compileGlyphImpl(code, cmds);
  670. cmds.push({ cmd: 'restore' });
  671. return cmds;
  672. },
  673. compileGlyphImpl: function compileGlyphImpl() {
  674. error('Children classes should implement this.');
  675. },
  676. hasBuiltPath: function hasBuiltPath(unicode) {
  677. var cmap = lookupCmap(this.cmap, unicode);
  678. return this.compiledGlyphs[cmap.glyphId] !== undefined && this.compiledCharCodeToGlyphId[cmap.charCode] !== undefined;
  679. }
  680. };
  681. function TrueTypeCompiled(glyphs, cmap, fontMatrix) {
  682. fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0];
  683. CompiledFont.call(this, fontMatrix);
  684. this.glyphs = glyphs;
  685. this.cmap = cmap;
  686. }
  687. Util.inherit(TrueTypeCompiled, CompiledFont, {
  688. compileGlyphImpl: function compileGlyphImpl(code, cmds) {
  689. compileGlyf(code, cmds, this);
  690. }
  691. });
  692. function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) {
  693. fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0];
  694. CompiledFont.call(this, fontMatrix);
  695. this.glyphs = cffInfo.glyphs;
  696. this.gsubrs = cffInfo.gsubrs || [];
  697. this.subrs = cffInfo.subrs || [];
  698. this.cmap = cmap;
  699. this.glyphNameMap = glyphNameMap || getGlyphsUnicode();
  700. this.gsubrsBias = this.gsubrs.length < 1240 ? 107 : this.gsubrs.length < 33900 ? 1131 : 32768;
  701. this.subrsBias = this.subrs.length < 1240 ? 107 : this.subrs.length < 33900 ? 1131 : 32768;
  702. }
  703. Util.inherit(Type2Compiled, CompiledFont, {
  704. compileGlyphImpl: function compileGlyphImpl(code, cmds) {
  705. compileCharString(code, cmds, this);
  706. }
  707. });
  708. return {
  709. create: function FontRendererFactory_create(font, seacAnalysisEnabled) {
  710. var data = new Uint8Array(font.data);
  711. var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
  712. var numTables = getUshort(data, 4);
  713. for (var i = 0, p = 12; i < numTables; i++, p += 16) {
  714. var tag = bytesToString(data.subarray(p, p + 4));
  715. var offset = getLong(data, p + 8);
  716. var length = getLong(data, p + 12);
  717. switch (tag) {
  718. case 'cmap':
  719. cmap = parseCmap(data, offset, offset + length);
  720. break;
  721. case 'glyf':
  722. glyf = data.subarray(offset, offset + length);
  723. break;
  724. case 'loca':
  725. loca = data.subarray(offset, offset + length);
  726. break;
  727. case 'head':
  728. unitsPerEm = getUshort(data, offset + 18);
  729. indexToLocFormat = getUshort(data, offset + 50);
  730. break;
  731. case 'CFF ':
  732. cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);
  733. break;
  734. }
  735. }
  736. if (glyf) {
  737. var fontMatrix = !unitsPerEm ? font.fontMatrix : [1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0];
  738. return new TrueTypeCompiled(parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix);
  739. }
  740. return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
  741. }
  742. };
  743. }();
  744. exports.FontRendererFactory = FontRendererFactory;