2
0

cff_parser_spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2021 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. var _cff_parser = require("../../core/cff_parser.js");
  24. var _fonts_utils = require("../../core/fonts_utils.js");
  25. var _stream = require("../../core/stream.js");
  26. describe("CFFParser", function () {
  27. function createWithNullProto(obj) {
  28. const result = Object.create(null);
  29. for (const i in obj) {
  30. result[i] = obj[i];
  31. }
  32. return result;
  33. }
  34. const privateDictStub = {
  35. getByName(name) {
  36. return 0;
  37. }
  38. };
  39. let fontData, parser, cff;
  40. beforeAll(function () {
  41. const exampleFont = "0100040100010101134142434445462b" + "54696d65732d526f6d616e000101011f" + "f81b00f81c02f81d03f819041c6f000d" + "fb3cfb6efa7cfa1605e911b8f1120003" + "01010813183030312e30303754696d65" + "7320526f6d616e54696d657300000002" + "010102030e0e7d99f92a99fb7695f773" + "8b06f79a93fc7c8c077d99f85695f75e" + "9908fb6e8cf87393f7108b09a70adf0b" + "f78e14";
  42. const fontArr = [];
  43. for (let i = 0, ii = exampleFont.length; i < ii; i += 2) {
  44. const hex = exampleFont.substring(i, i + 2);
  45. fontArr.push(parseInt(hex, 16));
  46. }
  47. fontData = new _stream.Stream(fontArr);
  48. });
  49. afterAll(function () {
  50. fontData = null;
  51. });
  52. beforeEach(function () {
  53. parser = new _cff_parser.CFFParser(fontData, {}, _fonts_utils.SEAC_ANALYSIS_ENABLED);
  54. cff = parser.parse();
  55. });
  56. afterEach(function () {
  57. parser = cff = null;
  58. });
  59. it("parses header", function () {
  60. const header = cff.header;
  61. expect(header.major).toEqual(1);
  62. expect(header.minor).toEqual(0);
  63. expect(header.hdrSize).toEqual(4);
  64. expect(header.offSize).toEqual(1);
  65. });
  66. it("parses name index", function () {
  67. const names = cff.names;
  68. expect(names.length).toEqual(1);
  69. expect(names[0]).toEqual("ABCDEF+Times-Roman");
  70. });
  71. it("parses string index", function () {
  72. const strings = cff.strings;
  73. expect(strings.count).toEqual(3);
  74. expect(strings.get(0)).toEqual(".notdef");
  75. expect(strings.get(391)).toEqual("001.007");
  76. });
  77. it("parses top dict", function () {
  78. const topDict = cff.topDict;
  79. expect(topDict.getByName("version")).toEqual(391);
  80. expect(topDict.getByName("FullName")).toEqual(392);
  81. expect(topDict.getByName("FamilyName")).toEqual(393);
  82. expect(topDict.getByName("Weight")).toEqual(389);
  83. expect(topDict.getByName("UniqueID")).toEqual(28416);
  84. expect(topDict.getByName("FontBBox")).toEqual([-168, -218, 1000, 898]);
  85. expect(topDict.getByName("CharStrings")).toEqual(94);
  86. expect(topDict.getByName("Private")).toEqual([45, 102]);
  87. });
  88. it("refuses to add topDict key with invalid value (bug 1068432)", function () {
  89. const topDict = cff.topDict;
  90. const defaultValue = topDict.getByName("UnderlinePosition");
  91. topDict.setByKey(3075, [NaN]);
  92. expect(topDict.getByName("UnderlinePosition")).toEqual(defaultValue);
  93. });
  94. it("ignores reserved commands in parseDict, and refuses to add privateDict " + "keys with invalid values (bug 1308536)", function () {
  95. const bytes = new Uint8Array([64, 39, 31, 30, 252, 114, 137, 115, 79, 30, 197, 119, 2, 99, 127, 6]);
  96. parser.bytes = bytes;
  97. const topDict = cff.topDict;
  98. topDict.setByName("Private", [bytes.length, 0]);
  99. const parsePrivateDict = function () {
  100. parser.parsePrivateDict(topDict);
  101. };
  102. expect(parsePrivateDict).not.toThrow();
  103. const privateDict = topDict.privateDict;
  104. expect(privateDict.getByName("BlueValues")).toBeNull();
  105. });
  106. it("parses a CharString having cntrmask", function () {
  107. const bytes = new Uint8Array([0, 1, 1, 0, 38, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 1, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 3, 20, 22, 22, 14]);
  108. parser.bytes = bytes;
  109. const charStringsIndex = parser.parseIndex(0).obj;
  110. const charStrings = parser.parseCharStrings({
  111. charStrings: charStringsIndex,
  112. privateDict: privateDictStub
  113. }).charStrings;
  114. expect(charStrings.count).toEqual(1);
  115. expect(charStrings.get(0).length).toEqual(38);
  116. });
  117. it("parses a CharString endchar with 4 args w/seac enabled", function () {
  118. const cffParser = new _cff_parser.CFFParser(fontData, {}, true);
  119. cffParser.parse();
  120. const bytes = new Uint8Array([0, 1, 1, 0, 237, 247, 22, 247, 72, 204, 247, 86, 14]);
  121. cffParser.bytes = bytes;
  122. const charStringsIndex = cffParser.parseIndex(0).obj;
  123. const result = cffParser.parseCharStrings({
  124. charStrings: charStringsIndex,
  125. privateDict: privateDictStub
  126. });
  127. expect(result.charStrings.count).toEqual(1);
  128. expect(result.charStrings.get(0).length).toEqual(1);
  129. expect(result.seacs.length).toEqual(1);
  130. expect(result.seacs[0].length).toEqual(4);
  131. expect(result.seacs[0][0]).toEqual(130);
  132. expect(result.seacs[0][1]).toEqual(180);
  133. expect(result.seacs[0][2]).toEqual(65);
  134. expect(result.seacs[0][3]).toEqual(194);
  135. });
  136. it("parses a CharString endchar with 4 args w/seac disabled", function () {
  137. const cffParser = new _cff_parser.CFFParser(fontData, {}, false);
  138. cffParser.parse();
  139. const bytes = new Uint8Array([0, 1, 1, 0, 237, 247, 22, 247, 72, 204, 247, 86, 14]);
  140. cffParser.bytes = bytes;
  141. const charStringsIndex = cffParser.parseIndex(0).obj;
  142. const result = cffParser.parseCharStrings({
  143. charStrings: charStringsIndex,
  144. privateDict: privateDictStub
  145. });
  146. expect(result.charStrings.count).toEqual(1);
  147. expect(result.charStrings.get(0).length).toEqual(9);
  148. expect(result.seacs.length).toEqual(0);
  149. });
  150. it("parses a CharString endchar no args", function () {
  151. const bytes = new Uint8Array([0, 1, 1, 0, 14]);
  152. parser.bytes = bytes;
  153. const charStringsIndex = parser.parseIndex(0).obj;
  154. const result = parser.parseCharStrings({
  155. charStrings: charStringsIndex,
  156. privateDict: privateDictStub
  157. });
  158. expect(result.charStrings.count).toEqual(1);
  159. expect(result.charStrings.get(0)[0]).toEqual(14);
  160. expect(result.seacs.length).toEqual(0);
  161. });
  162. it("parses predefined charsets", function () {
  163. const charset = parser.parseCharsets(0, 0, null, true);
  164. expect(charset.predefined).toEqual(true);
  165. });
  166. it("parses charset format 0", function () {
  167. const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x02]);
  168. parser.bytes = bytes;
  169. let charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
  170. expect(charset.charset[1]).toEqual("exclam");
  171. charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), true);
  172. expect(charset.charset[1]).toEqual(2);
  173. });
  174. it("parses charset format 1", function () {
  175. const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x01]);
  176. parser.bytes = bytes;
  177. let charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
  178. expect(charset.charset).toEqual([".notdef", "quoteright", "parenleft"]);
  179. charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), true);
  180. expect(charset.charset).toEqual([0, 8, 9]);
  181. });
  182. it("parses charset format 2", function () {
  183. const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01]);
  184. parser.bytes = bytes;
  185. let charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
  186. expect(charset.charset).toEqual([".notdef", "quoteright", "parenleft"]);
  187. charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), true);
  188. expect(charset.charset).toEqual([0, 8, 9]);
  189. });
  190. it("parses encoding format 0", function () {
  191. const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x08]);
  192. parser.bytes = bytes;
  193. const encoding = parser.parseEncoding(2, {}, new _cff_parser.CFFStrings(), null);
  194. expect(encoding.encoding).toEqual(createWithNullProto({
  195. 0x8: 1
  196. }));
  197. });
  198. it("parses encoding format 1", function () {
  199. const bytes = new Uint8Array([0x00, 0x00, 0x01, 0x01, 0x07, 0x01]);
  200. parser.bytes = bytes;
  201. const encoding = parser.parseEncoding(2, {}, new _cff_parser.CFFStrings(), null);
  202. expect(encoding.encoding).toEqual(createWithNullProto({
  203. 0x7: 0x01,
  204. 0x08: 0x02
  205. }));
  206. });
  207. it("parses fdselect format 0", function () {
  208. const bytes = new Uint8Array([0x00, 0x00, 0x01]);
  209. parser.bytes = bytes.slice();
  210. const fdSelect = parser.parseFDSelect(0, 2);
  211. expect(fdSelect.fdSelect).toEqual([0, 1]);
  212. expect(fdSelect.format).toEqual(0);
  213. });
  214. it("parses fdselect format 3", function () {
  215. const bytes = new Uint8Array([0x03, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x04]);
  216. parser.bytes = bytes.slice();
  217. const fdSelect = parser.parseFDSelect(0, 4);
  218. expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
  219. expect(fdSelect.format).toEqual(3);
  220. });
  221. it("parses invalid fdselect format 3 (bug 1146106)", function () {
  222. const bytes = new Uint8Array([0x03, 0x00, 0x02, 0x00, 0x01, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x04]);
  223. parser.bytes = bytes.slice();
  224. const fdSelect = parser.parseFDSelect(0, 4);
  225. expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
  226. expect(fdSelect.format).toEqual(3);
  227. });
  228. });
  229. describe("CFFCompiler", function () {
  230. function testParser(bytes) {
  231. bytes = new Uint8Array(bytes);
  232. return new _cff_parser.CFFParser({
  233. getBytes: () => {
  234. return bytes;
  235. }
  236. }, {}, _fonts_utils.SEAC_ANALYSIS_ENABLED);
  237. }
  238. it("encodes integers", function () {
  239. const c = new _cff_parser.CFFCompiler();
  240. expect(c.encodeInteger(0)).toEqual([0x8b]);
  241. expect(c.encodeInteger(100)).toEqual([0xef]);
  242. expect(c.encodeInteger(-100)).toEqual([0x27]);
  243. expect(c.encodeInteger(1000)).toEqual([0xfa, 0x7c]);
  244. expect(c.encodeInteger(-1000)).toEqual([0xfe, 0x7c]);
  245. expect(c.encodeInteger(10000)).toEqual([0x1c, 0x27, 0x10]);
  246. expect(c.encodeInteger(-10000)).toEqual([0x1c, 0xd8, 0xf0]);
  247. expect(c.encodeInteger(100000)).toEqual([0x1d, 0x00, 0x01, 0x86, 0xa0]);
  248. expect(c.encodeInteger(-100000)).toEqual([0x1d, 0xff, 0xfe, 0x79, 0x60]);
  249. });
  250. it("encodes floats", function () {
  251. const c = new _cff_parser.CFFCompiler();
  252. expect(c.encodeFloat(-2.25)).toEqual([0x1e, 0xe2, 0xa2, 0x5f]);
  253. expect(c.encodeFloat(5e-11)).toEqual([0x1e, 0x5c, 0x11, 0xff]);
  254. });
  255. it("sanitizes name index", function () {
  256. const c = new _cff_parser.CFFCompiler();
  257. let nameIndexCompiled = c.compileNameIndex(["[a"]);
  258. let parser = testParser(nameIndexCompiled);
  259. let nameIndex = parser.parseIndex(0);
  260. let names = parser.parseNameIndex(nameIndex.obj);
  261. expect(names).toEqual(["_a"]);
  262. let longName = "";
  263. for (let i = 0; i < 129; i++) {
  264. longName += "_";
  265. }
  266. nameIndexCompiled = c.compileNameIndex([longName]);
  267. parser = testParser(nameIndexCompiled);
  268. nameIndex = parser.parseIndex(0);
  269. names = parser.parseNameIndex(nameIndex.obj);
  270. expect(names[0].length).toEqual(127);
  271. });
  272. it("compiles fdselect format 0", function () {
  273. const fdSelect = new _cff_parser.CFFFDSelect(0, [3, 2, 1]);
  274. const c = new _cff_parser.CFFCompiler();
  275. const out = c.compileFDSelect(fdSelect);
  276. expect(out).toEqual([0, 3, 2, 1]);
  277. });
  278. it("compiles fdselect format 3", function () {
  279. const fdSelect = new _cff_parser.CFFFDSelect(3, [0, 0, 1, 1]);
  280. const c = new _cff_parser.CFFCompiler();
  281. const out = c.compileFDSelect(fdSelect);
  282. expect(out).toEqual([3, 0, 2, 0, 0, 0, 0, 2, 1, 0, 4]);
  283. });
  284. it("compiles fdselect format 3, single range", function () {
  285. const fdSelect = new _cff_parser.CFFFDSelect(3, [0, 0]);
  286. const c = new _cff_parser.CFFCompiler();
  287. const out = c.compileFDSelect(fdSelect);
  288. expect(out).toEqual([3, 0, 1, 0, 0, 0, 0, 2]);
  289. });
  290. it("compiles charset of CID font", function () {
  291. const charset = new _cff_parser.CFFCharset();
  292. const c = new _cff_parser.CFFCompiler();
  293. const numGlyphs = 7;
  294. const out = c.compileCharset(charset, numGlyphs, new _cff_parser.CFFStrings(), true);
  295. expect(out).toEqual([2, 0, 0, 0, numGlyphs - 1]);
  296. });
  297. it("compiles charset of non CID font", function () {
  298. const charset = new _cff_parser.CFFCharset(false, 0, ["space", "exclam"]);
  299. const c = new _cff_parser.CFFCompiler();
  300. const numGlyphs = 3;
  301. const out = c.compileCharset(charset, numGlyphs, new _cff_parser.CFFStrings(), false);
  302. expect(out).toEqual([0, 0, 1, 0, 2]);
  303. });
  304. });