From 775763a0914e93ae0075004aaf62314c7091e99c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 8 Jul 2018 21:36:20 +0200 Subject: [PATCH 1/6] Ensure that `CompiledFont.compileGlyph` always returns an Array (PR 6141 follow-up) PR 6141 changed `CompiledFont.compileGlyph` to, in the general case, return an Array. However, that PR apparenly forgot to update the no-glyph, empty-glyph, and endchar-glyph code-path and a String was still being (incorrectly) returned. Given the way that `FontFaceObject.getPathGenerator` (on the API side) is implemented, this shouldn't have caused any bugs despite the Worker possible returning unexpected data. --- src/core/font_renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 366037787..8e078f672 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -616,7 +616,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { parse(code); } - var noop = ''; + const NOOP = []; function CompiledFont(fontMatrix) { this.compiledGlyphs = Object.create(null); @@ -639,7 +639,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { compileGlyph(code, glyphId) { if (!code || code.length === 0 || code[0] === 14) { - return noop; + return NOOP; } let fontMatrix = this.fontMatrix; From ba1af467092b801b032e9fb5507959c61d6e7b74 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 8 Jul 2018 22:16:05 +0200 Subject: [PATCH 2/6] Convert `CompiledFont`, `TrueTypeCompiled`, and `Type2Compiled` to ES6 classes Also changes `var` to `let`/`const` in code already touched in the patch. --- src/core/font_renderer.js | 99 ++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 8e078f672..a76d50bd3 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -14,7 +14,7 @@ */ import { - bytesToString, FONT_IDENTITY_MATRIX, FormatError, unreachable, Util, warn + bytesToString, FONT_IDENTITY_MATRIX, FormatError, unreachable, warn } from '../shared/util'; import { CFFParser } from './cff_parser'; import { getGlyphsUnicode } from './glyphlist'; @@ -618,15 +618,20 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { const NOOP = []; - function CompiledFont(fontMatrix) { - this.compiledGlyphs = Object.create(null); - this.compiledCharCodeToGlyphId = Object.create(null); - this.fontMatrix = fontMatrix; - } - CompiledFont.prototype = { + class CompiledFont { + constructor(fontMatrix) { + if (this.constructor === CompiledFont) { + unreachable('Cannot initialize CompiledFont.'); + } + this.fontMatrix = fontMatrix; + + this.compiledGlyphs = Object.create(null); + this.compiledCharCodeToGlyphId = Object.create(null); + } + getPathJs(unicode) { - var cmap = lookupCmap(this.cmap, unicode); - var fn = this.compiledGlyphs[cmap.glyphId]; + const cmap = lookupCmap(this.cmap, unicode); + let fn = this.compiledGlyphs[cmap.glyphId]; if (!fn) { fn = this.compileGlyph(this.glyphs[cmap.glyphId], cmap.glyphId); this.compiledGlyphs[cmap.glyphId] = fn; @@ -635,7 +640,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { this.compiledCharCodeToGlyphId[cmap.charCode] = cmap.glyphId; } return fn; - }, + } compileGlyph(code, glyphId) { if (!code || code.length === 0 || code[0] === 14) { @@ -655,7 +660,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } } - var cmds = []; + const cmds = []; cmds.push({ cmd: 'save', }); cmds.push({ cmd: 'transform', args: fontMatrix.slice(), }); cmds.push({ cmd: 'scale', args: ['size', '-size'], }); @@ -665,58 +670,56 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { cmds.push({ cmd: 'restore', }); return cmds; - }, + } compileGlyphImpl() { unreachable('Children classes should implement this.'); - }, + } hasBuiltPath(unicode) { - var cmap = lookupCmap(this.cmap, unicode); + const cmap = lookupCmap(this.cmap, unicode); return (this.compiledGlyphs[cmap.glyphId] !== undefined && this.compiledCharCodeToGlyphId[cmap.charCode] !== undefined); - }, - }; - - function TrueTypeCompiled(glyphs, cmap, fontMatrix) { - fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0]; - CompiledFont.call(this, fontMatrix); - - this.glyphs = glyphs; - this.cmap = cmap; + } } - Util.inherit(TrueTypeCompiled, CompiledFont, { + class TrueTypeCompiled extends CompiledFont { + constructor(glyphs, cmap, fontMatrix) { + super(fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0]); + + this.glyphs = glyphs; + this.cmap = cmap; + } + compileGlyphImpl(code, cmds) { compileGlyf(code, cmds, this); - }, - }); - - function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) { - fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0]; - CompiledFont.call(this, fontMatrix); - - this.glyphs = cffInfo.glyphs; - this.gsubrs = cffInfo.gsubrs || []; - this.subrs = cffInfo.subrs || []; - this.cmap = cmap; - this.glyphNameMap = glyphNameMap || getGlyphsUnicode(); - - this.gsubrsBias = (this.gsubrs.length < 1240 ? - 107 : (this.gsubrs.length < 33900 ? 1131 : 32768)); - this.subrsBias = (this.subrs.length < 1240 ? - 107 : (this.subrs.length < 33900 ? 1131 : 32768)); - - this.isCFFCIDFont = cffInfo.isCFFCIDFont; - this.fdSelect = cffInfo.fdSelect; - this.fdArray = cffInfo.fdArray; + } } - Util.inherit(Type2Compiled, CompiledFont, { + class Type2Compiled extends CompiledFont { + constructor(cffInfo, cmap, fontMatrix, glyphNameMap) { + super(fontMatrix || [0.001, 0, 0, 0.001, 0, 0]); + + this.glyphs = cffInfo.glyphs; + this.gsubrs = cffInfo.gsubrs || []; + this.subrs = cffInfo.subrs || []; + this.cmap = cmap; + this.glyphNameMap = glyphNameMap || getGlyphsUnicode(); + + this.gsubrsBias = (this.gsubrs.length < 1240 ? + 107 : (this.gsubrs.length < 33900 ? 1131 : 32768)); + this.subrsBias = (this.subrs.length < 1240 ? + 107 : (this.subrs.length < 33900 ? 1131 : 32768)); + + this.isCFFCIDFont = cffInfo.isCFFCIDFont; + this.fdSelect = cffInfo.fdSelect; + this.fdArray = cffInfo.fdArray; + } + compileGlyphImpl(code, cmds, glyphId) { compileCharString(code, cmds, this, glyphId); - }, - }); + } + } return { create: function FontRendererFactory_create(font, seacAnalysisEnabled) { From b773b356af2d26cb5bc5a0f021110be81f065974 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 8 Jul 2018 22:31:31 +0200 Subject: [PATCH 3/6] Convert `NameOrNumberTree`, `NameTree`, and `NumberTree` to ES6 classes Also changes `var` to `let`/`const` in code already touched in the patch. --- src/core/obj.js | 231 ++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 123 deletions(-) diff --git a/src/core/obj.js b/src/core/obj.js index 89dfa4df3..22b6a2a77 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -1573,145 +1573,130 @@ var XRef = (function XRefClosure() { * see the specification (7.9.6 and 7.9.7) for additional details. * TODO: implement all the Dict functions and make this more efficient. */ -var NameOrNumberTree = (function NameOrNumberTreeClosure() { - function NameOrNumberTree(root, xref) { - unreachable('Cannot initialize NameOrNumberTree.'); +class NameOrNumberTree { + constructor(root, xref, type) { + if (this.constructor === NameOrNumberTree) { + unreachable('Cannot initialize NameOrNumberTree.'); + } + this.root = root; + this.xref = xref; + this._type = type; } - NameOrNumberTree.prototype = { - getAll: function NameOrNumberTree_getAll() { - var dict = Object.create(null); - if (!this.root) { - return dict; - } - var xref = this.xref; - // Reading Name/Number tree. - var processed = new RefSet(); - processed.put(this.root); - var queue = [this.root]; - while (queue.length > 0) { - var i, n; - var obj = xref.fetchIfRef(queue.shift()); - if (!isDict(obj)) { - continue; - } - if (obj.has('Kids')) { - var kids = obj.get('Kids'); - for (i = 0, n = kids.length; i < n; i++) { - var kid = kids[i]; - if (processed.has(kid)) { - throw new FormatError(`Duplicate entry in "${this._type}" tree.`); - } - queue.push(kid); - processed.put(kid); - } - continue; - } - var entries = obj.get(this._type); - if (Array.isArray(entries)) { - for (i = 0, n = entries.length; i < n; i += 2) { - dict[xref.fetchIfRef(entries[i])] = xref.fetchIfRef(entries[i + 1]); - } - } - } + getAll() { + const dict = Object.create(null); + if (!this.root) { return dict; - }, + } + const xref = this.xref; + // Reading Name/Number tree. + const processed = new RefSet(); + processed.put(this.root); + const queue = [this.root]; + while (queue.length > 0) { + const obj = xref.fetchIfRef(queue.shift()); + if (!isDict(obj)) { + continue; + } + if (obj.has('Kids')) { + const kids = obj.get('Kids'); + for (let i = 0, ii = kids.length; i < ii; i++) { + const kid = kids[i]; + if (processed.has(kid)) { + throw new FormatError(`Duplicate entry in "${this._type}" tree.`); + } + queue.push(kid); + processed.put(kid); + } + continue; + } + const entries = obj.get(this._type); + if (Array.isArray(entries)) { + for (let i = 0, ii = entries.length; i < ii; i += 2) { + dict[xref.fetchIfRef(entries[i])] = xref.fetchIfRef(entries[i + 1]); + } + } + } + return dict; + } - get: function NameOrNumberTree_get(key) { - if (!this.root) { + get(key) { + if (!this.root) { + return null; + } + const xref = this.xref; + let kidsOrEntries = xref.fetchIfRef(this.root); + let loopCount = 0; + const MAX_LEVELS = 10; + + // Perform a binary search to quickly find the entry that + // contains the key we are looking for. + while (kidsOrEntries.has('Kids')) { + if (++loopCount > MAX_LEVELS) { + warn('Search depth limit reached for "' + this._type + '" tree.'); return null; } - var xref = this.xref; - var kidsOrEntries = xref.fetchIfRef(this.root); - var loopCount = 0; - var MAX_LEVELS = 10; - var l, r, m; - - // Perform a binary search to quickly find the entry that - // contains the key we are looking for. - while (kidsOrEntries.has('Kids')) { - if (++loopCount > MAX_LEVELS) { - warn('Search depth limit reached for "' + this._type + '" tree.'); - return null; - } - - var kids = kidsOrEntries.get('Kids'); - if (!Array.isArray(kids)) { - return null; - } - - l = 0; - r = kids.length - 1; - while (l <= r) { - m = (l + r) >> 1; - var kid = xref.fetchIfRef(kids[m]); - var limits = kid.get('Limits'); - - if (key < xref.fetchIfRef(limits[0])) { - r = m - 1; - } else if (key > xref.fetchIfRef(limits[1])) { - l = m + 1; - } else { - kidsOrEntries = xref.fetchIfRef(kids[m]); - break; - } - } - if (l > r) { - return null; - } + const kids = kidsOrEntries.get('Kids'); + if (!Array.isArray(kids)) { + return null; } - // If we get here, then we have found the right entry. Now go through the - // entries in the dictionary until we find the key we're looking for. - var entries = kidsOrEntries.get(this._type); - if (Array.isArray(entries)) { - // Perform a binary search to reduce the lookup time. - l = 0; - r = entries.length - 2; - while (l <= r) { - // Check only even indices (0, 2, 4, ...) because the - // odd indices contain the actual data. - m = (l + r) & ~1; - var currentKey = xref.fetchIfRef(entries[m]); - if (key < currentKey) { - r = m - 2; - } else if (key > currentKey) { - l = m + 2; - } else { - return xref.fetchIfRef(entries[m + 1]); - } + let l = 0, r = kids.length - 1; + while (l <= r) { + const m = (l + r) >> 1; + const kid = xref.fetchIfRef(kids[m]); + const limits = kid.get('Limits'); + + if (key < xref.fetchIfRef(limits[0])) { + r = m - 1; + } else if (key > xref.fetchIfRef(limits[1])) { + l = m + 1; + } else { + kidsOrEntries = xref.fetchIfRef(kids[m]); + break; } } - return null; - }, - }; - return NameOrNumberTree; -})(); + if (l > r) { + return null; + } + } -var NameTree = (function NameTreeClosure() { - function NameTree(root, xref) { - this.root = root; - this.xref = xref; - this._type = 'Names'; + // If we get here, then we have found the right entry. Now go through the + // entries in the dictionary until we find the key we're looking for. + const entries = kidsOrEntries.get(this._type); + if (Array.isArray(entries)) { + // Perform a binary search to reduce the lookup time. + let l = 0, r = entries.length - 2; + while (l <= r) { + // Check only even indices (0, 2, 4, ...) because the + // odd indices contain the actual data. + const m = (l + r) & ~1; + const currentKey = xref.fetchIfRef(entries[m]); + if (key < currentKey) { + r = m - 2; + } else if (key > currentKey) { + l = m + 2; + } else { + return xref.fetchIfRef(entries[m + 1]); + } + } + } + return null; } +} - Util.inherit(NameTree, NameOrNumberTree, {}); - - return NameTree; -})(); - -var NumberTree = (function NumberTreeClosure() { - function NumberTree(root, xref) { - this.root = root; - this.xref = xref; - this._type = 'Nums'; +class NameTree extends NameOrNumberTree { + constructor(root, xref) { + super(root, xref, 'Names'); } +} - Util.inherit(NumberTree, NameOrNumberTree, {}); - - return NumberTree; -})(); +class NumberTree extends NameOrNumberTree { + constructor(root, xref) { + super(root, xref, 'Nums'); + } +} /** * "A PDF file can refer to the contents of another file by using a File From bf6d45f85a7783da6af969e14b8f117fc1272625 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 8 Jul 2018 22:41:52 +0200 Subject: [PATCH 4/6] Convert `CMap` and `IdentityCMap` to ES6 classes Also changes `var` to `let`/`const` in code already touched in the patch. --- src/core/cmap.js | 325 +++++++++++++++++++++++------------------------ 1 file changed, 157 insertions(+), 168 deletions(-) diff --git a/src/core/cmap.js b/src/core/cmap.js index eccaaa615..30b7012de 100644 --- a/src/core/cmap.js +++ b/src/core/cmap.js @@ -14,8 +14,8 @@ */ import { - CMapCompressionType, FormatError, isString, MissingDataException, - unreachable, Util, warn + CMapCompressionType, FormatError, isString, MissingDataException, unreachable, + warn } from '../shared/util'; import { isCmd, isEOF, isName, isStream } from './primitives'; import { Lexer } from './parser'; @@ -194,8 +194,8 @@ var BUILT_IN_CMAPS = [ 'WP-Symbol']; // CMap, not to be confused with TrueType's cmap. -var CMap = (function CMapClosure() { - function CMap(builtInCMap) { +class CMap { + constructor(builtInCMap = false) { // Codespace ranges are stored as follows: // [[1BytePairs], [2BytePairs], [3BytePairs], [4BytePairs]] // where nBytePairs are ranges e.g. [low1, high1, low2, high2, ...] @@ -211,204 +211,193 @@ var CMap = (function CMapClosure() { this.useCMap = null; this.builtInCMap = builtInCMap; } - CMap.prototype = { - addCodespaceRange(n, low, high) { - this.codespaceRanges[n - 1].push(low, high); - this.numCodespaceRanges++; - }, - mapCidRange(low, high, dstLow) { - while (low <= high) { - this._map[low++] = dstLow++; - } - }, + addCodespaceRange(n, low, high) { + this.codespaceRanges[n - 1].push(low, high); + this.numCodespaceRanges++; + } - mapBfRange(low, high, dstLow) { - var lastByte = dstLow.length - 1; - while (low <= high) { - this._map[low++] = dstLow; - // Only the last byte has to be incremented. - dstLow = dstLow.substr(0, lastByte) + - String.fromCharCode(dstLow.charCodeAt(lastByte) + 1); - } - }, + mapCidRange(low, high, dstLow) { + while (low <= high) { + this._map[low++] = dstLow++; + } + } - mapBfRangeToArray(low, high, array) { - var i = 0, ii = array.length; - while (low <= high && i < ii) { - this._map[low] = array[i++]; - ++low; - } - }, + mapBfRange(low, high, dstLow) { + var lastByte = dstLow.length - 1; + while (low <= high) { + this._map[low++] = dstLow; + // Only the last byte has to be incremented. + dstLow = dstLow.substr(0, lastByte) + + String.fromCharCode(dstLow.charCodeAt(lastByte) + 1); + } + } - // This is used for both bf and cid chars. - mapOne(src, dst) { - this._map[src] = dst; - }, + mapBfRangeToArray(low, high, array) { + let i = 0, ii = array.length; + while (low <= high && i < ii) { + this._map[low] = array[i++]; + ++low; + } + } - lookup(code) { - return this._map[code]; - }, + // This is used for both bf and cid chars. + mapOne(src, dst) { + this._map[src] = dst; + } - contains(code) { - return this._map[code] !== undefined; - }, + lookup(code) { + return this._map[code]; + } - forEach(callback) { - // Most maps have fewer than 65536 entries, and for those we use normal - // array iteration. But really sparse tables are possible -- e.g. with - // indices in the *billions*. For such tables we use for..in, which isn't - // ideal because it stringifies the indices for all present elements, but - // it does avoid iterating over every undefined entry. - let map = this._map; - let length = map.length; - if (length <= 0x10000) { - for (let i = 0; i < length; i++) { - if (map[i] !== undefined) { - callback(i, map[i]); - } - } - } else { - for (let i in map) { + contains(code) { + return this._map[code] !== undefined; + } + + forEach(callback) { + // Most maps have fewer than 65536 entries, and for those we use normal + // array iteration. But really sparse tables are possible -- e.g. with + // indices in the *billions*. For such tables we use for..in, which isn't + // ideal because it stringifies the indices for all present elements, but + // it does avoid iterating over every undefined entry. + let map = this._map; + let length = map.length; + if (length <= 0x10000) { + for (let i = 0; i < length; i++) { + if (map[i] !== undefined) { callback(i, map[i]); } } - }, - - charCodeOf(value) { - // `Array.prototype.indexOf` is *extremely* inefficient for arrays which - // are both very sparse and very large (see issue8372.pdf). - let map = this._map; - if (map.length <= 0x10000) { - return map.indexOf(value); + } else { + for (let i in map) { + callback(i, map[i]); } - for (let charCode in map) { - if (map[charCode] === value) { - return (charCode | 0); + } + } + + charCodeOf(value) { + // `Array.prototype.indexOf` is *extremely* inefficient for arrays which + // are both very sparse and very large (see issue8372.pdf). + const map = this._map; + if (map.length <= 0x10000) { + return map.indexOf(value); + } + for (let charCode in map) { + if (map[charCode] === value) { + return (charCode | 0); + } + } + return -1; + } + + getMap() { + return this._map; + } + + readCharCode(str, offset, out) { + let c = 0; + const codespaceRanges = this.codespaceRanges; + // 9.7.6.2 CMap Mapping + // The code length is at most 4. + for (let n = 0, nn = codespaceRanges.length; n < nn; n++) { + c = ((c << 8) | str.charCodeAt(offset + n)) >>> 0; + // Check each codespace range to see if it falls within. + const codespaceRange = codespaceRanges[n]; + for (let k = 0, kk = codespaceRange.length; k < kk;) { + const low = codespaceRange[k++]; + const high = codespaceRange[k++]; + if (c >= low && c <= high) { + out.charcode = c; + out.length = n + 1; + return; } } - return -1; - }, + } + out.charcode = 0; + out.length = 1; + } - getMap() { - return this._map; - }, + get length() { + return this._map.length; + } - readCharCode(str, offset, out) { - var c = 0; - var codespaceRanges = this.codespaceRanges; - var codespaceRangesLen = this.codespaceRanges.length; - // 9.7.6.2 CMap Mapping - // The code length is at most 4. - for (var n = 0; n < codespaceRangesLen; n++) { - c = ((c << 8) | str.charCodeAt(offset + n)) >>> 0; - // Check each codespace range to see if it falls within. - var codespaceRange = codespaceRanges[n]; - for (var k = 0, kk = codespaceRange.length; k < kk;) { - var low = codespaceRange[k++]; - var high = codespaceRange[k++]; - if (c >= low && c <= high) { - out.charcode = c; - out.length = n + 1; - return; - } - } - } - out.charcode = 0; - out.length = 1; - }, - - get length() { - return this._map.length; - }, - - get isIdentityCMap() { - if (!(this.name === 'Identity-H' || this.name === 'Identity-V')) { + get isIdentityCMap() { + if (!(this.name === 'Identity-H' || this.name === 'Identity-V')) { + return false; + } + if (this._map.length !== 0x10000) { + return false; + } + for (let i = 0; i < 0x10000; i++) { + if (this._map[i] !== i) { return false; } - if (this._map.length !== 0x10000) { - return false; - } - for (var i = 0; i < 0x10000; i++) { - if (this._map[i] !== i) { - return false; - } - } - return true; - }, - }; - return CMap; -})(); + } + return true; + } +} // A special case of CMap, where the _map array implicitly has a length of // 65536 and each element is equal to its index. -var IdentityCMap = (function IdentityCMapClosure() { - function IdentityCMap(vertical, n) { - CMap.call(this); +class IdentityCMap extends CMap { + constructor(vertical, n) { + super(); + this.vertical = vertical; this.addCodespaceRange(n, 0, 0xffff); } - Util.inherit(IdentityCMap, CMap, {}); - IdentityCMap.prototype = { - addCodespaceRange: CMap.prototype.addCodespaceRange, + mapCidRange(low, high, dstLow) { + unreachable('should not call mapCidRange'); + } - mapCidRange(low, high, dstLow) { - unreachable('should not call mapCidRange'); - }, + mapBfRange(low, high, dstLow) { + unreachable('should not call mapBfRange'); + } - mapBfRange(low, high, dstLow) { - unreachable('should not call mapBfRange'); - }, + mapBfRangeToArray(low, high, array) { + unreachable('should not call mapBfRangeToArray'); + } - mapBfRangeToArray(low, high, array) { - unreachable('should not call mapBfRangeToArray'); - }, + mapOne(src, dst) { + unreachable('should not call mapCidOne'); + } - mapOne(src, dst) { - unreachable('should not call mapCidOne'); - }, + lookup(code) { + return (Number.isInteger(code) && code <= 0xffff) ? code : undefined; + } - lookup(code) { - return (Number.isInteger(code) && code <= 0xffff) ? code : undefined; - }, + contains(code) { + return Number.isInteger(code) && code <= 0xffff; + } - contains(code) { - return Number.isInteger(code) && code <= 0xffff; - }, + forEach(callback) { + for (let i = 0; i <= 0xffff; i++) { + callback(i, i); + } + } - forEach(callback) { - for (var i = 0; i <= 0xffff; i++) { - callback(i, i); - } - }, + charCodeOf(value) { + return (Number.isInteger(value) && value <= 0xffff) ? value : -1; + } - charCodeOf(value) { - return (Number.isInteger(value) && value <= 0xffff) ? value : -1; - }, + getMap() { + // Sometimes identity maps must be instantiated, but it's rare. + const map = new Array(0x10000); + for (let i = 0; i <= 0xffff; i++) { + map[i] = i; + } + return map; + } - getMap() { - // Sometimes identity maps must be instantiated, but it's rare. - var map = new Array(0x10000); - for (var i = 0; i <= 0xffff; i++) { - map[i] = i; - } - return map; - }, + get length() { + return 0x10000; + } - readCharCode: CMap.prototype.readCharCode, - - get length() { - return 0x10000; - }, - - get isIdentityCMap() { - unreachable('should not access .isIdentityCMap'); - }, - }; - - return IdentityCMap; -})(); + get isIdentityCMap() { + unreachable('should not access .isIdentityCMap'); + } +} var BinaryCMapReader = (function BinaryCMapReaderClosure() { function hexToInt(a, size) { From c1c49badff7d689c364470fc7a525a9caab1533c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Mon, 9 Jul 2018 15:15:42 +0200 Subject: [PATCH 5/6] Remove the, now unused, `Util.inherit` helper function --- src/shared/util.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/shared/util.js b/src/shared/util.js index d2c5d1981..7f6fc931d 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -875,14 +875,6 @@ var Util = (function UtilClosure() { return (lowerCase ? romanStr.toLowerCase() : romanStr); }; - Util.inherit = function Util_inherit(sub, base, prototype) { - sub.prototype = Object.create(base.prototype); - sub.prototype.constructor = sub; - for (var prop in prototype) { - sub.prototype[prop] = prototype[prop]; - } - }; - return Util; })(); From 8e76d26e5b3bb62bebbebddf675e133c7c9f9444 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 10 Jul 2018 10:45:25 +0200 Subject: [PATCH 6/6] Move the `toRoman` helper function out of the `Util` scope Compared to all the other (static) methods in `Util`, the `toRoman` one looks slightly out of place. Even more so considering that `Util` is being exposed through `pdfjsLib`, where access to a Roman numerals conversion method doesn't make much sense. --- src/core/obj.js | 4 +-- src/shared/util.js | 74 ++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/core/obj.js b/src/core/obj.js index 22b6a2a77..823a8d138 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -16,7 +16,7 @@ import { bytesToString, createPromiseCapability, createValidAbsoluteUrl, FormatError, info, InvalidPDFException, isBool, isString, MissingDataException, shadow, - stringToPDFString, stringToUTF8String, unreachable, Util, warn, + stringToPDFString, stringToUTF8String, toRomanNumerals, unreachable, warn, XRefParseException } from '../shared/util'; import { @@ -310,7 +310,7 @@ var Catalog = (function CatalogClosure() { break; case 'R': case 'r': - currentLabel = Util.toRoman(currentIndex, style === 'r'); + currentLabel = toRomanNumerals(currentIndex, style === 'r'); break; case 'A': case 'a': diff --git a/src/shared/util.js b/src/shared/util.js index 7f6fc931d..642bacf9e 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -839,45 +839,46 @@ var Util = (function UtilClosure() { return result; }; - var ROMAN_NUMBER_MAP = [ - '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', - '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', - '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' - ]; - /** - * Converts positive integers to (upper case) Roman numerals. - * @param {integer} number - The number that should be converted. - * @param {boolean} lowerCase - Indicates if the result should be converted - * to lower case letters. The default is false. - * @return {string} The resulting Roman number. - */ - Util.toRoman = function Util_toRoman(number, lowerCase) { - assert(Number.isInteger(number) && number > 0, - 'The number should be a positive integer.'); - var pos, romanBuf = []; - // Thousands - while (number >= 1000) { - number -= 1000; - romanBuf.push('M'); - } - // Hundreds - pos = (number / 100) | 0; - number %= 100; - romanBuf.push(ROMAN_NUMBER_MAP[pos]); - // Tens - pos = (number / 10) | 0; - number %= 10; - romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]); - // Ones - romanBuf.push(ROMAN_NUMBER_MAP[20 + number]); - - var romanStr = romanBuf.join(''); - return (lowerCase ? romanStr.toLowerCase() : romanStr); - }; - return Util; })(); +const ROMAN_NUMBER_MAP = [ + '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', + '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', + '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' +]; + +/** + * Converts positive integers to (upper case) Roman numerals. + * @param {integer} number - The number that should be converted. + * @param {boolean} lowerCase - Indicates if the result should be converted + * to lower case letters. The default value is `false`. + * @return {string} The resulting Roman number. + */ +function toRomanNumerals(number, lowerCase = false) { + assert(Number.isInteger(number) && number > 0, + 'The number should be a positive integer.'); + let pos, romanBuf = []; + // Thousands + while (number >= 1000) { + number -= 1000; + romanBuf.push('M'); + } + // Hundreds + pos = (number / 100) | 0; + number %= 100; + romanBuf.push(ROMAN_NUMBER_MAP[pos]); + // Tens + pos = (number / 10) | 0; + number %= 10; + romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]); + // Ones + romanBuf.push(ROMAN_NUMBER_MAP[20 + number]); + + const romanStr = romanBuf.join(''); + return (lowerCase ? romanStr.toLowerCase() : romanStr); +} + var PDFStringTranslateTable = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, @@ -1025,6 +1026,7 @@ export { UnexpectedResponseException, UnknownErrorException, Util, + toRomanNumerals, XRefParseException, FormatError, arrayByteLength,