Compare commits

..

No commits in common. "3cd1b10433575e718b314fa7b7866b1ac9aa85ad" and "7081a1f112756c09b79134b52a5db052f201651e" have entirely different histories.

22 changed files with 238 additions and 309 deletions

View File

@ -14,7 +14,15 @@
*/
import {
_isValidExplicitDest,
collectActions,
isNumberArray,
MissingDataException,
PDF_VERSION_REGEXP,
recoverJsURL,
toRomanNumerals,
XRefEntryException,
} from "./core_utils.js";
import {
createValidAbsoluteUrl,
DocumentActionEventType,
FormatError,
@ -26,15 +34,6 @@ import {
stringToUTF8String,
warn,
} from "../shared/util.js";
import {
collectActions,
isNumberArray,
MissingDataException,
PDF_VERSION_REGEXP,
recoverJsURL,
toRomanNumerals,
XRefEntryException,
} from "./core_utils.js";
import {
Dict,
isDict,
@ -54,13 +53,52 @@ import { FileSpec } from "./file_spec.js";
import { MetadataParser } from "./metadata_parser.js";
import { StructTreeRoot } from "./struct_tree.js";
const isRef = v => v instanceof Ref;
const isValidExplicitDest = _isValidExplicitDest.bind(
null,
/* validRef = */ isRef,
/* validName = */ isName
);
function isValidExplicitDest(dest) {
if (!Array.isArray(dest) || dest.length < 2) {
return false;
}
const [page, zoom, ...args] = dest;
if (!(page instanceof Ref) && !Number.isInteger(page)) {
return false;
}
if (!(zoom instanceof Name)) {
return false;
}
const argsLen = args.length;
let allowNull = true;
switch (zoom.name) {
case "XYZ":
if (argsLen < 2 || argsLen > 3) {
return false;
}
break;
case "Fit":
case "FitB":
return argsLen === 0;
case "FitH":
case "FitBH":
case "FitV":
case "FitBV":
if (argsLen > 1) {
return false;
}
break;
case "FitR":
if (argsLen !== 4) {
return false;
}
allowNull = false;
break;
default:
return false;
}
for (const arg of args) {
if (!(typeof arg === "number" || (allowNull && arg === null))) {
return false;
}
}
return true;
}
function fetchDest(dest) {
if (dest instanceof Dict) {

View File

@ -414,7 +414,9 @@ class ChunkedStreamManager {
}
}
chunksToRequest.sort((a, b) => a - b);
chunksToRequest.sort(function (a, b) {
return a - b;
});
return this._requestChunks(chunksToRequest);
}

View File

@ -576,7 +576,9 @@ function getRanges(glyphs, toUnicodeExtraMap, numGlyphs) {
if (codes.length === 0) {
codes.push({ fontCharCode: 0, glyphId: 0 });
}
codes.sort((a, b) => a.fontCharCode - b.fontCharCode);
codes.sort(function fontGetRangesSort(a, b) {
return a.fontCharCode - b.fontCharCode;
});
// Split the sorted codes into ranges.
const ranges = [];
@ -1775,7 +1777,9 @@ class Font {
}
// removing duplicate entries
mappings.sort((a, b) => a.charCode - b.charCode);
mappings.sort(function (a, b) {
return a.charCode - b.charCode;
});
const finalMappings = [],
seenCharCodes = new Set();
for (const map of mappings) {

View File

@ -374,7 +374,9 @@ function decodeBitmap(
// Sorting is non-standard, and it is not required. But sorting increases
// the number of template bits that can be reused from the previous
// contextLabel in the main loop.
template.sort((a, b) => a.y - b.y || a.x - b.x);
template.sort(function (a, b) {
return a.y - b.y || a.x - b.x;
});
const templateLength = template.length;
const templateX = new Int8Array(templateLength);

View File

@ -48,9 +48,11 @@ class RunLengthStream extends DecodeStream {
}
} else {
n = 257 - n;
const b = repeatHeader[1];
buffer = this.ensureBuffer(bufferLength + n + 1);
buffer.fill(repeatHeader[1], bufferLength, bufferLength + n);
bufferLength += n;
for (let i = 0; i < n; i++) {
buffer[bufferLength++] = b;
}
}
this.bufferLength = bufferLength;
}

View File

@ -321,7 +321,11 @@ class SimpleDOMNode {
if (!this.childNodes) {
return this.nodeValue || "";
}
return this.childNodes.map(child => child.textContent).join("");
return this.childNodes
.map(function (child) {
return child.textContent;
})
.join("");
}
get children() {

View File

@ -18,7 +18,6 @@
*/
import {
_isValidExplicitDest,
AbortException,
AnnotationMode,
assert,
@ -596,20 +595,15 @@ function getFactoryUrlProp(val) {
throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
}
const isRefProxy = v =>
typeof v === "object" &&
Number.isInteger(v?.num) &&
v.num >= 0 &&
Number.isInteger(v?.gen) &&
v.gen >= 0;
const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
const isValidExplicitDest = _isValidExplicitDest.bind(
null,
/* validRef = */ isRefProxy,
/* validName = */ isNameProxy
);
function isRefProxy(ref) {
return (
typeof ref === "object" &&
Number.isInteger(ref?.num) &&
ref.num >= 0 &&
Number.isInteger(ref?.gen) &&
ref.gen >= 0
);
}
/**
* @typedef {Object} OnProgressParameters
@ -2342,7 +2336,6 @@ class PDFWorker {
/**
* @param {PDFWorkerParameters} params - The worker initialization parameters.
* @returns {PDFWorker}
*/
static fromPort(params) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
@ -3491,7 +3484,6 @@ const build =
export {
build,
getDocument,
isValidExplicitDest,
LoopbackPort,
PDFDataRangeTransport,
PDFDocumentLoadingTask,

View File

@ -45,7 +45,6 @@ import {
import {
build,
getDocument,
isValidExplicitDest,
PDFDataRangeTransport,
PDFWorker,
version,
@ -118,7 +117,6 @@ export {
InvalidPDFException,
isDataScheme,
isPdfFile,
isValidExplicitDest,
noContextMenu,
normalizeUnicode,
OPS,

View File

@ -1080,54 +1080,6 @@ function getUuid() {
const AnnotationPrefix = "pdfjs_internal_id_";
function _isValidExplicitDest(validRef, validName, dest) {
if (!Array.isArray(dest) || dest.length < 2) {
return false;
}
const [page, zoom, ...args] = dest;
if (!validRef(page) && !Number.isInteger(page)) {
return false;
}
if (!validName(zoom)) {
return false;
}
const argsLen = args.length;
let allowNull = true;
switch (zoom.name) {
case "XYZ":
if (argsLen < 2 || argsLen > 3) {
return false;
}
break;
case "Fit":
case "FitB":
return argsLen === 0;
case "FitH":
case "FitBH":
case "FitV":
case "FitBV":
if (argsLen > 1) {
return false;
}
break;
case "FitR":
if (argsLen !== 4) {
return false;
}
allowNull = false;
break;
default:
return false;
}
for (const arg of args) {
if (typeof arg === "number" || (allowNull && arg === null)) {
continue;
}
return false;
}
return true;
}
// TODO: Remove this once `Uint8Array.prototype.toHex` is generally available.
function toHexUtil(arr) {
if (Uint8Array.prototype.toHex) {
@ -1167,7 +1119,6 @@ if (
}
export {
_isValidExplicitDest,
AbortException,
AnnotationActionEventType,
AnnotationBorderStyleType,

View File

@ -42,7 +42,6 @@ function downloadFile(file, url, redirects = 0) {
.get(url, async function (response) {
if ([301, 302, 307, 308].includes(response.statusCode)) {
if (redirects > 10) {
response.resume();
reject(new Error("Too many redirects"));
return;
}
@ -51,14 +50,12 @@ function downloadFile(file, url, redirects = 0) {
await downloadFile(file, redirectTo, ++redirects);
resolve();
} catch (ex) {
response.resume();
reject(ex);
}
return;
}
if (response.statusCode !== 200) {
response.resume();
reject(new Error(`HTTP ${response.statusCode}`));
return;
}
@ -91,7 +88,7 @@ async function downloadManifestFiles(manifest) {
} catch (ex) {
console.error(`Error during downloading of ${url}:`, ex);
fs.writeFileSync(file, ""); // making it empty file
fs.writeFileSync(`${file}.error`, ex.toString());
fs.writeFileSync(`${file}.error`, ex);
}
}
}

View File

@ -49,7 +49,10 @@ async function initializePDFJS(callback) {
"pdfjs-test/font/font_os2_spec.js",
"pdfjs-test/font/font_post_spec.js",
"pdfjs-test/font/font_fpgm_spec.js",
].map(moduleName => import(moduleName)) // eslint-disable-line no-unsanitized/method
].map(function (moduleName) {
// eslint-disable-next-line no-unsanitized/method
return import(moduleName);
})
);
callback();

View File

@ -13,46 +13,10 @@
* limitations under the License.
*/
import {
awaitPromise,
closePages,
createPromise,
loadAndWait,
} from "./test_utils.mjs";
import { closePages, createPromise, loadAndWait } from "./test_utils.mjs";
function waitForLinkAnnotations(page, pageNumber) {
return page.evaluateHandle(
number => [
new Promise(resolve => {
const { eventBus } = window.PDFViewerApplication;
eventBus.on("linkannotationsadded", function listener(e) {
if (number === undefined || e.pageNumber === number) {
resolve();
eventBus.off("linkannotationsadded", listener);
}
});
}),
],
pageNumber
);
}
function recordInitialLinkAnnotationsEvent(eventBus) {
globalThis.initialLinkAnnotationsEventFired = false;
eventBus.on(
"linkannotationsadded",
() => {
globalThis.initialLinkAnnotationsEventFired = true;
},
{ once: true }
);
}
function waitForInitialLinkAnnotations(page) {
function waitForLinkAnnotations(page) {
return createPromise(page, resolve => {
if (globalThis.initialLinkAnnotationsEventFired) {
resolve();
return;
}
window.PDFViewerApplication.eventBus.on("linkannotationsadded", resolve, {
once: true,
});
@ -214,49 +178,4 @@ describe("autolinker", function () {
);
});
});
describe("when highlighting search results", function () {
let pages;
beforeAll(async () => {
pages = await loadAndWait(
"issue3115r.pdf",
".annotationLayer",
null,
{ eventBusSetup: recordInitialLinkAnnotationsEvent },
{ enableAutoLinking: true }
);
});
afterAll(async () => {
await closePages(pages);
});
it("must find links that overlap with search results", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await awaitPromise(await waitForInitialLinkAnnotations(page));
const linkAnnotationsPromise = await waitForLinkAnnotations(page, 36);
// Search for "rich.edu"
await page.click("#viewFindButton");
await page.waitForSelector("#viewFindButton", { hidden: false });
await page.type("#findInput", "rich.edu");
await page.waitForSelector(".textLayer .highlight");
await awaitPromise(linkAnnotationsPromise);
const urls = await page.$$eval(
".page[data-page-number='36'] > .annotationLayer > .linkAnnotation > a",
annotations => annotations.map(a => a.href)
);
expect(urls)
.withContext(`In ${browserName}`)
.toContain(jasmine.stringContaining("rich.edu"));
})
);
});
});
});

View File

@ -64,7 +64,9 @@ function flatten(stats) {
});
// Use only overall results if not grouped by 'stat'
if (!options.groupBy.includes("stat")) {
rows = rows.filter(s => s.stat === "Overall");
rows = rows.filter(function (s) {
return s.stat === "Overall";
});
}
return rows;
}
@ -127,7 +129,9 @@ function stat(baseline, current) {
}
const rows = [];
// collect rows and measure column widths
const width = labels.map(s => s.length);
const width = labels.map(function (s) {
return s.length;
});
rows.push(labels);
for (const key of keys) {
const baselineMean = mean(baselineGroup[key]);
@ -158,7 +162,9 @@ function stat(baseline, current) {
}
// add horizontal line
const hline = width.map(w => new Array(w + 1).join("-"));
const hline = width.map(function (w) {
return new Array(w + 1).join("-");
});
rows.splice(1, 0, hline);
// print output

View File

@ -1051,7 +1051,9 @@ async function closeSession(browser) {
await session.browser.close();
}
session.closed = true;
const allClosed = sessions.every(s => s.closed);
const allClosed = sessions.every(function (s) {
return s.closed;
});
if (allClosed) {
if (tempDir) {
fs.rmSync(tempDir, { recursive: true, force: true });

View File

@ -4869,9 +4869,9 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`)
// Issue 6205 reported an issue with font rendering, so clear the loaded
// fonts so that we can see whether loading PDFs in parallel does not
// cause any issues with the rendered fonts.
const destroyPromises = loadingTasks.map(loadingTask =>
loadingTask.destroy()
);
const destroyPromises = loadingTasks.map(function (loadingTask) {
return loadingTask.destroy();
});
await Promise.all(destroyPromises);
});

View File

@ -100,7 +100,10 @@ async function initializePDFJS(callback) {
"pdfjs-test/unit/xfa_serialize_data_spec.js",
"pdfjs-test/unit/xfa_tohtml_spec.js",
"pdfjs-test/unit/xml_spec.js",
].map(moduleName => import(moduleName)) // eslint-disable-line no-unsanitized/method
].map(function (moduleName) {
// eslint-disable-next-line no-unsanitized/method
return import(moduleName);
})
);
if (isNodeJS) {

View File

@ -36,7 +36,6 @@ import {
import {
build,
getDocument,
isValidExplicitDest,
PDFDataRangeTransport,
PDFWorker,
version,
@ -95,7 +94,6 @@ const expectedAPI = Object.freeze({
InvalidPDFException,
isDataScheme,
isPdfFile,
isValidExplicitDest,
noContextMenu,
normalizeUnicode,
OPS,

View File

@ -69,55 +69,13 @@ function calculateLinkPosition(range, pdfPageView) {
return { quadPoints, rect };
}
/**
* Given a DOM node `container` and an index into its text contents `offset`,
* returns a pair consisting of text node that the `offset` actually points
* to, together with the offset relative to that text node.
* When the offset points at the boundary between two node, the result will
* point to the first text node in depth-first traversal order.
*
* For example, given this DOM:
* <p>abc<span>def</span>ghi</p>
*
* textPosition(p, 0) -> [#text "abc", 0] (before `a`)
* textPosition(p, 2) -> [#text "abc", 2] (between `b` and `c`)
* textPosition(p, 3) -> [#text "abc", 3] (after `c`)
* textPosition(p, 5) -> [#text "def", 2] (between `e` and `f`)
* textPosition(p, 6) -> [#text "def", 3] (after `f`)
*/
function textPosition(container, offset) {
let currentContainer = container;
do {
if (currentContainer.nodeType === Node.TEXT_NODE) {
const currentLength = currentContainer.textContent.length;
if (offset <= currentLength) {
return [currentContainer, offset];
}
offset -= currentLength;
} else if (currentContainer.firstChild) {
currentContainer = currentContainer.firstChild;
continue;
}
while (!currentContainer.nextSibling && currentContainer !== container) {
currentContainer = currentContainer.parentNode;
}
if (currentContainer !== container) {
currentContainer = currentContainer.nextSibling;
}
} while (currentContainer !== container);
throw new Error("Offset is bigger than container's contents length.");
}
function createLinkAnnotation({ url, index, length }, pdfPageView, id) {
const highlighter = pdfPageView._textHighlighter;
const [{ begin, end }] = highlighter._convertMatches([index], [length]);
const range = new Range();
range.setStart(
...textPosition(highlighter.textDivs[begin.divIdx], begin.offset)
);
range.setEnd(...textPosition(highlighter.textDivs[end.divIdx], end.offset));
range.setStart(highlighter.textDivs[begin.divIdx].firstChild, begin.offset);
range.setEnd(highlighter.textDivs[end.divIdx].firstChild, end.offset);
return {
id: `inferred_link_${id}`,

View File

@ -398,7 +398,9 @@ class Stepper {
}
getNextBreakPoint() {
this.breakPoints.sort((a, b) => a - b);
this.breakPoints.sort(function (a, b) {
return a - b;
});
for (const breakPoint of this.breakPoints) {
if (breakPoint > this.currentIdx) {
return breakPoint;
@ -485,7 +487,9 @@ const Stats = (function Stats() {
statsDiv.textContent = stat.toString();
wrapper.append(title, statsDiv);
stats.push({ pageNumber, div: wrapper });
stats.sort((a, b) => a.pageNumber - b.pageNumber);
stats.sort(function (a, b) {
return a.pageNumber - b.pageNumber;
});
clear(this.panel);
for (const entry of stats) {
this.panel.append(entry.div);

View File

@ -53,12 +53,6 @@ class ExternalServices extends BaseExternalServices {
}
class MLManager {
static {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
this.getFakeMLManager = options => new FakeMLManager(options);
}
}
async isEnabledFor(_name) {
return false;
}
@ -74,82 +68,83 @@ class MLManager {
guess(_data) {}
toggleService(_name, _enabled) {}
static getFakeMLManager(options) {
return new FakeMLManager(options);
}
}
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
// eslint-disable-next-line no-var
var FakeMLManager = class {
eventBus = null;
class FakeMLManager {
eventBus = null;
hasProgress = false;
hasProgress = false;
constructor({ enableGuessAltText, enableAltTextModelDownload }) {
this.enableGuessAltText = enableGuessAltText;
this.enableAltTextModelDownload = enableAltTextModelDownload;
}
constructor({ enableGuessAltText, enableAltTextModelDownload }) {
this.enableGuessAltText = enableGuessAltText;
this.enableAltTextModelDownload = enableAltTextModelDownload;
}
setEventBus(eventBus, abortSignal) {
this.eventBus = eventBus;
}
setEventBus(eventBus, abortSignal) {
this.eventBus = eventBus;
}
async isEnabledFor(_name) {
return this.enableGuessAltText;
}
async isEnabledFor(_name) {
return this.enableGuessAltText;
}
async deleteModel(_name) {
this.enableAltTextModelDownload = false;
return null;
}
async deleteModel(_name) {
this.enableAltTextModelDownload = false;
return null;
}
async loadModel(_name) {}
async loadModel(_name) {}
async downloadModel(_name) {
// Simulate downloading the model but with progress.
// The progress can be seen in the new alt-text dialog.
this.hasProgress = true;
async downloadModel(_name) {
// Simulate downloading the model but with progress.
// The progress can be seen in the new alt-text dialog.
this.hasProgress = true;
const { promise, resolve } = Promise.withResolvers();
const total = 1e8;
const end = 1.5 * total;
const increment = 5e6;
let loaded = 0;
const id = setInterval(() => {
loaded += increment;
if (loaded <= end) {
this.eventBus.dispatch("loadaiengineprogress", {
source: this,
detail: {
total,
totalLoaded: loaded,
finished: loaded + increment >= end,
},
});
return;
}
clearInterval(id);
this.hasProgress = false;
this.enableAltTextModelDownload = true;
resolve(true);
}, 900);
return promise;
}
const { promise, resolve } = Promise.withResolvers();
const total = 1e8;
const end = 1.5 * total;
const increment = 5e6;
let loaded = 0;
const id = setInterval(() => {
loaded += increment;
if (loaded <= end) {
this.eventBus.dispatch("loadaiengineprogress", {
source: this,
detail: {
total,
totalLoaded: loaded,
finished: loaded + increment >= end,
},
});
return;
}
clearInterval(id);
this.hasProgress = false;
this.enableAltTextModelDownload = true;
resolve(true);
}, 900);
return promise;
}
isReady(_name) {
return this.enableAltTextModelDownload;
}
isReady(_name) {
return this.enableAltTextModelDownload;
}
guess({ request: { data } }) {
return new Promise(resolve => {
setTimeout(() => {
resolve(data ? { output: "Fake alt text." } : { error: true });
}, 3000);
});
}
guess({ request: { data } }) {
return new Promise(resolve => {
setTimeout(() => {
resolve(data ? { output: "Fake alt text." } : { error: true });
}, 3000);
});
}
toggleService(_name, enabled) {
this.enableGuessAltText = enabled;
}
};
toggleService(_name, enabled) {
this.enableGuessAltText = enabled;
}
}
export { ExternalServices, initCom, MLManager, Preferences };

View File

@ -16,7 +16,6 @@
/** @typedef {import("./event_utils").EventBus} EventBus */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
import { isValidExplicitDest } from "pdfjs-lib";
import { parseQueryString } from "./ui_utils.js";
const DEFAULT_LINK_REL = "noopener noreferrer nofollow";
@ -416,7 +415,7 @@ class PDFLinkService {
}
} catch {}
if (typeof dest === "string" || isValidExplicitDest(dest)) {
if (typeof dest === "string" || PDFLinkService.#isValidExplicitDest(dest)) {
this.goToDestination(dest);
return;
}
@ -487,6 +486,60 @@ class PDFLinkService {
optionalContentConfig
);
}
static #isValidExplicitDest(dest) {
if (!Array.isArray(dest) || dest.length < 2) {
return false;
}
const [page, zoom, ...args] = dest;
if (
!(
typeof page === "object" &&
Number.isInteger(page?.num) &&
Number.isInteger(page?.gen)
) &&
!Number.isInteger(page)
) {
return false;
}
if (!(typeof zoom === "object" && typeof zoom?.name === "string")) {
return false;
}
const argsLen = args.length;
let allowNull = true;
switch (zoom.name) {
case "XYZ":
if (argsLen < 2 || argsLen > 3) {
return false;
}
break;
case "Fit":
case "FitB":
return argsLen === 0;
case "FitH":
case "FitBH":
case "FitV":
case "FitBV":
if (argsLen > 1) {
return false;
}
break;
case "FitR":
if (argsLen !== 4) {
return false;
}
allowNull = false;
break;
default:
return false;
}
for (const arg of args) {
if (!(typeof arg === "number" || (allowNull && arg === null))) {
return false;
}
}
return true;
}
}
/**

View File

@ -39,7 +39,6 @@ const {
InvalidPDFException,
isDataScheme,
isPdfFile,
isValidExplicitDest,
noContextMenu,
normalizeUnicode,
OPS,
@ -91,7 +90,6 @@ export {
InvalidPDFException,
isDataScheme,
isPdfFile,
isValidExplicitDest,
noContextMenu,
normalizeUnicode,
OPS,