Merge pull request #14424 from Snuffleupagus/mv-addLinkAttributes
[api-minor] Move `addLinkAttributes`, `LinkTarget`, and `removeNullCharacters` into the viewer (PR 14092 follow-up)
This commit is contained in:
commit
8ac0ccc227
@ -13,24 +13,21 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
|
||||||
assert,
|
|
||||||
BaseException,
|
|
||||||
isString,
|
|
||||||
removeNullCharacters,
|
|
||||||
shadow,
|
|
||||||
stringToBytes,
|
|
||||||
Util,
|
|
||||||
warn,
|
|
||||||
} from "../shared/util.js";
|
|
||||||
import {
|
import {
|
||||||
BaseCanvasFactory,
|
BaseCanvasFactory,
|
||||||
BaseCMapReaderFactory,
|
BaseCMapReaderFactory,
|
||||||
BaseStandardFontDataFactory,
|
BaseStandardFontDataFactory,
|
||||||
BaseSVGFactory,
|
BaseSVGFactory,
|
||||||
} from "./base_factory.js";
|
} from "./base_factory.js";
|
||||||
|
import {
|
||||||
|
BaseException,
|
||||||
|
isString,
|
||||||
|
shadow,
|
||||||
|
stringToBytes,
|
||||||
|
Util,
|
||||||
|
warn,
|
||||||
|
} from "../shared/util.js";
|
||||||
|
|
||||||
const DEFAULT_LINK_REL = "noopener noreferrer nofollow";
|
|
||||||
const SVG_NS = "http://www.w3.org/2000/svg";
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
const PixelsPerInch = {
|
const PixelsPerInch = {
|
||||||
@ -316,70 +313,6 @@ class RenderingCancelledException extends BaseException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LinkTarget = {
|
|
||||||
NONE: 0, // Default value.
|
|
||||||
SELF: 1,
|
|
||||||
BLANK: 2,
|
|
||||||
PARENT: 3,
|
|
||||||
TOP: 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef ExternalLinkParameters
|
|
||||||
* @typedef {Object} ExternalLinkParameters
|
|
||||||
* @property {string} url - An absolute URL.
|
|
||||||
* @property {LinkTarget} [target] - The link target. The default value is
|
|
||||||
* `LinkTarget.NONE`.
|
|
||||||
* @property {string} [rel] - The link relationship. The default value is
|
|
||||||
* `DEFAULT_LINK_REL`.
|
|
||||||
* @property {boolean} [enabled] - Whether the link should be enabled. The
|
|
||||||
* default value is true.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds various attributes (href, title, target, rel) to hyperlinks.
|
|
||||||
* @param {HTMLAnchorElement} link - The link element.
|
|
||||||
* @param {ExternalLinkParameters} params
|
|
||||||
*/
|
|
||||||
function addLinkAttributes(link, { url, target, rel, enabled = true } = {}) {
|
|
||||||
assert(
|
|
||||||
url && typeof url === "string",
|
|
||||||
'addLinkAttributes: A valid "url" parameter must provided.'
|
|
||||||
);
|
|
||||||
|
|
||||||
const urlNullRemoved = removeNullCharacters(url);
|
|
||||||
if (enabled) {
|
|
||||||
link.href = link.title = urlNullRemoved;
|
|
||||||
} else {
|
|
||||||
link.href = "";
|
|
||||||
link.title = `Disabled: ${urlNullRemoved}`;
|
|
||||||
link.onclick = () => {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let targetStr = ""; // LinkTarget.NONE
|
|
||||||
switch (target) {
|
|
||||||
case LinkTarget.NONE:
|
|
||||||
break;
|
|
||||||
case LinkTarget.SELF:
|
|
||||||
targetStr = "_self";
|
|
||||||
break;
|
|
||||||
case LinkTarget.BLANK:
|
|
||||||
targetStr = "_blank";
|
|
||||||
break;
|
|
||||||
case LinkTarget.PARENT:
|
|
||||||
targetStr = "_parent";
|
|
||||||
break;
|
|
||||||
case LinkTarget.TOP:
|
|
||||||
targetStr = "_top";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
link.target = targetStr;
|
|
||||||
|
|
||||||
link.rel = typeof rel === "string" ? rel : DEFAULT_LINK_REL;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDataScheme(url) {
|
function isDataScheme(url) {
|
||||||
const ii = url.length;
|
const ii = url.length;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
@ -632,7 +565,6 @@ function getXfaPageViewport(xfaPage, { scale = 1, rotation = 0 }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
addLinkAttributes,
|
|
||||||
deprecated,
|
deprecated,
|
||||||
DOMCanvasFactory,
|
DOMCanvasFactory,
|
||||||
DOMCMapReaderFactory,
|
DOMCMapReaderFactory,
|
||||||
@ -644,7 +576,6 @@ export {
|
|||||||
isDataScheme,
|
isDataScheme,
|
||||||
isPdfFile,
|
isPdfFile,
|
||||||
isValidFetchUrl,
|
isValidFetchUrl,
|
||||||
LinkTarget,
|
|
||||||
loadScript,
|
loadScript,
|
||||||
PageViewport,
|
PageViewport,
|
||||||
PDFDateString,
|
PDFDateString,
|
||||||
|
71
src/pdf.js
71
src/pdf.js
@ -12,7 +12,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/* eslint-disable sort-exports/sort-exports */
|
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("./display/api").PDFDocumentLoadingTask} PDFDocumentLoadingTask */
|
/** @typedef {import("./display/api").PDFDocumentLoadingTask} PDFDocumentLoadingTask */
|
||||||
@ -20,19 +19,6 @@
|
|||||||
/** @typedef {import("./display/api").PDFPageProxy} PDFPageProxy */
|
/** @typedef {import("./display/api").PDFPageProxy} PDFPageProxy */
|
||||||
/** @typedef {import("./display/api").RenderTask} RenderTask */
|
/** @typedef {import("./display/api").RenderTask} RenderTask */
|
||||||
|
|
||||||
import {
|
|
||||||
addLinkAttributes,
|
|
||||||
getFilenameFromUrl,
|
|
||||||
getPdfFilenameFromUrl,
|
|
||||||
getXfaPageViewport,
|
|
||||||
isPdfFile,
|
|
||||||
isValidFetchUrl,
|
|
||||||
LinkTarget,
|
|
||||||
loadScript,
|
|
||||||
PDFDateString,
|
|
||||||
PixelsPerInch,
|
|
||||||
RenderingCancelledException,
|
|
||||||
} from "./display/display_utils.js";
|
|
||||||
import {
|
import {
|
||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
@ -44,7 +30,6 @@ import {
|
|||||||
OPS,
|
OPS,
|
||||||
PasswordResponses,
|
PasswordResponses,
|
||||||
PermissionFlag,
|
PermissionFlag,
|
||||||
removeNullCharacters,
|
|
||||||
shadow,
|
shadow,
|
||||||
UnexpectedResponseException,
|
UnexpectedResponseException,
|
||||||
UNSUPPORTED_FEATURES,
|
UNSUPPORTED_FEATURES,
|
||||||
@ -60,6 +45,17 @@ import {
|
|||||||
setPDFNetworkStreamFactory,
|
setPDFNetworkStreamFactory,
|
||||||
version,
|
version,
|
||||||
} from "./display/api.js";
|
} from "./display/api.js";
|
||||||
|
import {
|
||||||
|
getFilenameFromUrl,
|
||||||
|
getPdfFilenameFromUrl,
|
||||||
|
getXfaPageViewport,
|
||||||
|
isPdfFile,
|
||||||
|
isValidFetchUrl,
|
||||||
|
loadScript,
|
||||||
|
PDFDateString,
|
||||||
|
PixelsPerInch,
|
||||||
|
RenderingCancelledException,
|
||||||
|
} from "./display/display_utils.js";
|
||||||
import { AnnotationLayer } from "./display/annotation_layer.js";
|
import { AnnotationLayer } from "./display/annotation_layer.js";
|
||||||
import { GlobalWorkerOptions } from "./display/worker_options.js";
|
import { GlobalWorkerOptions } from "./display/worker_options.js";
|
||||||
import { isNodeJS } from "./shared/is_node.js";
|
import { isNodeJS } from "./shared/is_node.js";
|
||||||
@ -108,49 +104,38 @@ if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
// From "./display/display_utils.js":
|
AnnotationLayer,
|
||||||
addLinkAttributes,
|
|
||||||
getFilenameFromUrl,
|
|
||||||
getPdfFilenameFromUrl,
|
|
||||||
isPdfFile,
|
|
||||||
LinkTarget,
|
|
||||||
loadScript,
|
|
||||||
PDFDateString,
|
|
||||||
PixelsPerInch,
|
|
||||||
RenderingCancelledException,
|
|
||||||
getXfaPageViewport,
|
|
||||||
// From "./shared/util.js":
|
|
||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
|
build,
|
||||||
CMapCompressionType,
|
CMapCompressionType,
|
||||||
createObjectURL,
|
createObjectURL,
|
||||||
createPromiseCapability,
|
createPromiseCapability,
|
||||||
createValidAbsoluteUrl,
|
createValidAbsoluteUrl,
|
||||||
|
getDocument,
|
||||||
|
getFilenameFromUrl,
|
||||||
|
getPdfFilenameFromUrl,
|
||||||
|
getXfaPageViewport,
|
||||||
|
GlobalWorkerOptions,
|
||||||
InvalidPDFException,
|
InvalidPDFException,
|
||||||
|
isPdfFile,
|
||||||
|
loadScript,
|
||||||
|
LoopbackPort,
|
||||||
MissingPDFException,
|
MissingPDFException,
|
||||||
OPS,
|
OPS,
|
||||||
PasswordResponses,
|
PasswordResponses,
|
||||||
|
PDFDataRangeTransport,
|
||||||
|
PDFDateString,
|
||||||
|
PDFWorker,
|
||||||
PermissionFlag,
|
PermissionFlag,
|
||||||
removeNullCharacters,
|
PixelsPerInch,
|
||||||
|
RenderingCancelledException,
|
||||||
|
renderTextLayer,
|
||||||
shadow,
|
shadow,
|
||||||
|
SVGGraphics,
|
||||||
UnexpectedResponseException,
|
UnexpectedResponseException,
|
||||||
UNSUPPORTED_FEATURES,
|
UNSUPPORTED_FEATURES,
|
||||||
Util,
|
Util,
|
||||||
VerbosityLevel,
|
VerbosityLevel,
|
||||||
// From "./display/api.js":
|
|
||||||
build,
|
|
||||||
getDocument,
|
|
||||||
LoopbackPort,
|
|
||||||
PDFDataRangeTransport,
|
|
||||||
PDFWorker,
|
|
||||||
version,
|
version,
|
||||||
// From "./display/annotation_layer.js":
|
|
||||||
AnnotationLayer,
|
|
||||||
// From "./display/worker_options.js":
|
|
||||||
GlobalWorkerOptions,
|
|
||||||
// From "./display/text_layer.js":
|
|
||||||
renderTextLayer,
|
|
||||||
// From "./display/svg.js":
|
|
||||||
SVGGraphics,
|
|
||||||
// From "./display/xfa_layer.js":
|
|
||||||
XfaLayer,
|
XfaLayer,
|
||||||
};
|
};
|
||||||
|
@ -575,23 +575,6 @@ class AbortException extends BaseException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NullCharactersRegExp = /\x00+/g;
|
|
||||||
const InvisibleCharactersRegExp = /[\x01-\x1F]/g;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} str
|
|
||||||
*/
|
|
||||||
function removeNullCharacters(str, replaceInvisible = false) {
|
|
||||||
if (typeof str !== "string") {
|
|
||||||
warn("The argument for removeNullCharacters must be a string.");
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
if (replaceInvisible) {
|
|
||||||
str = str.replace(InvisibleCharactersRegExp, " ");
|
|
||||||
}
|
|
||||||
return str.replace(NullCharactersRegExp, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function bytesToString(bytes) {
|
function bytesToString(bytes) {
|
||||||
assert(
|
assert(
|
||||||
bytes !== null && typeof bytes === "object" && bytes.length !== undefined,
|
bytes !== null && typeof bytes === "object" && bytes.length !== undefined,
|
||||||
@ -1185,7 +1168,6 @@ export {
|
|||||||
PasswordException,
|
PasswordException,
|
||||||
PasswordResponses,
|
PasswordResponses,
|
||||||
PermissionFlag,
|
PermissionFlag,
|
||||||
removeNullCharacters,
|
|
||||||
RenderingIntentFlag,
|
RenderingIntentFlag,
|
||||||
setVerbosityLevel,
|
setVerbosityLevel,
|
||||||
shadow,
|
shadow,
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
isPortraitOrientation,
|
isPortraitOrientation,
|
||||||
isValidRotation,
|
isValidRotation,
|
||||||
parseQueryString,
|
parseQueryString,
|
||||||
|
removeNullCharacters,
|
||||||
} from "../../web/ui_utils.js";
|
} from "../../web/ui_utils.js";
|
||||||
|
|
||||||
describe("ui_utils", function () {
|
describe("ui_utils", function () {
|
||||||
@ -139,6 +140,30 @@ describe("ui_utils", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("removeNullCharacters", function () {
|
||||||
|
it("should not modify string without null characters", function () {
|
||||||
|
const str = "string without null chars";
|
||||||
|
expect(removeNullCharacters(str)).toEqual("string without null chars");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should modify string with null characters", function () {
|
||||||
|
const str = "string\x00With\x00Null\x00Chars";
|
||||||
|
expect(removeNullCharacters(str)).toEqual("stringWithNullChars");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should modify string with non-displayable characters", function () {
|
||||||
|
const str = Array.from(Array(32).keys())
|
||||||
|
.map(x => String.fromCharCode(x) + "a")
|
||||||
|
.join("");
|
||||||
|
// \x00 is replaced by an empty string.
|
||||||
|
const expected =
|
||||||
|
"a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a";
|
||||||
|
expect(removeNullCharacters(str, /* replaceInvisible */ true)).toEqual(
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("getPageSizeInches", function () {
|
describe("getPageSizeInches", function () {
|
||||||
it("gets page size (in inches)", function () {
|
it("gets page size (in inches)", function () {
|
||||||
const page = {
|
const page = {
|
||||||
|
@ -25,7 +25,6 @@ import {
|
|||||||
isNum,
|
isNum,
|
||||||
isSameOrigin,
|
isSameOrigin,
|
||||||
isString,
|
isString,
|
||||||
removeNullCharacters,
|
|
||||||
string32,
|
string32,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
@ -175,30 +174,6 @@ describe("util", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("removeNullCharacters", function () {
|
|
||||||
it("should not modify string without null characters", function () {
|
|
||||||
const str = "string without null chars";
|
|
||||||
expect(removeNullCharacters(str)).toEqual("string without null chars");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should modify string with null characters", function () {
|
|
||||||
const str = "string\x00With\x00Null\x00Chars";
|
|
||||||
expect(removeNullCharacters(str)).toEqual("stringWithNullChars");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should modify string with non-displayable characters", function () {
|
|
||||||
const str = Array.from(Array(32).keys())
|
|
||||||
.map(x => String.fromCharCode(x) + "a")
|
|
||||||
.join("");
|
|
||||||
// \x00 is replaced by an empty string.
|
|
||||||
const expected =
|
|
||||||
"a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a";
|
|
||||||
expect(removeNullCharacters(str, /* replaceInvisible */ true)).toEqual(
|
|
||||||
expected
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("ReadableStream", function () {
|
describe("ReadableStream", function () {
|
||||||
it("should return an Object", function () {
|
it("should return an Object", function () {
|
||||||
const readable = new ReadableStream();
|
const readable = new ReadableStream();
|
||||||
|
@ -46,7 +46,6 @@ import {
|
|||||||
GlobalWorkerOptions,
|
GlobalWorkerOptions,
|
||||||
InvalidPDFException,
|
InvalidPDFException,
|
||||||
isPdfFile,
|
isPdfFile,
|
||||||
LinkTarget,
|
|
||||||
loadScript,
|
loadScript,
|
||||||
MissingPDFException,
|
MissingPDFException,
|
||||||
OPS,
|
OPS,
|
||||||
@ -57,6 +56,7 @@ import {
|
|||||||
version,
|
version,
|
||||||
} from "pdfjs-lib";
|
} from "pdfjs-lib";
|
||||||
import { CursorTool, PDFCursorTools } from "./pdf_cursor_tools.js";
|
import { CursorTool, PDFCursorTools } from "./pdf_cursor_tools.js";
|
||||||
|
import { LinkTarget, PDFLinkService } from "./pdf_link_service.js";
|
||||||
import { OverlayManager } from "./overlay_manager.js";
|
import { OverlayManager } from "./overlay_manager.js";
|
||||||
import { PasswordPrompt } from "./password_prompt.js";
|
import { PasswordPrompt } from "./password_prompt.js";
|
||||||
import { PDFAttachmentViewer } from "./pdf_attachment_viewer.js";
|
import { PDFAttachmentViewer } from "./pdf_attachment_viewer.js";
|
||||||
@ -65,7 +65,6 @@ import { PDFFindBar } from "./pdf_find_bar.js";
|
|||||||
import { PDFFindController } from "./pdf_find_controller.js";
|
import { PDFFindController } from "./pdf_find_controller.js";
|
||||||
import { PDFHistory } from "./pdf_history.js";
|
import { PDFHistory } from "./pdf_history.js";
|
||||||
import { PDFLayerViewer } from "./pdf_layer_viewer.js";
|
import { PDFLayerViewer } from "./pdf_layer_viewer.js";
|
||||||
import { PDFLinkService } from "./pdf_link_service.js";
|
|
||||||
import { PDFOutlineViewer } from "./pdf_outline_viewer.js";
|
import { PDFOutlineViewer } from "./pdf_outline_viewer.js";
|
||||||
import { PDFPresentationMode } from "./pdf_presentation_mode.js";
|
import { PDFPresentationMode } from "./pdf_presentation_mode.js";
|
||||||
import { PDFRenderingQueue } from "./pdf_rendering_queue.js";
|
import { PDFRenderingQueue } from "./pdf_rendering_queue.js";
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { removeNullCharacters } from "pdfjs-lib";
|
import { removeNullCharacters } from "./ui_utils.js";
|
||||||
|
|
||||||
const TREEITEM_OFFSET_TOP = -100; // px
|
const TREEITEM_OFFSET_TOP = -100; // px
|
||||||
const TREEITEM_SELECTED_CLASS = "selected";
|
const TREEITEM_SELECTED_CLASS = "selected";
|
||||||
|
@ -16,8 +16,72 @@
|
|||||||
/** @typedef {import("./event_utils").EventBus} EventBus */
|
/** @typedef {import("./event_utils").EventBus} EventBus */
|
||||||
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
|
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
|
||||||
|
|
||||||
import { addLinkAttributes, LinkTarget } from "pdfjs-lib";
|
import { parseQueryString, removeNullCharacters } from "./ui_utils.js";
|
||||||
import { parseQueryString } from "./ui_utils.js";
|
|
||||||
|
const DEFAULT_LINK_REL = "noopener noreferrer nofollow";
|
||||||
|
|
||||||
|
const LinkTarget = {
|
||||||
|
NONE: 0, // Default value.
|
||||||
|
SELF: 1,
|
||||||
|
BLANK: 2,
|
||||||
|
PARENT: 3,
|
||||||
|
TOP: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef ExternalLinkParameters
|
||||||
|
* @typedef {Object} ExternalLinkParameters
|
||||||
|
* @property {string} url - An absolute URL.
|
||||||
|
* @property {LinkTarget} [target] - The link target. The default value is
|
||||||
|
* `LinkTarget.NONE`.
|
||||||
|
* @property {string} [rel] - The link relationship. The default value is
|
||||||
|
* `DEFAULT_LINK_REL`.
|
||||||
|
* @property {boolean} [enabled] - Whether the link should be enabled. The
|
||||||
|
* default value is true.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds various attributes (href, title, target, rel) to hyperlinks.
|
||||||
|
* @param {HTMLAnchorElement} link - The link element.
|
||||||
|
* @param {ExternalLinkParameters} params
|
||||||
|
*/
|
||||||
|
function addLinkAttributes(link, { url, target, rel, enabled = true } = {}) {
|
||||||
|
if (!url || typeof url !== "string") {
|
||||||
|
throw new Error('A valid "url" parameter must provided.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlNullRemoved = removeNullCharacters(url);
|
||||||
|
if (enabled) {
|
||||||
|
link.href = link.title = urlNullRemoved;
|
||||||
|
} else {
|
||||||
|
link.href = "";
|
||||||
|
link.title = `Disabled: ${urlNullRemoved}`;
|
||||||
|
link.onclick = () => {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetStr = ""; // LinkTarget.NONE
|
||||||
|
switch (target) {
|
||||||
|
case LinkTarget.NONE:
|
||||||
|
break;
|
||||||
|
case LinkTarget.SELF:
|
||||||
|
targetStr = "_self";
|
||||||
|
break;
|
||||||
|
case LinkTarget.BLANK:
|
||||||
|
targetStr = "_blank";
|
||||||
|
break;
|
||||||
|
case LinkTarget.PARENT:
|
||||||
|
targetStr = "_parent";
|
||||||
|
break;
|
||||||
|
case LinkTarget.TOP:
|
||||||
|
targetStr = "_top";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
link.target = targetStr;
|
||||||
|
|
||||||
|
link.rel = typeof rel === "string" ? rel : DEFAULT_LINK_REL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} PDFLinkServiceOptions
|
* @typedef {Object} PDFLinkServiceOptions
|
||||||
@ -38,6 +102,8 @@ import { parseQueryString } from "./ui_utils.js";
|
|||||||
* @implements {IPDFLinkService}
|
* @implements {IPDFLinkService}
|
||||||
*/
|
*/
|
||||||
class PDFLinkService {
|
class PDFLinkService {
|
||||||
|
#pagesRefCache = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PDFLinkServiceOptions} options
|
* @param {PDFLinkServiceOptions} options
|
||||||
*/
|
*/
|
||||||
@ -57,14 +123,12 @@ class PDFLinkService {
|
|||||||
this.pdfDocument = null;
|
this.pdfDocument = null;
|
||||||
this.pdfViewer = null;
|
this.pdfViewer = null;
|
||||||
this.pdfHistory = null;
|
this.pdfHistory = null;
|
||||||
|
|
||||||
this._pagesRefCache = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDocument(pdfDocument, baseUrl = null) {
|
setDocument(pdfDocument, baseUrl = null) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
this._pagesRefCache = Object.create(null);
|
this.#pagesRefCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
setViewer(pdfViewer) {
|
setViewer(pdfViewer) {
|
||||||
@ -110,10 +174,7 @@ class PDFLinkService {
|
|||||||
this.pdfViewer.pagesRotation = value;
|
this.pdfViewer.pagesRotation = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#goToDestinationHelper(rawDest, namedDest = null, explicitDest) {
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_goToDestinationHelper(rawDest, namedDest = null, explicitDest) {
|
|
||||||
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
|
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
|
||||||
const destRef = explicitDest[0];
|
const destRef = explicitDest[0];
|
||||||
let pageNumber;
|
let pageNumber;
|
||||||
@ -128,11 +189,11 @@ class PDFLinkService {
|
|||||||
.getPageIndex(destRef)
|
.getPageIndex(destRef)
|
||||||
.then(pageIndex => {
|
.then(pageIndex => {
|
||||||
this.cachePageRef(pageIndex + 1, destRef);
|
this.cachePageRef(pageIndex + 1, destRef);
|
||||||
this._goToDestinationHelper(rawDest, namedDest, explicitDest);
|
this.#goToDestinationHelper(rawDest, namedDest, explicitDest);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
console.error(
|
console.error(
|
||||||
`PDFLinkService._goToDestinationHelper: "${destRef}" is not ` +
|
`PDFLinkService.#goToDestinationHelper: "${destRef}" is not ` +
|
||||||
`a valid page reference, for dest="${rawDest}".`
|
`a valid page reference, for dest="${rawDest}".`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -142,14 +203,14 @@ class PDFLinkService {
|
|||||||
pageNumber = destRef + 1;
|
pageNumber = destRef + 1;
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(
|
||||||
`PDFLinkService._goToDestinationHelper: "${destRef}" is not ` +
|
`PDFLinkService.#goToDestinationHelper: "${destRef}" is not ` +
|
||||||
`a valid destination reference, for dest="${rawDest}".`
|
`a valid destination reference, for dest="${rawDest}".`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
|
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
|
||||||
console.error(
|
console.error(
|
||||||
`PDFLinkService._goToDestinationHelper: "${pageNumber}" is not ` +
|
`PDFLinkService.#goToDestinationHelper: "${pageNumber}" is not ` +
|
||||||
`a valid page number, for dest="${rawDest}".`
|
`a valid page number, for dest="${rawDest}".`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -193,7 +254,7 @@ class PDFLinkService {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._goToDestinationHelper(dest, namedDest, explicitDest);
|
this.#goToDestinationHelper(dest, namedDest, explicitDest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,7 +291,7 @@ class PDFLinkService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around the `addLinkAttributes`-function in the API.
|
* Wrapper around the `addLinkAttributes` helper function.
|
||||||
* @param {HTMLAnchorElement} link
|
* @param {HTMLAnchorElement} link
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {boolean} [newWindow]
|
* @param {boolean} [newWindow]
|
||||||
@ -340,8 +401,7 @@ class PDFLinkService {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(
|
||||||
`PDFLinkService.setHash: "${zoomArg}" is not ` +
|
`PDFLinkService.setHash: "${zoomArg}" is not a valid zoom value.`
|
||||||
"a valid zoom value."
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,13 +439,17 @@ class PDFLinkService {
|
|||||||
}
|
}
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
|
|
||||||
if (typeof dest === "string" || isValidExplicitDestination(dest)) {
|
if (
|
||||||
|
typeof dest === "string" ||
|
||||||
|
PDFLinkService.#isValidExplicitDestination(dest)
|
||||||
|
) {
|
||||||
this.goToDestination(dest);
|
this.goToDestination(dest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.error(
|
console.error(
|
||||||
`PDFLinkService.setHash: "${unescape(hash)}" is not ` +
|
`PDFLinkService.setHash: "${unescape(
|
||||||
"a valid destination."
|
hash
|
||||||
|
)}" is not a valid destination.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,11 +504,11 @@ class PDFLinkService {
|
|||||||
}
|
}
|
||||||
const refStr =
|
const refStr =
|
||||||
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
|
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
|
||||||
this._pagesRefCache[refStr] = pageNum;
|
this.#pagesRefCache.set(refStr, pageNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @ignore
|
||||||
*/
|
*/
|
||||||
_cachedPageNumber(pageRef) {
|
_cachedPageNumber(pageRef) {
|
||||||
if (!pageRef) {
|
if (!pageRef) {
|
||||||
@ -452,7 +516,7 @@ class PDFLinkService {
|
|||||||
}
|
}
|
||||||
const refStr =
|
const refStr =
|
||||||
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
|
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
|
||||||
return this._pagesRefCache?.[refStr] || null;
|
return this.#pagesRefCache.get(refStr) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -468,65 +532,65 @@ class PDFLinkService {
|
|||||||
isPageCached(pageNumber) {
|
isPageCached(pageNumber) {
|
||||||
return this.pdfViewer.isPageCached(pageNumber);
|
return this.pdfViewer.isPageCached(pageNumber);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function isValidExplicitDestination(dest) {
|
static #isValidExplicitDestination(dest) {
|
||||||
if (!Array.isArray(dest)) {
|
if (!Array.isArray(dest)) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const destLength = dest.length;
|
|
||||||
if (destLength < 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const page = dest[0];
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
typeof page === "object" &&
|
|
||||||
Number.isInteger(page.num) &&
|
|
||||||
Number.isInteger(page.gen)
|
|
||||||
) &&
|
|
||||||
!(Number.isInteger(page) && page >= 0)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const zoom = dest[1];
|
|
||||||
if (!(typeof zoom === "object" && typeof zoom.name === "string")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let allowNull = true;
|
|
||||||
switch (zoom.name) {
|
|
||||||
case "XYZ":
|
|
||||||
if (destLength !== 5) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Fit":
|
|
||||||
case "FitB":
|
|
||||||
return destLength === 2;
|
|
||||||
case "FitH":
|
|
||||||
case "FitBH":
|
|
||||||
case "FitV":
|
|
||||||
case "FitBV":
|
|
||||||
if (destLength !== 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "FitR":
|
|
||||||
if (destLength !== 6) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
allowNull = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (let i = 2; i < destLength; i++) {
|
|
||||||
const param = dest[i];
|
|
||||||
if (!(typeof param === "number" || (allowNull && param === null))) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const destLength = dest.length;
|
||||||
|
if (destLength < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const page = dest[0];
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
typeof page === "object" &&
|
||||||
|
Number.isInteger(page.num) &&
|
||||||
|
Number.isInteger(page.gen)
|
||||||
|
) &&
|
||||||
|
!(Number.isInteger(page) && page >= 0)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const zoom = dest[1];
|
||||||
|
if (!(typeof zoom === "object" && typeof zoom.name === "string")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let allowNull = true;
|
||||||
|
switch (zoom.name) {
|
||||||
|
case "XYZ":
|
||||||
|
if (destLength !== 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Fit":
|
||||||
|
case "FitB":
|
||||||
|
return destLength === 2;
|
||||||
|
case "FitH":
|
||||||
|
case "FitBH":
|
||||||
|
case "FitV":
|
||||||
|
case "FitBV":
|
||||||
|
if (destLength !== 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "FitR":
|
||||||
|
if (destLength !== 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
allowNull = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 2; i < destLength; i++) {
|
||||||
|
const param = dest[i];
|
||||||
|
if (!(typeof param === "number" || (allowNull && param === null))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -634,4 +698,4 @@ class SimpleLinkService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { PDFLinkService, SimpleLinkService };
|
export { LinkTarget, PDFLinkService, SimpleLinkService };
|
||||||
|
@ -19,7 +19,11 @@ import {
|
|||||||
DefaultTextLayerFactory,
|
DefaultTextLayerFactory,
|
||||||
DefaultXfaLayerFactory,
|
DefaultXfaLayerFactory,
|
||||||
} from "./default_factory.js";
|
} from "./default_factory.js";
|
||||||
import { PDFLinkService, SimpleLinkService } from "./pdf_link_service.js";
|
import {
|
||||||
|
LinkTarget,
|
||||||
|
PDFLinkService,
|
||||||
|
SimpleLinkService,
|
||||||
|
} from "./pdf_link_service.js";
|
||||||
import { PDFSinglePageViewer, PDFViewer } from "./pdf_viewer.js";
|
import { PDFSinglePageViewer, PDFViewer } from "./pdf_viewer.js";
|
||||||
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
||||||
import { DownloadManager } from "./download_manager.js";
|
import { DownloadManager } from "./download_manager.js";
|
||||||
@ -49,6 +53,7 @@ export {
|
|||||||
DownloadManager,
|
DownloadManager,
|
||||||
EventBus,
|
EventBus,
|
||||||
GenericL10n,
|
GenericL10n,
|
||||||
|
LinkTarget,
|
||||||
NullL10n,
|
NullL10n,
|
||||||
PDFFindController,
|
PDFFindController,
|
||||||
PDFHistory,
|
PDFHistory,
|
||||||
|
@ -200,6 +200,24 @@ function parseQueryString(query) {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NullCharactersRegExp = /\x00/g;
|
||||||
|
const InvisibleCharactersRegExp = /[\x01-\x1F]/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str
|
||||||
|
* @param {boolean} [replaceInvisible]
|
||||||
|
*/
|
||||||
|
function removeNullCharacters(str, replaceInvisible = false) {
|
||||||
|
if (typeof str !== "string") {
|
||||||
|
console.error(`The argument must be a string.`);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
if (replaceInvisible) {
|
||||||
|
str = str.replace(InvisibleCharactersRegExp, " ");
|
||||||
|
}
|
||||||
|
return str.replace(NullCharactersRegExp, "");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use binary search to find the index of the first item in a given array which
|
* Use binary search to find the index of the first item in a given array which
|
||||||
* passes a given condition. The items are expected to be sorted in the sense
|
* passes a given condition. The items are expected to be sorted in the sense
|
||||||
@ -838,6 +856,7 @@ export {
|
|||||||
parseQueryString,
|
parseQueryString,
|
||||||
PresentationModeState,
|
PresentationModeState,
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
|
removeNullCharacters,
|
||||||
RendererType,
|
RendererType,
|
||||||
RenderingStates,
|
RenderingStates,
|
||||||
roundToDivide,
|
roundToDivide,
|
||||||
|
Loading…
Reference in New Issue
Block a user