Vanessa 2023-03-20 15:56:24 +08:00
parent 7ad5cdd067
commit 23f2af3501
57 changed files with 83865 additions and 2979 deletions

View file

@ -531,7 +531,7 @@ export const getHighlight = (element: HTMLElement) => {
const showHighlight = (selected: IPdfAnno, pdf: any, hl?: boolean) => {
const pageIndex = selected.index;
const page = pdf.pdfViewer.getPageView(pageIndex);
let textLayerElement = page.textLayer.textLayerDiv;
let textLayerElement = page.textLayer.div;
if (!textLayerElement.lastElementChild) {
return;
}

View file

@ -284,6 +284,8 @@ export class Asset extends Model {
<svg><use xlink:href="#iconPlay"></use></svg>
</button>
<span id="scrollPage" class="fn__none"></span>
<span id="print" class="fn__none"></span>
<span id="secondaryPrint" class="fn__none"></span>
<span id="viewBookmark" class="fn__none"></span>
<span id="secondaryViewBookmark" class="fn__none"></span>
<button id="secondaryToolbarToggle" class="toolbarButton b3-tooltips b3-tooltips__sw" aria-label="${window.siyuan.languages.more}" tabindex="36" aria-expanded="false" aria-controls="secondaryToolbar">

View file

@ -16,12 +16,9 @@
/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */
// eslint-disable-next-line max-len
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
// eslint-disable-next-line max-len
/** @typedef {import("../annotation_storage.js").AnnotationStorage} AnnotationStorage */
// eslint-disable-next-line max-len
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
/** @typedef {import("./interfaces").IL10n} IL10n */
@ -30,13 +27,11 @@ import { NullL10n } from "./l10n_utils.js";
/**
* @typedef {Object} AnnotationEditorLayerBuilderOptions
* @property {number} mode - Editor mode
* @property {AnnotationEditorUIManager} [uiManager]
* @property {HTMLDivElement} pageDiv
* @property {PDFPageProxy} pdfPage
* @property {TextAccessibilityManager} accessibilityManager
* @property {AnnotationStorage} annotationStorage
* @property {IL10n} l10n - Localization service.
* @property {AnnotationEditorUIManager} uiManager
* @property {IL10n} [l10n]
* @property {TextAccessibilityManager} [accessibilityManager]
*/
class AnnotationEditorLayerBuilder {
@ -48,7 +43,6 @@ class AnnotationEditorLayerBuilder {
constructor(options) {
this.pageDiv = options.pageDiv;
this.pdfPage = options.pdfPage;
this.annotationStorage = options.annotationStorage || null;
this.accessibilityManager = options.accessibilityManager;
this.l10n = options.l10n || NullL10n;
this.annotationEditorLayer = null;
@ -78,34 +72,41 @@ class AnnotationEditorLayerBuilder {
}
// Create an AnnotationEditor layer div
this.div = document.createElement("div");
this.div.className = "annotationEditorLayer";
this.div.tabIndex = 0;
this.pageDiv.append(this.div);
const div = (this.div = document.createElement("div"));
div.className = "annotationEditorLayer";
div.tabIndex = 0;
div.hidden = true;
this.pageDiv.append(div);
this.annotationEditorLayer = new AnnotationEditorLayer({
uiManager: this.#uiManager,
div: this.div,
annotationStorage: this.annotationStorage,
div,
accessibilityManager: this.accessibilityManager,
pageIndex: this.pdfPage._pageIndex,
pageIndex: this.pdfPage.pageNumber - 1,
l10n: this.l10n,
viewport: clonedViewport,
});
const parameters = {
viewport: clonedViewport,
div: this.div,
div,
annotations: null,
intent,
};
this.annotationEditorLayer.render(parameters);
this.show();
}
cancel() {
this._cancelled = true;
this.destroy();
if (!this.div) {
return;
}
this.pageDiv = null;
this.annotationEditorLayer.destroy();
this.div.remove();
}
hide() {
@ -116,20 +117,11 @@ class AnnotationEditorLayerBuilder {
}
show() {
if (!this.div) {
if (!this.div || this.annotationEditorLayer.isEmpty) {
return;
}
this.div.hidden = false;
}
destroy() {
if (!this.div) {
return;
}
this.pageDiv = null;
this.annotationEditorLayer.destroy();
this.div.remove();
}
}
export { AnnotationEditorLayerBuilder };

View file

@ -24,6 +24,7 @@
import { AnnotationLayer } from "./pdfjs";
import { NullL10n } from "./l10n_utils.js";
import { PresentationModeState } from "./ui_utils.js";
/**
* @typedef {Object} AnnotationLayerBuilderOptions
@ -40,12 +41,15 @@ import { NullL10n } from "./l10n_utils.js";
* @property {Promise<boolean>} [hasJSActionsPromise]
* @property {Promise<Object<string, Array<Object>> | null>}
* [fieldObjectsPromise]
* @property {Object} [mouseState]
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap]
* @property {TextAccessibilityManager} accessibilityManager
* @property {TextAccessibilityManager} [accessibilityManager]
*/
class AnnotationLayerBuilder {
#numAnnotations = 0;
#onPresentationModeChanged = null;
/**
* @param {AnnotationLayerBuilderOptions} options
*/
@ -61,7 +65,6 @@ class AnnotationLayerBuilder {
enableScripting = false,
hasJSActionsPromise = null,
fieldObjectsPromise = null,
mouseState = null,
annotationCanvasMap = null,
accessibilityManager = null,
}) {
@ -74,14 +77,14 @@ class AnnotationLayerBuilder {
this.l10n = l10n;
this.annotationStorage = annotationStorage;
this.enableScripting = enableScripting;
this._hasJSActionsPromise = hasJSActionsPromise;
this._fieldObjectsPromise = fieldObjectsPromise;
this._mouseState = mouseState;
this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false);
this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null);
this._annotationCanvasMap = annotationCanvasMap;
this._accessibilityManager = accessibilityManager;
this.div = null;
this._cancelled = false;
this._eventBus = linkService.eventBus;
}
/**
@ -91,18 +94,41 @@ class AnnotationLayerBuilder {
* annotations is complete.
*/
async render(viewport, intent = "display") {
const [annotations, hasJSActions = false, fieldObjects = null] =
await Promise.all([
this.pdfPage.getAnnotations({ intent }),
this._hasJSActionsPromise,
this._fieldObjectsPromise,
]);
if (this._cancelled || annotations.length === 0) {
if (this.div) {
if (this._cancelled || this.#numAnnotations === 0) {
return;
}
// If an annotationLayer already exists, refresh its children's
// transformation matrices.
AnnotationLayer.update({
viewport: viewport.clone({ dontFlip: true }),
div: this.div,
annotationCanvasMap: this._annotationCanvasMap,
});
return;
}
const parameters = {
const [annotations, hasJSActions, fieldObjects] = await Promise.all([
this.pdfPage.getAnnotations({ intent }),
this._hasJSActionsPromise,
this._fieldObjectsPromise,
]);
if (this._cancelled) {
return;
}
this.#numAnnotations = annotations.length;
// Create an annotation layer div and render the annotations
// if there is at least one annotation.
this.div = document.createElement("div");
this.div.className = "annotationLayer";
this.pageDiv.append(this.div);
if (this.#numAnnotations === 0) {
this.hide();
return;
}
AnnotationLayer.render({
viewport: viewport.clone({ dontFlip: true }),
div: this.div,
annotations,
@ -115,30 +141,37 @@ class AnnotationLayerBuilder {
enableScripting: this.enableScripting,
hasJSActions,
fieldObjects,
mouseState: this._mouseState,
annotationCanvasMap: this._annotationCanvasMap,
accessibilityManager: this._accessibilityManager,
};
});
// NOTE this.l10n.translate(this.div);
if (this.div) {
// If an annotationLayer already exists, refresh its children's
// transformation matrices.
AnnotationLayer.update(parameters);
} else {
// Create an annotation layer div and render the annotations
// if there is at least one annotation.
this.div = document.createElement("div");
this.div.className = "annotationLayer";
this.pageDiv.append(this.div);
parameters.div = this.div;
AnnotationLayer.render(parameters);
this.l10n.translate(this.div);
// Ensure that interactive form elements in the annotationLayer are
// disabled while PresentationMode is active (see issue 12232).
if (this.linkService.isInPresentationMode) {
this.#updatePresentationModeState(PresentationModeState.FULLSCREEN);
}
if (!this.#onPresentationModeChanged) {
this.#onPresentationModeChanged = evt => {
this.#updatePresentationModeState(evt.state);
};
this._eventBus?._on(
"presentationmodechanged",
this.#onPresentationModeChanged
);
}
}
cancel() {
this._cancelled = true;
if (this.#onPresentationModeChanged) {
this._eventBus?._off(
"presentationmodechanged",
this.#onPresentationModeChanged
);
this.#onPresentationModeChanged = null;
}
}
hide() {
@ -147,6 +180,29 @@ class AnnotationLayerBuilder {
}
this.div.hidden = true;
}
#updatePresentationModeState(state) {
if (!this.div) {
return;
}
let disableFormElements = false;
switch (state) {
case PresentationModeState.FULLSCREEN:
disableFormElements = true;
break;
case PresentationModeState.NORMAL:
break;
default:
return;
}
for (const section of this.div.childNodes) {
if (section.hasAttribute("data-internal-link")) {
continue;
}
section.inert = disableFormElements;
}
}
}
export { AnnotationLayerBuilder };

File diff suppressed because it is too large Load diff

View file

@ -12,34 +12,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Constants } from '../../constants'
const compatibilityParams = Object.create(null)
if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
const compatibilityParams = Object.create(null);
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
if (
typeof PDFJSDev !== 'undefined' &&
PDFJSDev.test('LIB') &&
typeof navigator === 'undefined'
typeof PDFJSDev !== "undefined" &&
PDFJSDev.test("LIB") &&
typeof navigator === "undefined"
) {
globalThis.navigator = Object.create(null)
globalThis.navigator = Object.create(null);
}
const userAgent = navigator.userAgent || ''
const platform = navigator.platform || ''
const maxTouchPoints = navigator.maxTouchPoints || 1
const userAgent = navigator.userAgent || "";
const platform = navigator.platform || "";
const maxTouchPoints = navigator.maxTouchPoints || 1;
const isAndroid = /Android/.test(userAgent)
const isAndroid = /Android/.test(userAgent);
const isIOS =
/\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) ||
(platform === 'MacIntel' && maxTouchPoints > 1);
(platform === "MacIntel" && maxTouchPoints > 1);
// Limit canvas size to 5 mega-pixels on mobile.
// Support: Android, iOS
(function checkCanvasSizeLimitation () {
(function checkCanvasSizeLimitation() {
if (isIOS || isAndroid) {
compatibilityParams.maxCanvasPixels = 5242880
compatibilityParams.maxCanvasPixels = 5242880;
}
})()
})();
}
const OptionKind = {
@ -47,7 +46,7 @@ const OptionKind = {
API: 0x04,
WORKER: 0x08,
PREFERENCE: 0x80,
}
};
/**
* NOTE: These options are used to generate the `default_preferences.json` file,
@ -70,9 +69,14 @@ const defaultOptions = {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
defaultZoomDelay: {
/** @type {number} */
value: 400,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
defaultZoomValue: {
/** @type {string} */
value: '',
value: "",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
disableHistory: {
@ -97,12 +101,12 @@ const defaultOptions = {
},
enableScripting: {
/** @type {boolean} */
value: typeof PDFJSDev === 'undefined' || !PDFJSDev.test('CHROME'),
value: typeof PDFJSDev === "undefined" || !PDFJSDev.test("CHROME"),
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
externalLinkRel: {
/** @type {string} */
value: 'noopener noreferrer nofollow',
value: "noopener noreferrer nofollow",
kind: OptionKind.VIEWER,
},
externalLinkTarget: {
@ -122,7 +126,7 @@ const defaultOptions = {
},
imageResourcesPath: {
/** @type {string} */
value: './images/',
value: "./images/",
kind: OptionKind.VIEWER,
},
maxCanvasPixels: {
@ -137,17 +141,17 @@ const defaultOptions = {
},
pageColorsBackground: {
/** @type {string} */
value: 'Canvas',
value: "Canvas",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
pageColorsForeground: {
/** @type {string} */
value: 'CanvasText',
value: "CanvasText",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
pdfBugEnabled: {
/** @type {boolean} */
value: typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION'),
value: typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION"),
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
printResolution: {
@ -182,7 +186,7 @@ const defaultOptions = {
},
viewerCssTheme: {
/** @type {number} */
value: typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME') ? 2 : 0,
value: typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME") ? 2 : 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
viewOnLoad: {
@ -223,7 +227,7 @@ const defaultOptions = {
},
docBaseUrl: {
/** @type {string} */
value: '',
value: "",
kind: OptionKind.API,
},
enableXfa: {
@ -241,6 +245,11 @@ const defaultOptions = {
value: true,
kind: OptionKind.API,
},
isOffscreenCanvasSupported: {
/** @type {boolean} */
value: true,
kind: OptionKind.API,
},
maxImageSize: {
/** @type {number} */
value: -1,
@ -253,10 +262,7 @@ const defaultOptions = {
},
standardFontDataUrl: {
/** @type {string} */
value:
typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')
? '../external/standard_fonts/'
: '../web/standard_fonts/',
value: "standard_fonts/", // NOTE
kind: OptionKind.API,
},
verbosity: {
@ -273,131 +279,130 @@ const defaultOptions = {
workerSrc: {
/** @type {string} */
// NOTE
value: `${Constants.PROTYLE_CDN}/js/pdf/pdf.worker.js?v=3.0.150`,
value: `${Constants.PROTYLE_CDN}/js/pdf/pdf.worker.js?v=3.4.120`,
kind: OptionKind.WORKER,
},
}
};
if (
typeof PDFJSDev === 'undefined' ||
PDFJSDev.test('!PRODUCTION || GENERIC')
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || GENERIC")
) {
defaultOptions.defaultUrl = {
/** @type {string} */
value: 'compressed.tracemonkey-pldi-09.pdf',
value: "compressed.tracemonkey-pldi-09.pdf",
kind: OptionKind.VIEWER,
}
};
defaultOptions.disablePreferences = {
/** @type {boolean} */
value: typeof PDFJSDev !== 'undefined' && PDFJSDev.test('TESTING'),
value: typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING"),
kind: OptionKind.VIEWER,
}
};
defaultOptions.locale = {
/** @type {string} */
value: navigator.language || 'en-US',
value: navigator.language || "en-US",
kind: OptionKind.VIEWER,
}
};
defaultOptions.renderer = {
/** @type {string} */
value: 'canvas',
value: "canvas",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
}
};
defaultOptions.sandboxBundleSrc = {
/** @type {string} */
value:
typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')
? '../build/dev-sandbox/pdf.sandbox.js'
: '../build/pdf.sandbox.js',
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
? "../build/dev-sandbox/pdf.sandbox.js"
: "../build/pdf.sandbox.js",
kind: OptionKind.VIEWER,
}
} else if (PDFJSDev.test('CHROME')) {
};
} else if (PDFJSDev.test("CHROME")) {
defaultOptions.defaultUrl = {
/** @type {string} */
value: '',
value: "",
kind: OptionKind.VIEWER,
}
};
defaultOptions.disableTelemetry = {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
}
};
defaultOptions.sandboxBundleSrc = {
/** @type {string} */
value: '../build/pdf.sandbox.js',
value: "../build/pdf.sandbox.js",
kind: OptionKind.VIEWER,
}
};
}
const userOptions = Object.create(null)
const userOptions = Object.create(null);
class AppOptions {
constructor () {
throw new Error('Cannot initialize AppOptions.')
constructor() {
throw new Error("Cannot initialize AppOptions.");
}
static get (name) {
const userOption = userOptions[name]
static get(name) {
const userOption = userOptions[name];
if (userOption !== undefined) {
return userOption
return userOption;
}
const defaultOption = defaultOptions[name]
const defaultOption = defaultOptions[name];
if (defaultOption !== undefined) {
return compatibilityParams[name] ?? defaultOption.value
return compatibilityParams[name] ?? defaultOption.value;
}
return undefined
return undefined;
}
static getAll (kind = null) {
const options = Object.create(null)
static getAll(kind = null) {
const options = Object.create(null);
for (const name in defaultOptions) {
const defaultOption = defaultOptions[name]
const defaultOption = defaultOptions[name];
if (kind) {
if ((kind & defaultOption.kind) === 0) {
continue
continue;
}
if (kind === OptionKind.PREFERENCE) {
const value = defaultOption.value,
valueType = typeof value
valueType = typeof value;
if (
valueType === 'boolean' ||
valueType === 'string' ||
(valueType === 'number' && Number.isInteger(value))
valueType === "boolean" ||
valueType === "string" ||
(valueType === "number" && Number.isInteger(value))
) {
options[name] = value
continue
options[name] = value;
continue;
}
throw new Error(`Invalid type for preference: ${name}`)
throw new Error(`Invalid type for preference: ${name}`);
}
}
const userOption = userOptions[name]
const userOption = userOptions[name];
options[name] =
userOption !== undefined
? userOption
: compatibilityParams[name] ?? defaultOption.value
: compatibilityParams[name] ?? defaultOption.value;
}
return options
return options;
}
static set (name, value) {
userOptions[name] = value
static set(name, value) {
userOptions[name] = value;
}
static setAll (options) {
static setAll(options) {
for (const name in options) {
userOptions[name] = options[name]
userOptions[name] = options[name];
}
}
static remove (name) {
delete userOptions[name]
}
/**
* @ignore
*/
static _hasUserOptions () {
return Object.keys(userOptions).length > 0
static remove(name) {
delete userOptions[name];
}
}
export { AppOptions, compatibilityParams, OptionKind }
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
AppOptions._hasUserOptions = function () {
return Object.keys(userOptions).length > 0;
};
}
export { AppOptions, compatibilityParams, OptionKind };

View file

@ -75,9 +75,7 @@ function waitOnEventOrTimeout({ target, name, delay = 0 }) {
* and `off` methods. To raise an event, the `dispatch` method shall be used.
*/
class EventBus {
constructor() {
this._listeners = Object.create(null);
}
#listeners = Object.create(null);
/**
* @param {string} eventName
@ -108,7 +106,7 @@ class EventBus {
* @param {Object} data
*/
dispatch(eventName, data) {
const eventListeners = this._listeners[eventName];
const eventListeners = this.#listeners[eventName];
if (!eventListeners || eventListeners.length === 0) {
return;
}
@ -139,7 +137,7 @@ class EventBus {
* @ignore
*/
_on(eventName, listener, options = null) {
const eventListeners = (this._listeners[eventName] ||= []);
const eventListeners = (this.#listeners[eventName] ||= []);
eventListeners.push({
listener,
external: options?.external === true,
@ -151,7 +149,7 @@ class EventBus {
* @ignore
*/
_off(eventName, listener, options = null) {
const eventListeners = this._listeners[eventName];
const eventListeners = this.#listeners[eventName];
if (!eventListeners) {
return;
}

View file

@ -14,10 +14,8 @@
*/
import { BasePreferences } from "./preferences.js";
// NOTE
// import { DownloadManager } from "./download_manager.js";
import { GenericScripting } from "./generic_scripting.js";
import { shadow } from './pdfjs'
import {shadow} from "./pdfjs";
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) {
throw new Error(
@ -37,44 +35,48 @@ class GenericPreferences extends BasePreferences {
}
}
// NOTE
class GenericExternalServices {
constructor () {
throw new Error('Cannot initialize DefaultExternalServices.')
class DefaultExternalServices {
constructor() {
throw new Error("Cannot initialize DefaultExternalServices.");
}
static updateFindControlState (data) {}
static updateFindControlState(data) {}
static updateFindMatchesCount (data) {}
static updateFindMatchesCount(data) {}
static initPassiveLoading (callbacks) {}
static initPassiveLoading(callbacks) {}
static async fallback (data) {}
static reportTelemetry(data) {}
static reportTelemetry (data) {}
static get supportsIntegratedFind () {
return shadow(this, 'supportsIntegratedFind', false)
static get supportsPinchToZoom() {
return shadow(this, "supportsPinchToZoom", true);
}
static get supportsDocumentFonts () {
return shadow(this, 'supportsDocumentFonts', true)
static get supportsIntegratedFind() {
return shadow(this, "supportsIntegratedFind", false);
}
static get supportedMouseWheelZoomModifierKeys () {
return shadow(this, 'supportedMouseWheelZoomModifierKeys', {
static get supportsDocumentFonts() {
return shadow(this, "supportsDocumentFonts", true);
}
static get supportedMouseWheelZoomModifierKeys() {
return shadow(this, "supportedMouseWheelZoomModifierKeys", {
ctrlKey: true,
metaKey: true,
})
});
}
static get isInAutomation () {
return shadow(this, 'isInAutomation', false)
static get isInAutomation() {
return shadow(this, "isInAutomation", false);
}
static createDownloadManager(options) {
// NOTE
// return new DownloadManager();
static updateEditorStates(data) {
throw new Error("Not implemented: updateEditorStates");
}
static createDownloadManager() {
// NOTE return new DownloadManager();
}
static createPreferences() {
@ -82,13 +84,15 @@ class GenericExternalServices {
}
static createL10n({ locale = "en-US" }) {
// NOTE
// return new GenericL10n(locale);
// NOTE return new GenericL10n(locale);
}
static createScripting({ sandboxBundleSrc }) {
return new GenericScripting(sandboxBundleSrc);
}
}
// NOTE
export { GenericCom, GenericExternalServices };
// PDFViewerApplication.externalServices = GenericExternalServices;
export { GenericCom, DefaultExternalServices };

View file

@ -38,12 +38,6 @@ const DEFAULT_L10N_STRINGS = {
document_properties_linearized_yes: "Yes",
document_properties_linearized_no: "No",
print_progress_percent: "{{progress}}%",
"toggle_sidebar.title": "Toggle Sidebar",
"toggle_sidebar_notification2.title":
"Toggle Sidebar (document contains outline/attachments/layers)",
additional_layers: "Additional Layers",
page_landmark: "Page {{page}}",
thumb_page_title: "Page {{page}}",
@ -57,24 +51,17 @@ const DEFAULT_L10N_STRINGS = {
"find_match_count_limit[other]": "More than {{limit}} matches",
find_not_found: "Phrase not found",
error_version_info: "PDF.js v{{version}} (build: {{build}})",
error_message: "Message: {{message}}",
error_stack: "Stack: {{stack}}",
error_file: "File: {{file}}",
error_line: "Line: {{line}}",
rendering_error: "An error occurred while rendering the page.",
page_scale_width: "Page Width",
page_scale_fit: "Page Fit",
page_scale_auto: "Automatic Zoom",
page_scale_actual: "Actual Size",
page_scale_percent: "{{scale}}%",
loading: "Loading…",
loading_error: "An error occurred while loading the PDF.",
invalid_file_error: "Invalid or corrupted PDF file.",
missing_file_error: "Missing PDF file.",
unexpected_response_error: "Unexpected server response.",
rendering_error: "An error occurred while rendering the page.",
printing_not_supported:
"Warning: Printing is not fully supported by this browser.",
@ -87,6 +74,9 @@ const DEFAULT_L10N_STRINGS = {
editor_ink2_aria_label: "Draw Editor",
editor_ink_canvas_aria_label: "User-created image",
};
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) {
DEFAULT_L10N_STRINGS.print_progress_percent = "{{progress}}%";
}
function getL10nFallback(key, args) {
switch (key) {

View file

@ -85,9 +85,7 @@ class PasswordPrompt {
this.input.focus();
}
// NOTE
this.label.textContent = window.siyuan.languages[`password_${passwordIncorrect
? 'invalid'
: 'label'}`]
this.label.textContent = window.siyuan.languages[`password_${passwordIncorrect ? 'invalid' : 'label'}`]
}
async close() {

View file

@ -118,7 +118,10 @@ class PDFAttachmentViewer extends BaseTreeViewer {
for (const name of names) {
const item = attachments[name];
const content = item.content,
filename = getFilenameFromUrl(item.filename);
filename = getFilenameFromUrl(
item.filename,
/* onlyStripPath = */ true
);
const div = document.createElement("div");
div.className = "treeItem";

View file

@ -13,15 +13,9 @@
* limitations under the License.
*/
import { CursorTool, PresentationModeState } from "./ui_utils.js";
import { AnnotationEditorType } from "./pdfjs";
import { GrabToPan } from "./grab_to_pan.js";
import { PresentationModeState } from "./ui_utils.js";
const CursorTool = {
SELECT: 0, // The default value.
HAND: 1,
ZOOM: 2,
};
/**
* @typedef {Object} PDFCursorToolsOptions
@ -175,4 +169,4 @@ class PDFCursorTools {
}
}
export { CursorTool, PDFCursorTools };
export { PDFCursorTools };

View file

@ -239,11 +239,9 @@ class PDFDocumentProperties {
}
// NOTE
if (mb >= 1) {
return `${mb >= 1 && (+mb.toPrecision(
3)).toLocaleString()} MB ${fileSize.toLocaleString()} bytes`
return `${mb >= 1 && (+mb.toPrecision(3)).toLocaleString()} MB ${fileSize.toLocaleString()} bytes`
}
return `${mb < 1 && (+kb.toPrecision(
3)).toLocaleString()} KB (${fileSize.toLocaleString()} bytes`
return `${mb < 1 && (+kb.toPrecision(3)).toLocaleString()} KB (${fileSize.toLocaleString()} bytes`
}
async #parsePageSize(pageSizeInches, pagesRotation) {
@ -313,15 +311,11 @@ class PDFDocumentProperties {
// NOTE
const [{ width, height }, unit, name, orientation] = await Promise.all([
this._isNonMetricLocale ? sizeInches : sizeMillimeters,
this._isNonMetricLocale
? window.siyuan.languages.unitInches
: window.siyuan.languages.unitMillimeters,
this._isNonMetricLocale ? window.siyuan.languages.unitInches : window.siyuan.languages.unitMillimeters,
rawName &&
window.siyuan.languages[`document_properties_page_size_name_${rawName.toLowerCase()}`],
window.siyuan.languages[`document_properties_page_size_orientation_${isPortrait
? 'portrait'
: 'landscape'}`],
])
window.siyuan.languages[`document_properties_page_size_orientation_${isPortrait ? 'portrait' : 'landscape'}`],
]);
if (name) {
return `${width.toLocaleString()} × ${height.toLocaleString()} ${unit} (${name}, ${orientation})`
}

View file

@ -149,9 +149,8 @@ class PDFFindBar {
status = "notFound";
break;
case FindState.WRAPPED:
findMsg = window.siyuan.languages.find_not_found[`find_reached_${previous
? 'top'
: 'bottom'}`]
// NOTE
findMsg = window.siyuan.languages.find_not_found[`find_reached_${previous ? 'top' : 'bottom'}`]
break;
}
this.findField.setAttribute("data-status", status);
@ -165,7 +164,7 @@ class PDFFindBar {
updateResultsCount({ current = 0, total = 0 } = {}) {
const limit = MATCHES_COUNT_LIMIT;
// // NOTE
// NOTE
let matchCountMsg = "";
if (total > 0) {

View file

@ -76,9 +76,7 @@ const DIACRITICS_EXCEPTION = new Set([
// https://www.compart.com/fr/unicode/combining/132
0x0f74,
]);
const DIACRITICS_EXCEPTION_STR = [...DIACRITICS_EXCEPTION.values()]
.map(x => String.fromCharCode(x))
.join("");
let DIACRITICS_EXCEPTION_STR; // Lazily initialized, see below.
const DIACRITICS_REG_EXP = /\p{M}+/gu;
const SPECIAL_CHARS_REG_EXP =
@ -95,6 +93,8 @@ const SYLLABLES_LENGTHS = new Map();
const FIRST_CHAR_SYLLABLES_REG_EXP =
"[\\u1100-\\u1112\\ud7a4-\\ud7af\\ud84a\\ud84c\\ud850\\ud854\\ud857\\ud85f]";
const NFKC_CHARS_TO_NORMALIZE = new Map();
let noSyllablesRegExp = null;
let withSyllablesRegExp = null;
@ -126,7 +126,18 @@ function normalize(text) {
} else {
// Compile the regular expression for text normalization once.
const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join("");
const regexp = `([${replace}])|(\\p{M}+(?:-\\n)?)|(\\S-\\n)|(\\p{Ideographic}\\n)|(\\n)`;
const toNormalizeWithNFKC =
"\u2460-\u2473" + // Circled numbers.
"\u24b6-\u24ff" + // Circled letters/numbers.
"\u3244-\u32bf" + // Circled ideograms/numbers.
"\u32d0-\u32fe" + // Circled ideograms.
"\uff00-\uffef"; // Halfwidth, fullwidth forms.
// 3040-309F: Hiragana
// 30A0-30FF: Katakana
const CJK = "(?:\\p{Ideographic}|[\u3040-\u30FF])";
const HKDiacritics = "(?:\u3099|\u309A)";
const regexp = `([${replace}])|([${toNormalizeWithNFKC}])|(${HKDiacritics}\\n)|(\\p{M}+(?:-\\n)?)|(\\S-\\n)|(${CJK}\\n)|(\\n)`;
if (syllablePositions.length === 0) {
// Most of the syllables belong to Hangul so there are no need
@ -188,11 +199,11 @@ function normalize(text) {
normalized = normalized.replace(
normalizationRegex,
(match, p1, p2, p3, p4, p5, p6, i) => {
(match, p1, p2, p3, p4, p5, p6, p7, p8, i) => {
i -= shiftOrigin;
if (p1) {
// Maybe fractions or quotations mark...
const replacement = CHARACTERS_TO_NORMALIZE[match];
const replacement = CHARACTERS_TO_NORMALIZE[p1];
const jj = replacement.length;
for (let j = 1; j < jj; j++) {
positions.push([i - shift + j, shift - j]);
@ -202,8 +213,47 @@ function normalize(text) {
}
if (p2) {
const hasTrailingDashEOL = p2.endsWith("\n");
const len = hasTrailingDashEOL ? p2.length - 2 : p2.length;
// Use the NFKC representation to normalize the char.
let replacement = NFKC_CHARS_TO_NORMALIZE.get(p2);
if (!replacement) {
replacement = p2.normalize("NFKC");
NFKC_CHARS_TO_NORMALIZE.set(p2, replacement);
}
const jj = replacement.length;
for (let j = 1; j < jj; j++) {
positions.push([i - shift + j, shift - j]);
}
shift -= jj - 1;
return replacement;
}
if (p3) {
// We've a Katakana-Hiragana diacritic followed by a \n so don't replace
// the \n by a whitespace.
hasDiacritics = true;
// Diacritic.
if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) {
++rawDiacriticsIndex;
} else {
// i is the position of the first diacritic
// so (i - 1) is the position for the letter before.
positions.push([i - 1 - shift + 1, shift - 1]);
shift -= 1;
shiftOrigin += 1;
}
// End-of-line.
positions.push([i - shift + 1, shift]);
shiftOrigin += 1;
eol += 1;
return p3.charAt(0);
}
if (p4) {
const hasTrailingDashEOL = p4.endsWith("\n");
const len = hasTrailingDashEOL ? p4.length - 2 : p4.length;
// Diacritics.
hasDiacritics = true;
@ -223,19 +273,19 @@ function normalize(text) {
if (hasTrailingDashEOL) {
// Diacritics are followed by a -\n.
// See comments in `if (p3)` block.
// See comments in `if (p5)` block.
i += len - 1;
positions.push([i - shift + 1, 1 + shift]);
shift += 1;
shiftOrigin += 1;
eol += 1;
return p2.slice(0, len);
return p4.slice(0, len);
}
return p2;
return p4;
}
if (p3) {
if (p5) {
// "X-\n" is removed because an hyphen at the end of a line
// with not a space before is likely here to mark a break
// in a word.
@ -244,19 +294,19 @@ function normalize(text) {
shift += 1;
shiftOrigin += 1;
eol += 1;
return p3.charAt(0);
return p5.charAt(0);
}
if (p4) {
if (p6) {
// An ideographic at the end of a line doesn't imply adding an extra
// white space.
positions.push([i - shift + 1, shift]);
shiftOrigin += 1;
eol += 1;
return p4.charAt(0);
return p6.charAt(0);
}
if (p5) {
if (p7) {
// eol is replaced by space: "foo\nbar" is likely equivalent to
// "foo bar".
positions.push([i - shift + 1, shift - 1]);
@ -266,7 +316,7 @@ function normalize(text) {
return " ";
}
// p6
// p8
if (i + eol === syllablePositions[syllableIndex]?.[1]) {
// A syllable (1 char) is replaced with several chars (n) so
// newCharsLen = n - 1.
@ -278,7 +328,7 @@ function normalize(text) {
shift -= newCharLen;
shiftOrigin += newCharLen;
}
return p6;
return p8;
}
);
@ -314,18 +364,26 @@ function getOriginalIndex(diffs, pos, len) {
* @typedef {Object} PDFFindControllerOptions
* @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {EventBus} eventBus - The application event bus.
* @property {boolean} updateMatchesCountOnProgress - True if the matches
* count must be updated on progress or only when the last page is reached.
* The default value is `true`.
*/
/**
* Provides search functionality to find a given string in a PDF document.
*/
class PDFFindController {
#updateMatchesCountOnProgress = true;
#visitedPagesCount = 0;
/**
* @param {PDFFindControllerOptions} options
*/
constructor({ linkService, eventBus }) {
constructor({ linkService, eventBus, updateMatchesCountOnProgress = true }) {
this._linkService = linkService;
this._eventBus = eventBus;
this.#updateMatchesCountOnProgress = updateMatchesCountOnProgress;
this.#reset();
eventBus._on("find", this.#onFind.bind(this));
@ -464,6 +522,7 @@ class PDFFindController {
this._pdfDocument = null;
this._pageMatches = [];
this._pageMatchesLength = [];
this.#visitedPagesCount = 0;
this._state = null;
// Currently selected match.
this._selected = {
@ -566,9 +625,13 @@ class PDFFindController {
}
#calculateRegExpMatch(query, entireWord, pageIndex, pageContent) {
const matches = [],
matchesLength = [];
const matches = (this._pageMatches[pageIndex] = []);
const matchesLength = (this._pageMatchesLength[pageIndex] = []);
if (!query) {
// The query can be empty because some chars like diacritics could have
// been stripped out.
return;
}
const diffs = this._pageDiffs[pageIndex];
let match;
while ((match = query.exec(pageContent)) !== null) {
@ -590,8 +653,6 @@ class PDFFindController {
matchesLength.push(matchLen);
}
}
this._pageMatches[pageIndex] = matches;
this._pageMatchesLength[pageIndex] = matchesLength;
}
#convertToRegExpString(query, hasDiacritics) {
@ -652,6 +713,10 @@ class PDFFindController {
if (matchDiacritics) {
// aX must not match aXY.
if (hasDiacritics) {
DIACRITICS_EXCEPTION_STR ||= String.fromCharCode(
...DIACRITICS_EXCEPTION
);
isUnicode = true;
query = `${query}(?=[${DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`;
}
@ -662,7 +727,7 @@ class PDFFindController {
#calculateMatch(pageIndex) {
let query = this.#query;
if (query.length === 0) {
if (!query) {
// Do nothing: the matches should be wiped out already.
return;
}
@ -695,7 +760,7 @@ class PDFFindController {
}
const flags = `g${isUnicode ? "u" : ""}${caseSensitive ? "" : "i"}`;
query = new RegExp(query, flags);
query = query ? new RegExp(query, flags) : null;
this.#calculateRegExpMatch(query, entireWord, pageIndex, pageContent);
@ -711,8 +776,14 @@ class PDFFindController {
// Update the match count.
const pageMatchesCount = this._pageMatches[pageIndex].length;
if (pageMatchesCount > 0) {
this._matchesCountTotal += pageMatchesCount;
this._matchesCountTotal += pageMatchesCount;
if (this.#updateMatchesCountOnProgress) {
if (pageMatchesCount > 0) {
this.#updateUIResultsCount();
}
} else if (++this.#visitedPagesCount === this._linkService.pagesCount) {
// For example, in GeckoView we want to have only the final update because
// the Java side provides only one object to update the counts.
this.#updateUIResultsCount();
}
}
@ -807,6 +878,7 @@ class PDFFindController {
this._resumePageIdx = null;
this._pageMatches.length = 0;
this._pageMatchesLength.length = 0;
this.#visitedPagesCount = 0;
this._matchesCountTotal = 0;
this.#updateAllPages(); // Wipe out any previously highlighted matches.
@ -825,7 +897,7 @@ class PDFFindController {
}
// If there's no query there's no point in searching.
if (this.#query === "") {
if (!this.#query) {
this.#updateUIState(FindState.FOUND);
return;
}
@ -1005,6 +1077,16 @@ class PDFFindController {
}
#updateUIState(state, previous = false) {
if (
!this.#updateMatchesCountOnProgress &&
(this.#visitedPagesCount !== this._linkService.pagesCount ||
state === FindState.PENDING)
) {
// When this.#updateMatchesCountOnProgress is false we only send an update
// when everything is ready.
return;
}
this._eventBus.dispatch("updatefindcontrolstate", {
source: this,
state,

View file

@ -173,6 +173,13 @@ class PDFLinkService {
this.pdfViewer.pagesRotation = value;
}
/**
* @type {boolean}
*/
get isInPresentationMode() {
return this.pdfViewer.isInPresentationMode;
}
#goToDestinationHelper(rawDest, namedDest = null, explicitDest) {
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
const destRef = explicitDest[0];
@ -673,6 +680,13 @@ class SimpleLinkService {
*/
set rotation(value) {}
/**
* @type {boolean}
*/
get isInPresentationMode() {
return false;
}
/**
* @param {string|Array} dest - The named, or explicit, PDF destination.
*/

View file

@ -20,8 +20,9 @@ import { SidebarView } from "./ui_utils.js";
/**
* @typedef {Object} PDFOutlineViewerOptions
* @property {HTMLDivElement} container - The viewer element.
* @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {EventBus} eventBus - The application event bus.
* @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {DownloadManager} downloadManager - The download manager.
*/
/**
@ -37,6 +38,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
constructor(options) {
super(options);
this.linkService = options.linkService;
this.downloadManager = options.downloadManager;
this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this));
this.eventBus._on(
@ -109,7 +111,10 @@ class PDFOutlineViewer extends BaseTreeViewer {
/**
* @private
*/
_bindLink(element, { url, newWindow, action, dest, setOCGState }) {
_bindLink(
element,
{ url, newWindow, action, attachment, dest, setOCGState }
) {
const { linkService } = this;
if (url) {
@ -124,6 +129,18 @@ class PDFOutlineViewer extends BaseTreeViewer {
};
return;
}
if (attachment) {
element.href = linkService.getAnchorUrl("");
element.onclick = () => {
this.downloadManager.openOrDownloadData(
element,
attachment.content,
attachment.filename
);
return false;
};
return;
}
if (setOCGState) {
element.href = linkService.getAnchorUrl("");
element.onclick = () => {

File diff suppressed because it is too large Load diff

View file

@ -224,21 +224,24 @@ class PDFPresentationMode {
evt.preventDefault();
return;
}
if (evt.button === 0) {
// Enable clicking of links in presentation mode. Note: only links
// pointing to destinations in the current PDF document work.
const isInternalLink =
evt.target.href && evt.target.classList.contains("internalLink");
if (!isInternalLink) {
// Unless an internal link was clicked, advance one page.
evt.preventDefault();
if (evt.button !== 0) {
return;
}
// Enable clicking of links in presentation mode. Note: only links
// pointing to destinations in the current PDF document work.
if (
evt.target.href &&
evt.target.parentNode?.hasAttribute("data-internal-link")
) {
return;
}
// Unless an internal link was clicked, advance one page.
evt.preventDefault();
if (evt.shiftKey) {
this.pdfViewer.previousPage();
} else {
this.pdfViewer.nextPage();
}
}
if (evt.shiftKey) {
this.pdfViewer.previousPage();
} else {
this.pdfViewer.nextPage();
}
}

View file

@ -46,7 +46,6 @@ class PDFScriptingManager {
this._destroyCapability = null;
this._scripting = null;
this._mouseState = Object.create(null);
this._ready = false;
this._eventBus = eventBus;
@ -143,19 +142,9 @@ class PDFScriptingManager {
this._closeCapability?.resolve();
});
this._domEvents.set("mousedown", event => {
this._mouseState.isDown = true;
});
this._domEvents.set("mouseup", event => {
this._mouseState.isDown = false;
});
for (const [name, listener] of this._internalEvents) {
this._eventBus._on(name, listener);
}
for (const [name, listener] of this._domEvents) {
window.addEventListener(name, listener, true);
}
try {
const docProperties = await this._getDocProperties();
@ -229,10 +218,6 @@ class PDFScriptingManager {
});
}
get mouseState() {
return this._mouseState;
}
get destroyPromise() {
return this._destroyCapability?.promise || null;
}
@ -248,13 +233,6 @@ class PDFScriptingManager {
return shadow(this, "_internalEvents", new Map());
}
/**
* @private
*/
get _domEvents() {
return shadow(this, "_domEvents", new Map());
}
/**
* @private
*/
@ -287,13 +265,21 @@ class PDFScriptingManager {
case "error":
console.error(value);
break;
case "layout":
if (isInPresentationMode) {
case "layout": {
// NOTE: Always ignore the pageLayout in GeckoView since there's
// no UI available to change Scroll/Spread modes for the user.
if (
(typeof PDFJSDev === "undefined"
? window.isGECKOVIEW
: PDFJSDev.test("GECKOVIEW")) ||
isInPresentationMode
) {
return;
}
const modes = apiPageLayoutToViewerModes(value);
this._pdfViewer.spreadMode = modes.spreadMode;
break;
}
case "page-num":
this._pdfViewer.currentPageNumber = value + 1;
break;
@ -508,16 +494,10 @@ class PDFScriptingManager {
}
this._internalEvents.clear();
for (const [name, listener] of this._domEvents) {
window.removeEventListener(name, listener, true);
}
this._domEvents.clear();
this._pageOpenPending.clear();
this._visitedPages.clear();
this._scripting = null;
delete this._mouseState.isDown;
this._ready = false;
this._destroyCapability?.resolve();

View file

@ -14,7 +14,6 @@
*/
/** @typedef {import("../src/display/api").PDFDocumentProxy} PDFDocumentProxy */
/** @typedef {import("./event_utils").EventBus} EventBus */
/** @typedef {import("./interfaces").IL10n} IL10n */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len
@ -36,7 +35,6 @@ const THUMBNAIL_SELECTED_CLASS = "selected";
* @typedef {Object} PDFThumbnailViewerOptions
* @property {HTMLDivElement} container - The container for the thumbnail
* elements.
* @property {EventBus} eventBus - The application event bus.
* @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
* @property {IL10n} l10n - Localization service.
@ -52,14 +50,7 @@ class PDFThumbnailViewer {
/**
* @param {PDFThumbnailViewerOptions} options
*/
constructor({
container,
eventBus,
linkService,
renderingQueue,
l10n,
pageColors,
}) {
constructor({ container, linkService, renderingQueue, l10n, pageColors }) {
this.container = container;
this.linkService = linkService;
this.renderingQueue = renderingQueue;

View file

@ -22,18 +22,7 @@
/** @typedef {import("./event_utils").EventBus} EventBus */
/** @typedef {import("./interfaces").IDownloadManager} IDownloadManager */
/** @typedef {import("./interfaces").IL10n} IL10n */
// eslint-disable-next-line max-len
/** @typedef {import("./interfaces").IPDFAnnotationLayerFactory} IPDFAnnotationLayerFactory */
// eslint-disable-next-line max-len
/** @typedef {import("./interfaces").IPDFAnnotationEditorLayerFactory} IPDFAnnotationEditorLayerFactory */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len
/** @typedef {import("./interfaces").IPDFStructTreeLayerFactory} IPDFStructTreeLayerFactory */
// eslint-disable-next-line max-len
/** @typedef {import("./interfaces").IPDFTextLayerFactory} IPDFTextLayerFactory */
/** @typedef {import("./interfaces").IPDFXfaLayerFactory} IPDFXfaLayerFactory */
// eslint-disable-next-line max-len
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
import {
AnnotationEditorType,
@ -69,16 +58,10 @@ import {
VERTICAL_PADDING,
watchScroll,
} from "./ui_utils.js";
import { AnnotationEditorLayerBuilder } from "./annotation_editor_layer_builder.js";
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
import { NullL10n } from "./l10n_utils.js";
import { PDFPageView } from "./pdf_page_view.js";
import { PDFRenderingQueue } from "./pdf_rendering_queue.js";
import { SimpleLinkService } from "./pdf_link_service.js";
import { StructTreeLayerBuilder } from "./struct_tree_layer_builder.js";
import { TextHighlighter } from "./text_highlighter.js";
import { TextLayerBuilder } from "./text_layer_builder.js";
import { XfaLayerBuilder } from "./xfa_layer_builder.js";
const DEFAULT_CACHE_SIZE = 10;
const ENABLE_PERMISSIONS_CLASS = "enablePermissions";
@ -128,6 +111,8 @@ function isValidAnnotationEditorMode(mode) {
* landscape pages upon printing. The default is `false`.
* @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default
* value is `false`.
* @property {boolean} [isOffscreenCanvasSupported] - Allows to use an
* OffscreenCanvas if needed.
* @property {number} [maxCanvasPixels] - The maximum supported canvas size in
* total pixels, i.e. width * height. Use -1 for no limit. The default value
* is 4096 * 4096 (16 mega-pixels).
@ -209,12 +194,6 @@ class PDFPageViewBuffer {
/**
* Simple viewer control to display PDF content/pages.
*
* @implements {IPDFAnnotationLayerFactory}
* @implements {IPDFAnnotationEditorLayerFactory}
* @implements {IPDFStructTreeLayerFactory}
* @implements {IPDFTextLayerFactory}
* @implements {IPDFXfaLayerFactory}
*/
class PDFViewer {
#buffer = null;
@ -225,14 +204,20 @@ class PDFViewer {
#annotationMode = AnnotationMode.ENABLE_FORMS;
#containerTopLeft = null;
#enablePermissions = false;
#previousContainerHeight = 0;
#resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this));
#scrollModePageState = null;
#onVisibilityChange = null;
#scaleTimeoutId = null;
/**
* @param {PDFViewerOptions} options
*/
@ -252,12 +237,7 @@ class PDFViewer {
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || GENERIC")
) {
if (
!(
this.container?.tagName.toUpperCase() === "DIV" &&
this.viewer?.tagName.toUpperCase() === "DIV"
)
) {
if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") {
throw new Error("Invalid `container` and/or `viewer` option.");
}
@ -268,12 +248,13 @@ class PDFViewer {
throw new Error("The `container` must be absolutely positioned.");
}
}
this.#resizeObserver.observe(this.container);
this.eventBus = options.eventBus;
this.linkService = options.linkService || new SimpleLinkService();
this.downloadManager = options.downloadManager || null;
this.findController = options.findController || null;
this._scriptingManager = options.scriptingManager || null;
this.removePageBorders = options.removePageBorders || false;
this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this.#annotationMode =
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
@ -285,9 +266,12 @@ class PDFViewer {
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || GENERIC")
) {
this.removePageBorders = options.removePageBorders || false;
this.renderer = options.renderer || RendererType.CANVAS;
}
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
this.isOffscreenCanvasSupported =
options.isOffscreenCanvasSupported ?? true;
this.maxCanvasPixels = options.maxCanvasPixels;
this.l10n = options.l10n || NullL10n;
this.#enablePermissions = options.enablePermissions || false;
@ -324,10 +308,14 @@ class PDFViewer {
this._onBeforeDraw = this._onAfterDraw = null;
this._resetView();
if (this.removePageBorders) {
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
this.removePageBorders
) {
this.viewer.classList.add("removePageBorders");
}
this.updateContainerHeightCss();
this.#updateContainerHeightCss();
}
get pagesCount() {
@ -467,7 +455,7 @@ class PDFViewer {
if (!this.pdfDocument) {
return;
}
this._setScale(val, false);
this._setScale(val, { noScroll: false });
}
/**
@ -484,7 +472,7 @@ class PDFViewer {
if (!this.pdfDocument) {
return;
}
this._setScale(val, false);
this._setScale(val, { noScroll: false });
}
/**
@ -516,14 +504,12 @@ class PDFViewer {
const pageNumber = this._currentPageNumber;
const updateArgs = { rotation };
for (const pageView of this._pages) {
pageView.update(updateArgs);
}
this.refresh(true, { rotation });
// Prevent errors in case the rotation changes *before* the scale has been
// set to a non-default value.
if (this._currentScaleValue) {
this._setScale(this._currentScaleValue, true);
this._setScale(this._currentScaleValue, { noScroll: true });
}
this.eventBus.dispatch("rotationchanging", {
@ -549,6 +535,36 @@ class PDFViewer {
return this.pdfDocument ? this._pagesCapability.promise : null;
}
#layerProperties() {
const self = this;
return {
get annotationEditorUIManager() {
return self.#annotationEditorUIManager;
},
get annotationStorage() {
return self.pdfDocument?.annotationStorage;
},
get downloadManager() {
return self.downloadManager;
},
get enableScripting() {
return !!self._scriptingManager;
},
get fieldObjectsPromise() {
return self.pdfDocument?.getFieldObjects();
},
get findController() {
return self.findController;
},
get hasJSActionsPromise() {
return self.pdfDocument?.hasJSActions();
},
get linkService() {
return self.linkService;
},
};
}
/**
* Currently only *some* permissions are supported.
* @returns {Object}
@ -648,7 +664,6 @@ class PDFViewer {
if (!pdfDocument) {
return;
}
const isPureXfa = pdfDocument.isPureXfa;
const pagesCount = pdfDocument.numPages;
const firstPagePromise = pdfDocument.getPage(1);
// Rendering (potentially) depends on this, hence fetching it immediately.
@ -722,12 +737,13 @@ class PDFViewer {
if (annotationEditorMode !== AnnotationEditorType.DISABLE) {
const mode = annotationEditorMode;
if (isPureXfa) {
if (pdfDocument.isPureXfa) {
console.warn("Warning: XFA-editing is not implemented.");
} else if (isValidAnnotationEditorMode(mode)) {
this.#annotationEditorUIManager = new AnnotationEditorUIManager(
this.container,
this.eventBus
this.eventBus,
pdfDocument?.annotationStorage
);
if (mode !== AnnotationEditorType.NONE) {
this.#annotationEditorUIManager.updateMode(mode);
@ -737,20 +753,16 @@ class PDFViewer {
}
}
const layerProperties = this.#layerProperties.bind(this);
const viewerElement =
this._scrollMode === ScrollMode.PAGE ? null : this.viewer;
const scale = this.currentScale;
const viewport = firstPdfPage.getViewport({
scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS,
});
const textLayerFactory =
textLayerMode !== TextLayerMode.DISABLE && !isPureXfa ? this : null;
const annotationLayerFactory =
annotationMode !== AnnotationMode.DISABLE ? this : null;
const xfaLayerFactory = isPureXfa ? this : null;
const annotationEditorLayerFactory = this.#annotationEditorUIManager
? this
: null;
// Ensure that the various layers always get the correct initial size,
// see issue 15795.
this.viewer.style.setProperty("--scale-factor", viewport.scale);
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
const pageView = new PDFPageView({
@ -761,14 +773,8 @@ class PDFViewer {
defaultViewport: viewport.clone(),
optionalContentConfigPromise,
renderingQueue: this.renderingQueue,
textLayerFactory,
textLayerMode,
annotationLayerFactory,
annotationMode,
xfaLayerFactory,
annotationEditorLayerFactory,
textHighlighterFactory: this,
structTreeLayerFactory: this,
imageResourcesPath: this.imageResourcesPath,
renderer:
typeof PDFJSDev === "undefined" ||
@ -776,9 +782,11 @@ class PDFViewer {
? this.renderer
: null,
useOnlyCssZoom: this.useOnlyCssZoom,
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
maxCanvasPixels: this.maxCanvasPixels,
pageColors: this.pageColors,
l10n: this.l10n,
layerProperties,
});
this._pages.push(pageView);
}
@ -1024,10 +1032,12 @@ class PDFViewer {
#scrollIntoView(pageView, pageSpot = null) {
const { div, id } = pageView;
if (this._scrollMode === ScrollMode.PAGE) {
// Ensure that `this._currentPageNumber` is correct.
// Ensure that `this._currentPageNumber` is correct, when `#scrollIntoView`
// is called directly (and not from `#resetCurrentPageView`).
if (this._currentPageNumber !== id) {
this._setCurrentPageNumber(id);
}
if (this._scrollMode === ScrollMode.PAGE) {
this.#ensurePageViewVisible();
// Ensure that rendering always occurs, to avoid showing a blank page,
// even if the current position doesn't change when the page is scrolled.
@ -1047,6 +1057,15 @@ class PDFViewer {
}
}
scrollIntoView(div, pageSpot);
// Ensure that the correct *initial* document position is set, when any
// OpenParameters are used, for documents with non-default Scroll/Spread
// modes (fixes issue 15695). This is necessary since the scroll-handler
// invokes the `update`-method asynchronously, and `this._location` could
// thus be wrong when the initial zooming occurs in the default viewer.
if (!this._currentScaleValue && this._location) {
this._location = null;
}
}
/**
@ -1060,7 +1079,11 @@ class PDFViewer {
);
}
_setScaleUpdatePages(newScale, newValue, noScroll = false, preset = false) {
_setScaleUpdatePages(
newScale,
newValue,
{ noScroll = false, preset = false, drawingDelay = -1 }
) {
this._currentScaleValue = newValue.toString();
if (this.#isSameScale(newScale)) {
@ -1074,15 +1097,24 @@ class PDFViewer {
return;
}
docStyle.setProperty(
this.viewer.style.setProperty(
"--scale-factor",
newScale * PixelsPerInch.PDF_TO_CSS_UNITS
);
const updateArgs = { scale: newScale };
for (const pageView of this._pages) {
pageView.update(updateArgs);
const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;
this.refresh(true, {
scale: newScale,
drawingDelay: postponeDrawing ? drawingDelay : -1,
});
if (postponeDrawing) {
this.#scaleTimeoutId = setTimeout(() => {
this.#scaleTimeoutId = null;
this.refresh();
}, drawingDelay);
}
this._currentScale = newScale;
if (!noScroll) {
@ -1117,7 +1149,6 @@ class PDFViewer {
if (this.defaultRenderingQueue) {
this.update();
}
this.updateContainerHeightCss();
}
/**
@ -1133,11 +1164,12 @@ class PDFViewer {
return 1;
}
_setScale(value, noScroll = false) {
_setScale(value, options) {
let scale = parseFloat(value);
if (scale > 0) {
this._setScaleUpdatePages(scale, value, noScroll, /* preset = */ false);
options.preset = false;
this._setScaleUpdatePages(scale, value, options);
} else {
const currentPage = this._pages[this._currentPageNumber - 1];
if (!currentPage) {
@ -1147,8 +1179,18 @@ class PDFViewer {
vPadding = VERTICAL_PADDING;
if (this.isInPresentationMode) {
hPadding = vPadding = 4;
} else if (this.removePageBorders) {
// Pages have a 2px (transparent) border in PresentationMode, see
// the `web/pdf_viewer.css` file.
hPadding = vPadding = 4; // 2 * 2px
if (this._spreadMode !== SpreadMode.NONE) {
// Account for two pages being visible in PresentationMode, thus
// "doubling" the total border width.
hPadding *= 2;
}
} else if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
this.removePageBorders
) {
hPadding = vPadding = 0;
} else if (this._scrollMode === ScrollMode.HORIZONTAL) {
[hPadding, vPadding] = [vPadding, hPadding]; // Swap the padding values.
@ -1185,7 +1227,8 @@ class PDFViewer {
console.error(`_setScale: "${value}" is an unknown zoom value.`);
return;
}
this._setScaleUpdatePages(scale, value, noScroll, /* preset = */ true);
options.preset = true;
this._setScaleUpdatePages(scale, value, options);
}
}
@ -1197,7 +1240,7 @@ class PDFViewer {
if (this.isInPresentationMode) {
// Fixes the case when PDF has different page sizes.
this._setScale(this._currentScaleValue, true);
this._setScale(this._currentScaleValue, { noScroll: true });
}
this.#scrollIntoView(pageView);
}
@ -1314,9 +1357,15 @@ class PDFViewer {
y = destArray[3];
width = destArray[4] - x;
height = destArray[5] - y;
const hPadding = this.removePageBorders ? 0 : SCROLLBAR_PADDING;
const vPadding = this.removePageBorders ? 0 : VERTICAL_PADDING;
let hPadding = SCROLLBAR_PADDING,
vPadding = VERTICAL_PADDING;
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
this.removePageBorders
) {
hPadding = vPadding = 0;
}
widthScale =
(this.container.clientWidth - hPadding) /
width /
@ -1582,23 +1631,6 @@ class PDFViewer {
return this.scroll.down;
}
/**
* Only show the `loadingIcon`-spinner on visible pages (see issue 14242).
*/
#toggleLoadingIconSpinner(visibleIds) {
for (const id of visibleIds) {
const pageView = this._pages[id - 1];
pageView?.toggleLoadingIconSpinner(/* viewVisible = */ true);
}
for (const pageView of this.#buffer) {
if (visibleIds.has(pageView.id)) {
// Handled above, since the "buffer" may not contain all visible pages.
continue;
}
pageView.toggleLoadingIconSpinner(/* viewVisible = */ false);
}
}
forceRendering(currentlyVisiblePages) {
const visiblePages = currentlyVisiblePages || this._getVisiblePages();
const scrollAhead = this.#getScrollAhead(visiblePages);
@ -1612,7 +1644,6 @@ class PDFViewer {
scrollAhead,
preRenderExtra
);
this.#toggleLoadingIconSpinner(visiblePages.ids);
if (pageView) {
this.#ensurePdfPageLoaded(pageView).then(() => {
@ -1623,185 +1654,6 @@ class PDFViewer {
return false;
}
/**
* @typedef {Object} CreateTextLayerBuilderParameters
* @property {HTMLDivElement} textLayerDiv
* @property {number} pageIndex
* @property {PageViewport} viewport
* @property {EventBus} eventBus
* @property {TextHighlighter} highlighter
* @property {TextAccessibilityManager} [accessibilityManager]
*/
/**
* @param {CreateTextLayerBuilderParameters}
* @returns {TextLayerBuilder}
*/
createTextLayerBuilder({
textLayerDiv,
pageIndex,
viewport,
eventBus,
highlighter,
accessibilityManager = null,
}) {
return new TextLayerBuilder({
textLayerDiv,
eventBus,
pageIndex,
viewport,
highlighter,
accessibilityManager,
});
}
/**
* @typedef {Object} CreateTextHighlighterParameters
* @property {number} pageIndex
* @property {EventBus} eventBus
*/
/**
* @param {CreateTextHighlighterParameters}
* @returns {TextHighlighter}
*/
createTextHighlighter({ pageIndex, eventBus }) {
return new TextHighlighter({
eventBus,
pageIndex,
findController: this.isInPresentationMode ? null : this.findController,
});
}
/**
* @typedef {Object} CreateAnnotationLayerBuilderParameters
* @property {HTMLDivElement} pageDiv
* @property {PDFPageProxy} pdfPage
* @property {AnnotationStorage} [annotationStorage] - Storage for annotation
* data in forms.
* @property {string} [imageResourcesPath] - Path for image resources, mainly
* for annotation icons. Include trailing slash.
* @property {boolean} renderForms
* @property {IL10n} l10n
* @property {boolean} [enableScripting]
* @property {Promise<boolean>} [hasJSActionsPromise]
* @property {Object} [mouseState]
* @property {Promise<Object<string, Array<Object>> | null>}
* [fieldObjectsPromise]
* @property {Map<string, HTMLCanvasElement>} [annotationCanvasMap] - Map some
* annotation ids with canvases used to render them.
* @property {TextAccessibilityManager} [accessibilityManager]
*/
/**
* @param {CreateAnnotationLayerBuilderParameters}
* @returns {AnnotationLayerBuilder}
*/
createAnnotationLayerBuilder({
pageDiv,
pdfPage,
annotationStorage = this.pdfDocument?.annotationStorage,
imageResourcesPath = "",
renderForms = true,
l10n = NullL10n,
enableScripting = this.enableScripting,
hasJSActionsPromise = this.pdfDocument?.hasJSActions(),
mouseState = this._scriptingManager?.mouseState,
fieldObjectsPromise = this.pdfDocument?.getFieldObjects(),
annotationCanvasMap = null,
accessibilityManager = null,
}) {
return new AnnotationLayerBuilder({
pageDiv,
pdfPage,
annotationStorage,
imageResourcesPath,
renderForms,
linkService: this.linkService,
downloadManager: this.downloadManager,
l10n,
enableScripting,
hasJSActionsPromise,
mouseState,
fieldObjectsPromise,
annotationCanvasMap,
accessibilityManager,
});
}
/**
* @typedef {Object} CreateAnnotationEditorLayerBuilderParameters
* @property {AnnotationEditorUIManager} [uiManager]
* @property {HTMLDivElement} pageDiv
* @property {PDFPageProxy} pdfPage
* @property {IL10n} l10n
* @property {AnnotationStorage} [annotationStorage] - Storage for annotation
* @property {TextAccessibilityManager} [accessibilityManager]
* data in forms.
*/
/**
* @param {CreateAnnotationEditorLayerBuilderParameters}
* @returns {AnnotationEditorLayerBuilder}
*/
createAnnotationEditorLayerBuilder({
uiManager = this.#annotationEditorUIManager,
pageDiv,
pdfPage,
accessibilityManager = null,
l10n,
annotationStorage = this.pdfDocument?.annotationStorage,
}) {
return new AnnotationEditorLayerBuilder({
uiManager,
pageDiv,
pdfPage,
annotationStorage,
accessibilityManager,
l10n,
});
}
/**
* @typedef {Object} CreateXfaLayerBuilderParameters
* @property {HTMLDivElement} pageDiv
* @property {PDFPageProxy} pdfPage
* @property {AnnotationStorage} [annotationStorage] - Storage for annotation
* data in forms.
*/
/**
* @param {CreateXfaLayerBuilderParameters}
* @returns {XfaLayerBuilder}
*/
createXfaLayerBuilder({
pageDiv,
pdfPage,
annotationStorage = this.pdfDocument?.annotationStorage,
}) {
return new XfaLayerBuilder({
pageDiv,
pdfPage,
annotationStorage,
linkService: this.linkService,
});
}
/**
* @typedef {Object} CreateStructTreeLayerBuilderParameters
* @property {PDFPageProxy} pdfPage
*/
/**
* @param {CreateStructTreeLayerBuilderParameters}
* @returns {StructTreeLayerBuilder}
*/
createStructTreeLayerBuilder({ pdfPage }) {
return new StructTreeLayerBuilder({
pdfPage,
});
}
/**
* @type {boolean} Whether all pages of the PDF document have identical
* widths and heights.
@ -1878,11 +1730,7 @@ class PDFViewer {
}
this._optionalContentConfigPromise = promise;
const updateArgs = { optionalContentConfigPromise: promise };
for (const pageView of this._pages) {
pageView.update(updateArgs);
}
this.update();
this.refresh(false, { optionalContentConfigPromise: promise });
this.eventBus.dispatch("optionalcontentconfigchanged", {
source: this,
@ -1945,7 +1793,7 @@ class PDFViewer {
// Call this before re-scrolling to the current page, to ensure that any
// changes in scale don't move the current page.
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
this._setScale(this._currentScaleValue, true);
this._setScale(this._currentScaleValue, { noScroll: true });
}
this._setCurrentPageNumber(pageNumber, /* resetCurrentPageView = */ true);
this.update();
@ -2017,7 +1865,7 @@ class PDFViewer {
// Call this before re-scrolling to the current page, to ensure that any
// changes in scale don't move the current page.
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
this._setScale(this._currentScaleValue, true);
this._setScale(this._currentScaleValue, { noScroll: true });
}
this._setCurrentPageNumber(pageNumber, /* resetCurrentPageView = */ true);
this.update();
@ -2157,42 +2005,110 @@ class PDFViewer {
/**
* Increase the current zoom level one, or more, times.
* @param {number} [steps] - Defaults to zooming once.
* @param {Object|null} [options]
*/
increaseScale(steps = 1) {
increaseScale(options = null) {
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
typeof options === "number"
) {
console.error(
"The `increaseScale` method-signature was updated, please use an object instead."
);
options = { steps: options };
}
if (!this.pdfDocument) {
return;
}
options ||= Object.create(null);
let newScale = this._currentScale;
do {
newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
newScale = Math.ceil(newScale * 10) / 10;
newScale = Math.min(MAX_SCALE, newScale);
} while (--steps > 0 && newScale < MAX_SCALE);
this.currentScaleValue = newScale;
if (options.scaleFactor > 1) {
newScale = Math.min(
MAX_SCALE,
Math.round(newScale * options.scaleFactor * 100) / 100
);
} else {
let steps = options.steps ?? 1;
do {
newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
newScale = Math.ceil(newScale * 10) / 10;
newScale = Math.min(MAX_SCALE, newScale);
} while (--steps > 0 && newScale < MAX_SCALE);
}
options.noScroll = false;
this._setScale(newScale, options);
}
/**
* Decrease the current zoom level one, or more, times.
* @param {number} [steps] - Defaults to zooming once.
* @param {Object|null} [options]
*/
decreaseScale(steps = 1) {
decreaseScale(options = null) {
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
typeof options === "number"
) {
console.error(
"The `decreaseScale` method-signature was updated, please use an object instead."
);
options = { steps: options };
}
if (!this.pdfDocument) {
return;
}
options ||= Object.create(null);
let newScale = this._currentScale;
do {
newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
newScale = Math.floor(newScale * 10) / 10;
newScale = Math.max(MIN_SCALE, newScale);
} while (--steps > 0 && newScale > MIN_SCALE);
this.currentScaleValue = newScale;
if (options.scaleFactor > 0 && options.scaleFactor < 1) {
newScale = Math.max(
MIN_SCALE,
Math.round(newScale * options.scaleFactor * 100) / 100
);
} else {
let steps = options.steps ?? 1;
do {
newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
newScale = Math.floor(newScale * 10) / 10;
newScale = Math.max(MIN_SCALE, newScale);
} while (--steps > 0 && newScale > MIN_SCALE);
}
options.noScroll = false;
this._setScale(newScale, options);
}
updateContainerHeightCss() {
const height = this.container.clientHeight;
#updateContainerHeightCss(height = this.container.clientHeight) {
if (height !== this.#previousContainerHeight) {
this.#previousContainerHeight = height;
docStyle.setProperty("--viewer-container-height", `${height}px`);
}
}
#resizeObserverCallback(entries) {
for (const entry of entries) {
if (entry.target === this.container) {
this.#updateContainerHeightCss(
Math.floor(entry.borderBoxSize[0].blockSize)
);
this.#containerTopLeft = null;
break;
}
}
}
get containerTopLeft() {
return (this.#containerTopLeft ||= [
this.container.offsetTop,
this.container.offsetLeft,
]);
}
/**
* @type {number}
*/
@ -2235,15 +2151,20 @@ class PDFViewer {
this.#annotationEditorUIManager.updateParams(type, value);
}
refresh() {
refresh(noUpdate = false, updateArgs = Object.create(null)) {
if (!this.pdfDocument) {
return;
}
const updateArgs = {};
for (const pageView of this._pages) {
pageView.update(updateArgs);
}
this.update();
if (this.#scaleTimeoutId !== null) {
clearTimeout(this.#scaleTimeoutId);
this.#scaleTimeoutId = null;
}
if (!noUpdate) {
this.update();
}
}
}

View file

@ -18,5 +18,5 @@
const {addScriptSync} = require('../../protyle/util/addScript')
const {Constants} = require('../../constants')
addScriptSync(`${Constants.PROTYLE_CDN}/js/pdf/pdf.js?v=3.0.150`, 'pdfjsScript')
addScriptSync(`${Constants.PROTYLE_CDN}/js/pdf/pdf.js?v=3.4.120`, 'pdfjsScript')
module.exports = window["pdfjs-dist/build/pdf"];

View file

@ -13,8 +13,7 @@
* limitations under the License.
*/
import { ScrollMode, SpreadMode } from "./ui_utils.js";
import { CursorTool } from "./pdf_cursor_tools.js";
import { CursorTool, ScrollMode, SpreadMode } from "./ui_utils.js";
import { PagesCountLimit } from "./pdf_viewer.js";
/**

View file

@ -13,8 +13,6 @@
* limitations under the License.
*/
/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */
const PDF_ROLE_TO_HTML_ROLE = {
// Document level structure types
Document: null, // There's a "document" role, but it doesn't make sense here.
@ -73,24 +71,35 @@ const PDF_ROLE_TO_HTML_ROLE = {
const HEADING_PATTERN = /^H(\d+)$/;
/**
* @typedef {Object} StructTreeLayerBuilderOptions
* @property {PDFPageProxy} pdfPage
*/
class StructTreeLayerBuilder {
/**
* @param {StructTreeLayerBuilderOptions} options
*/
constructor({ pdfPage }) {
this.pdfPage = pdfPage;
#treeDom = undefined;
get renderingDone() {
return this.#treeDom !== undefined;
}
render(structTree) {
return this._walk(structTree);
if (this.#treeDom !== undefined) {
return this.#treeDom;
}
const treeDom = this.#walk(structTree);
treeDom?.classList.add("structTree");
return (this.#treeDom = treeDom);
}
_setAttributes(structElement, htmlElement) {
hide() {
if (this.#treeDom && !this.#treeDom.hidden) {
this.#treeDom.hidden = true;
}
}
show() {
if (this.#treeDom?.hidden) {
this.#treeDom.hidden = false;
}
}
#setAttributes(structElement, htmlElement) {
if (structElement.alt !== undefined) {
htmlElement.setAttribute("aria-label", structElement.alt);
}
@ -102,7 +111,7 @@ class StructTreeLayerBuilder {
}
}
_walk(node) {
#walk(node) {
if (!node) {
return null;
}
@ -119,16 +128,16 @@ class StructTreeLayerBuilder {
}
}
this._setAttributes(node, element);
this.#setAttributes(node, element);
if (node.children) {
if (node.children.length === 1 && "id" in node.children[0]) {
// Often there is only one content node so just set the values on the
// parent node to avoid creating an extra span.
this._setAttributes(node.children[0], element);
this.#setAttributes(node.children[0], element);
} else {
for (const kid of node.children) {
element.append(this._walk(kid));
element.append(this.#walk(kid));
}
}
}

View file

@ -95,6 +95,7 @@ class TextHighlighter {
);
this._onUpdateTextLayerMatches = null;
}
this._updateMatches(/* reset = */ true);
}
_convertMatches(matches, matchesLength) {
@ -264,8 +265,8 @@ class TextHighlighter {
}
}
_updateMatches() {
if (!this.enabled) {
_updateMatches(reset = false) {
if (!this.enabled && !reset) {
return;
}
const { findController, matches, pageIdx } = this;
@ -273,8 +274,7 @@ class TextHighlighter {
let clearedUntilDivIdx = -1;
// Clear all current matches.
for (let i = 0, ii = matches.length; i < ii; i++) {
const match = matches[i];
for (const match of matches) {
const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
for (let n = begin, end = match.end.divIdx; n <= end; n++) {
const div = textDivs[n];
@ -284,7 +284,7 @@ class TextHighlighter {
clearedUntilDivIdx = match.end.divIdx + 1;
}
if (!findController?.highlightMatches) {
if (!findController?.highlightMatches || reset) {
return;
}
// Convert the matches on the `findController` into the match format

View file

@ -15,23 +15,21 @@
// eslint-disable-next-line max-len
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
/** @typedef {import("./event_utils").EventBus} EventBus */
/** @typedef {import("../src/display/api").TextContent} TextContent */
/** @typedef {import("./text_highlighter").TextHighlighter} TextHighlighter */
// eslint-disable-next-line max-len
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
import { renderTextLayer } from "./pdfjs";
import { getHighlight } from '../anno'
import { renderTextLayer, updateTextLayer } from "./pdfjs";
import {getHighlight} from "../anno";
/**
* @typedef {Object} TextLayerBuilderOptions
* @property {HTMLDivElement} textLayerDiv - The text layer container.
* @property {EventBus} eventBus - The application event bus.
* @property {number} pageIndex - The page index.
* @property {PageViewport} viewport - The viewport of the text layer.
* @property {TextHighlighter} highlighter - Optional object that will handle
* highlighting text from the find controller.
* @property {TextAccessibilityManager} [accessibilityManager]
* @property {boolean} [isOffscreenCanvasSupported] - Allows to use an
* OffscreenCanvas if needed.
*/
/**
@ -40,28 +38,29 @@ import { getHighlight } from '../anno'
* contain text that matches the PDF text they are overlaying.
*/
class TextLayerBuilder {
#rotation = 0;
#scale = 0;
#textContentSource = null;
constructor({
textLayerDiv,
eventBus,
pageIndex,
viewport,
highlighter = null,
accessibilityManager = null,
isOffscreenCanvasSupported = true,
}) {
this.textLayerDiv = textLayerDiv;
this.eventBus = eventBus;
this.textContent = null;
this.textContentItemsStr = [];
this.textContentStream = null;
this.renderingDone = false;
this.pageNumber = pageIndex + 1;
this.viewport = viewport;
this.textDivs = [];
this.textDivProperties = new WeakMap();
this.textLayerRenderTask = null;
this.highlighter = highlighter;
this.accessibilityManager = accessibilityManager;
this.isOffscreenCanvasSupported = isOffscreenCanvasSupported;
this.#bindMouse();
this.div = document.createElement("div");
this.div.className = "textLayer";
this.hide();
}
#finishRendering() {
@ -69,54 +68,86 @@ class TextLayerBuilder {
const endOfContent = document.createElement("div");
endOfContent.className = "endOfContent";
this.textLayerDiv.append(endOfContent);
this.div.append(endOfContent);
this.#bindMouse();
this.eventBus.dispatch("textlayerrendered", {
source: this,
pageNumber: this.pageNumber,
numTextDivs: this.textDivs.length,
});
// NOTE
getHighlight(this.textLayerDiv)
getHighlight(this.div)
}
get numTextDivs() {
return this.textDivs.length;
}
/**
* Renders the text layer.
*
* @param {number} [timeout] - Wait for a specified amount of milliseconds
* before rendering.
* @param {PageViewport} viewport
*/
render(timeout = 0) {
if (!(this.textContent || this.textContentStream) || this.renderingDone) {
async render(viewport) {
if (!this.#textContentSource) {
throw new Error('No "textContentSource" parameter specified.');
}
const scale = viewport.scale * (globalThis.devicePixelRatio || 1);
const { rotation } = viewport;
if (this.renderingDone) {
const mustRotate = rotation !== this.#rotation;
const mustRescale = scale !== this.#scale;
if (mustRotate || mustRescale) {
this.hide();
updateTextLayer({
container: this.div,
viewport,
textDivs: this.textDivs,
textDivProperties: this.textDivProperties,
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
mustRescale,
mustRotate,
});
this.#scale = scale;
this.#rotation = rotation;
}
this.show();
return;
}
this.cancel();
this.textDivs.length = 0;
this.cancel();
this.highlighter?.setTextMapping(this.textDivs, this.textContentItemsStr);
this.accessibilityManager?.setTextMapping(this.textDivs);
const textLayerFrag = document.createDocumentFragment();
this.textLayerRenderTask = renderTextLayer({
textContent: this.textContent,
textContentStream: this.textContentStream,
container: textLayerFrag,
viewport: this.viewport,
textContentSource: this.#textContentSource,
container: this.div,
viewport,
textDivs: this.textDivs,
textDivProperties: this.textDivProperties,
textContentItemsStr: this.textContentItemsStr,
timeout,
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
});
this.textLayerRenderTask.promise.then(
() => {
this.textLayerDiv.append(textLayerFrag);
this.#finishRendering();
this.highlighter?.enable();
this.accessibilityManager?.enable();
},
function (reason) {
// Cancelled or failed to render text layer; skipping errors.
}
);
await this.textLayerRenderTask.promise;
this.#finishRendering();
this.#scale = scale;
this.#rotation = rotation;
this.show();
this.accessibilityManager?.enable();
}
hide() {
if (!this.div.hidden) {
// We turn off the highlighter in order to avoid to scroll into view an
// element of the text layer which could be hidden.
this.highlighter?.disable();
this.div.hidden = true;
}
}
show() {
if (this.div.hidden && this.renderingDone) {
this.div.hidden = false;
this.highlighter?.enable();
}
}
/**
@ -129,16 +160,17 @@ class TextLayerBuilder {
}
this.highlighter?.disable();
this.accessibilityManager?.disable();
this.textContentItemsStr.length = 0;
this.textDivs.length = 0;
this.textDivProperties = new WeakMap();
}
setTextContentStream(readableStream) {
/**
* @param {ReadableStream | TextContent} source
*/
setTextContentSource(source) {
this.cancel();
this.textContentStream = readableStream;
}
setTextContent(textContent) {
this.cancel();
this.textContent = textContent;
this.#textContentSource = source;
}
/**
@ -147,7 +179,7 @@ class TextLayerBuilder {
* dragged up or down.
*/
#bindMouse() {
const div = this.textLayerDiv;
const { div } = this;
div.addEventListener("mousedown", evt => {
const end = div.querySelector(".endOfContent");

View file

@ -17,7 +17,6 @@ import {
animationStarted,
DEFAULT_SCALE,
DEFAULT_SCALE_VALUE,
docStyle,
MAX_SCALE,
MIN_SCALE,
noContextMenuHandler,
@ -262,6 +261,7 @@ class Toolbar {
items.pageNumber.type = "text";
} else {
items.pageNumber.type = "number";
// NOTE
items.numPages.textContent = "/ " + pagesCount;
}
items.pageNumber.max = pagesCount;
@ -269,6 +269,7 @@ class Toolbar {
if (this.hasPageLabels) {
items.pageNumber.value = this.pageLabel;
// NOTE
items.numPages.textContent = `(${pageNumber} / ${pagesCount})`
} else {
items.pageNumber.value = pageNumber;
@ -318,15 +319,10 @@ class Toolbar {
await animationStarted;
const style = getComputedStyle(items.scaleSelect),
scaleSelectContainerWidth = parseInt(
style.getPropertyValue("--scale-select-container-width"),
10
),
scaleSelectOverflow = parseInt(
style.getPropertyValue("--scale-select-overflow"),
10
);
const style = getComputedStyle(items.scaleSelect);
const scaleSelectWidth = parseFloat(
style.getPropertyValue("--scale-select-width")
);
// The temporary canvas is used to measure text length in the DOM.
const canvas = document.createElement("canvas");
@ -334,16 +330,19 @@ class Toolbar {
ctx.font = `${style.fontSize} ${style.fontFamily}`;
let maxWidth = 0;
for (const predefinedValue of predefinedValuesPromise) {
for (const predefinedValue of await predefinedValuesPromise) {
const { width } = ctx.measureText(predefinedValue);
if (width > maxWidth) {
maxWidth = width;
}
}
maxWidth += 2 * scaleSelectOverflow;
// Account for the icon width, and ensure that there's always some spacing
// between the text and the icon.
maxWidth += 0.3 * scaleSelectWidth;
if (maxWidth > scaleSelectContainerWidth) {
docStyle.setProperty("--scale-select-container-width", `${maxWidth}px`);
if (maxWidth > scaleSelectWidth) {
const container = items.scaleSelect.parentNode;
container.style.setProperty("--scale-select-width", `${maxWidth}px`);
}
// Zeroing the width and height cause Firefox to release graphics resources
// immediately, which can greatly reduce memory consumption.

View file

@ -13,8 +13,6 @@
* limitations under the License.
*/
import {hasClosestByAttribute} from "../../protyle/util/hasClosest";
const DEFAULT_SCALE_VALUE = "auto";
const DEFAULT_SCALE = 1.0;
const DEFAULT_SCALE_DELTA = 1.1;
@ -76,6 +74,12 @@ const SpreadMode = {
EVEN: 2,
};
const CursorTool = {
SELECT: 0, // The default value.
HAND: 1,
ZOOM: 2,
};
// Used by `PDFViewerApplication`, and by the API unit-tests.
const AutoPrintRegExp = /\bprint\s*\(/;
@ -624,17 +628,16 @@ function normalizeWheelEventDirection(evt) {
}
function normalizeWheelEventDelta(evt) {
const deltaMode = evt.deltaMode; // Avoid being affected by bug 1392460.
let delta = normalizeWheelEventDirection(evt);
const MOUSE_DOM_DELTA_PIXEL_MODE = 0;
const MOUSE_DOM_DELTA_LINE_MODE = 1;
const MOUSE_PIXELS_PER_LINE = 30;
const MOUSE_LINES_PER_PAGE = 30;
// Converts delta to per-page units
if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) {
if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
} else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) {
} else if (deltaMode === WheelEvent.DOM_DELTA_LINE) {
delta /= MOUSE_LINES_PER_PAGE;
}
return delta;
@ -695,13 +698,18 @@ function clamp(v, min, max) {
class ProgressBar {
#classList = null;
#disableAutoFetchTimeout = null;
#percent = 0;
#style = null;
#visible = true;
// NOTE
constructor(bar) {
this.#classList = bar.classList;
this.#style = bar.style;
}
get percent() {
@ -717,7 +725,7 @@ class ProgressBar {
}
this.#classList.remove("indeterminate");
docStyle.setProperty("--progressBar-percent", `${this.#percent}%`);
this.#style.setProperty("--progressBar-percent", `${this.#percent}%`);
}
setWidth(viewer) {
@ -727,10 +735,28 @@ class ProgressBar {
const container = viewer.parentNode;
const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
if (scrollbarWidth > 0) {
docStyle.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`);
this.#style.setProperty(
"--progressBar-end-offset",
`${scrollbarWidth}px`
);
}
}
setDisableAutoFetch(delay = /* ms = */ 5000) {
if (isNaN(this.#percent)) {
return;
}
if (this.#disableAutoFetchTimeout) {
clearTimeout(this.#disableAutoFetchTimeout);
}
this.show();
this.#disableAutoFetchTimeout = setTimeout(() => {
this.#disableAutoFetchTimeout = null;
this.hide();
}, delay);
}
hide() {
if (!this.#visible) {
return;
@ -775,9 +801,6 @@ function getActiveOrFocusedElement() {
/**
* Converts API PageLayout values to the format used by `BaseViewer`.
* NOTE: This is supported to the extent that the viewer implements the
* necessary Scroll/Spread modes (since SinglePage, TwoPageLeft,
* and TwoPageRight all suggests using non-continuous scrolling).
* @param {string} mode - The API PageLayout value.
* @returns {Object}
*/
@ -839,6 +862,7 @@ export {
AutoPrintRegExp,
backtrackBeforeAllVisibleElements, // only exported for testing
binarySearchFirstItem,
CursorTool,
DEFAULT_SCALE,
DEFAULT_SCALE_DELTA,
DEFAULT_SCALE_VALUE,

View file

@ -13,8 +13,6 @@
* limitations under the License.
*/
import {setStorageVal} from "../../protyle/util/compatibility";
const DEFAULT_VIEW_HISTORY_CACHE_SIZE = 20;
/**

View file

@ -13,237 +13,192 @@
* limitations under the License.
*/
import { RenderingStates, ScrollMode, SpreadMode } from './ui_utils.js'
import { AppOptions } from './app_options.js'
import { LinkTarget } from './pdf_link_service.js'
import { PDFViewerApplication } from './app.js'
import { initAnno } from '../anno'
import { RenderingStates, ScrollMode, SpreadMode } from "./ui_utils.js";
import { AppOptions } from "./app_options.js";
import { LinkTarget } from "./pdf_link_service.js";
import { PDFViewerApplication } from "./app.js";
import { initAnno } from "../anno";
/* eslint-disable-next-line no-unused-vars */
const pdfjsVersion =
typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('BUNDLE_VERSION') : void 0
typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_VERSION") : void 0;
/* eslint-disable-next-line no-unused-vars */
const pdfjsBuild =
typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('BUNDLE_BUILD') : void 0
typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0;
const AppConstants =
typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')
? {LinkTarget, RenderingStates, ScrollMode, SpreadMode}
: null
window.PDFViewerApplication = PDFViewerApplication
window.PDFViewerApplicationConstants = AppConstants
window.PDFViewerApplicationOptions = AppOptions
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME')) {
(function rewriteUrlClosure () {
// Run this code outside DOMContentLoaded to make sure that the URL
// is rewritten as soon as possible.
const queryString = document.location.search.slice(1)
const m = /(^|&)file=([^&]*)/.exec(queryString)
const defaultUrl = m ? decodeURIComponent(m[2]) : ''
// Example: chrome-extension://.../http://example.com/file.pdf
const humanReadableUrl = '/' + defaultUrl + location.hash
history.replaceState(history.state, '', humanReadableUrl)
if (top === window) {
// eslint-disable-next-line no-undef
chrome.runtime.sendMessage('showPageAction')
}
AppOptions.set('defaultUrl', defaultUrl)
})()
}
typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")
? { LinkTarget, RenderingStates, ScrollMode, SpreadMode }
: null;
// NOTE
// if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) {
// require('./firefoxcom.js')
// require('./firefox_print_service.js')
// }
// if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('GENERIC')) {
// require('./genericcom.js')
// }
// if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME')) {
// require('./chromecom.js')
// }
// if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('CHROME || GENERIC')) {
// require('./pdf_print_service.js')
// }
// window.PDFViewerApplication = PDFViewerApplication;
// window.PDFViewerApplicationConstants = AppConstants;
// window.PDFViewerApplicationOptions = AppOptions;
// NOTE
function getViewerConfiguration (element) {
function getViewerConfiguration(element) {
return {
appContainer: element,
mainContainer: element.querySelector('#viewerContainer'),
viewerContainer: element.querySelector('#viewer'),
mainContainer: element.querySelector("#viewerContainer"),
viewerContainer: element.querySelector("#viewer"),
toolbar: {
// NOTE
rectAnno: element.querySelector('#rectAnno'),
container: element.querySelector('#toolbarViewer'),
numPages: element.querySelector('#numPages'),
pageNumber: element.querySelector('#pageNumber'),
scaleSelect: element.querySelector('#scaleSelect'),
customScaleOption: element.querySelector('#customScaleOption'),
previous: element.querySelector('#previous'),
next: element.querySelector('#next'),
zoomIn: element.querySelector('#zoomIn'),
zoomOut: element.querySelector('#zoomOut'),
viewFind: element.querySelector('#viewFind'),
rectAnno: element.querySelector("#rectAnno"),
container: element.querySelector("#toolbarViewer"),
numPages: element.querySelector("#numPages"),
pageNumber: element.querySelector("#pageNumber"),
scaleSelect: element.querySelector("#scaleSelect"),
customScaleOption: element.querySelector("#customScaleOption"),
previous: element.querySelector("#previous"),
next: element.querySelector("#next"),
zoomIn: element.querySelector("#zoomIn"),
zoomOut: element.querySelector("#zoomOut"),
viewFind: element.querySelector("#viewFind"),
openFile:
typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')
? element.querySelector('#openFile')
typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")
? element.querySelector("#openFile")
: null,
print: element.querySelector('#print'),
editorFreeTextButton: element.querySelector('#editorFreeText'),
editorFreeTextParamsToolbar: element.querySelector('#editorFreeTextParamsToolbar'),
editorInkButton: element.querySelector('#editorInk'),
editorInkParamsToolbar: element.querySelector('#editorInkParamsToolbar'),
download: element.querySelector('#download'),
print: element.querySelector("#print"),
editorFreeTextButton: element.querySelector("#editorFreeText"),
editorFreeTextParamsToolbar: element.querySelector(
"#editorFreeTextParamsToolbar"
),
editorInkButton: element.querySelector("#editorInk"),
editorInkParamsToolbar: element.querySelector("#editorInkParamsToolbar"),
download: element.querySelector("#download"),
},
secondaryToolbar: {
toolbar: element.querySelector('#secondaryToolbar'),
toggleButton: element.querySelector('#secondaryToolbarToggle'),
presentationModeButton: element.querySelector('#presentationMode'),
toolbar: element.querySelector("#secondaryToolbar"),
toggleButton: element.querySelector("#secondaryToolbarToggle"),
presentationModeButton: element.querySelector("#presentationMode"),
openFileButton:
typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')
? element.querySelector('#secondaryOpenFile')
typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")
? element.querySelector("#secondaryOpenFile")
: null,
printButton: element.querySelector('#secondaryPrint'),
downloadButton: element.querySelector('#secondaryDownload'),
viewBookmarkButton: element.querySelector('#viewBookmark'),
firstPageButton: element.querySelector('#firstPage'),
lastPageButton: element.querySelector('#lastPage'),
pageRotateCwButton: element.querySelector('#pageRotateCw'),
pageRotateCcwButton: element.querySelector('#pageRotateCcw'),
cursorSelectToolButton: element.querySelector('#cursorSelectTool'),
cursorHandToolButton: element.querySelector('#cursorHandTool'),
scrollPageButton: element.querySelector('#scrollPage'),
scrollVerticalButton: element.querySelector('#scrollVertical'),
scrollHorizontalButton: element.querySelector('#scrollHorizontal'),
scrollWrappedButton: element.querySelector('#scrollWrapped'),
spreadNoneButton: element.querySelector('#spreadNone'),
spreadOddButton: element.querySelector('#spreadOdd'),
spreadEvenButton: element.querySelector('#spreadEven'),
documentPropertiesButton: element.querySelector('#documentProperties'),
printButton: element.querySelector("#secondaryPrint"),
downloadButton: element.querySelector("#secondaryDownload"),
viewBookmarkButton: element.querySelector("#viewBookmark"),
firstPageButton: element.querySelector("#firstPage"),
lastPageButton: element.querySelector("#lastPage"),
pageRotateCwButton: element.querySelector("#pageRotateCw"),
pageRotateCcwButton: element.querySelector("#pageRotateCcw"),
cursorSelectToolButton: element.querySelector("#cursorSelectTool"),
cursorHandToolButton: element.querySelector("#cursorHandTool"),
scrollPageButton: element.querySelector("#scrollPage"),
scrollVerticalButton: element.querySelector("#scrollVertical"),
scrollHorizontalButton: element.querySelector("#scrollHorizontal"),
scrollWrappedButton: element.querySelector("#scrollWrapped"),
spreadNoneButton: element.querySelector("#spreadNone"),
spreadOddButton: element.querySelector("#spreadOdd"),
spreadEvenButton: element.querySelector("#spreadEven"),
documentPropertiesButton: element.querySelector("#documentProperties"),
},
sidebar: {
// Divs (and sidebar button)
outerContainer: element.querySelector('#outerContainer'),
sidebarContainer: element.querySelector('#sidebarContainer'),
toggleButton: element.querySelector('#sidebarToggle'),
outerContainer: element.querySelector("#outerContainer"),
sidebarContainer: element.querySelector("#sidebarContainer"),
toggleButton: element.querySelector("#sidebarToggle"),
// Buttons
thumbnailButton: element.querySelector('#viewThumbnail'),
outlineButton: element.querySelector('#viewOutline'),
attachmentsButton: element.querySelector('#viewAttachments'),
layersButton: element.querySelector('#viewLayers'),
thumbnailButton: element.querySelector("#viewThumbnail"),
outlineButton: element.querySelector("#viewOutline"),
attachmentsButton: element.querySelector("#viewAttachments"),
layersButton: element.querySelector("#viewLayers"),
// Views
thumbnailView: element.querySelector('#thumbnailView'),
outlineView: element.querySelector('#outlineView'),
attachmentsView: element.querySelector('#attachmentsView'),
layersView: element.querySelector('#layersView'),
thumbnailView: element.querySelector("#thumbnailView"),
outlineView: element.querySelector("#outlineView"),
attachmentsView: element.querySelector("#attachmentsView"),
layersView: element.querySelector("#layersView"),
// View-specific options
outlineOptionsContainer: element.querySelector('#outlineOptionsContainer'),
currentOutlineItemButton: element.querySelector('#currentOutlineItem'),
outlineOptionsContainer: element.querySelector("#outlineOptionsContainer"),
currentOutlineItemButton: element.querySelector("#currentOutlineItem"),
},
sidebarResizer: {
outerContainer: element.querySelector('#outerContainer'),
resizer: element.querySelector('#sidebarResizer'),
outerContainer: element.querySelector("#outerContainer"),
resizer: element.querySelector("#sidebarResizer"),
},
findBar: {
bar: element.querySelector('#findbar'),
toggleButton: element.querySelector('#viewFind'),
findField: element.querySelector('#findInput'),
highlightAllCheckbox: element.querySelector('#findHighlightAll'),
caseSensitiveCheckbox: element.querySelector('#findMatchCase'),
matchDiacriticsCheckbox: element.querySelector('#findMatchDiacritics'),
entireWordCheckbox: element.querySelector('#findEntireWord'),
findMsg: element.querySelector('#findMsg'),
findResultsCount: element.querySelector('#findResultsCount'),
findPreviousButton: element.querySelector('#findPrevious'),
findNextButton: element.querySelector('#findNext'),
bar: element.querySelector("#findbar"),
toggleButton: element.querySelector("#viewFind"),
findField: element.querySelector("#findInput"),
highlightAllCheckbox: element.querySelector("#findHighlightAll"),
caseSensitiveCheckbox: element.querySelector("#findMatchCase"),
matchDiacriticsCheckbox: element.querySelector("#findMatchDiacritics"),
entireWordCheckbox: element.querySelector("#findEntireWord"),
findMsg: element.querySelector("#findMsg"),
findResultsCount: element.querySelector("#findResultsCount"),
findPreviousButton: element.querySelector("#findPrevious"),
findNextButton: element.querySelector("#findNext"),
},
passwordOverlay: {
dialog: element.querySelector('#passwordDialog'),
label: element.querySelector('#passwordText'),
input: element.querySelector('#password'),
submitButton: element.querySelector('#passwordSubmit'),
cancelButton: element.querySelector('#passwordCancel'),
dialog: element.querySelector("#passwordDialog"),
label: element.querySelector("#passwordText"),
input: element.querySelector("#password"),
submitButton: element.querySelector("#passwordSubmit"),
cancelButton: element.querySelector("#passwordCancel"),
},
documentProperties: {
dialog: element.querySelector('#documentPropertiesDialog'),
closeButton: element.querySelector('#documentPropertiesClose'),
dialog: element.querySelector("#documentPropertiesDialog"),
closeButton: element.querySelector("#documentPropertiesClose"),
fields: {
fileName: element.querySelector('#fileNameField'),
fileSize: element.querySelector('#fileSizeField'),
title: element.querySelector('#titleField'),
author: element.querySelector('#authorField'),
subject: element.querySelector('#subjectField'),
keywords: element.querySelector('#keywordsField'),
creationDate: element.querySelector('#creationDateField'),
modificationDate: element.querySelector('#modificationDateField'),
creator: element.querySelector('#creatorField'),
producer: element.querySelector('#producerField'),
version: element.querySelector('#versionField'),
pageCount: element.querySelector('#pageCountField'),
pageSize: element.querySelector('#pageSizeField'),
linearized: element.querySelector('#linearizedField'),
fileName: element.querySelector("#fileNameField"),
fileSize: element.querySelector("#fileSizeField"),
title: element.querySelector("#titleField"),
author: element.querySelector("#authorField"),
subject: element.querySelector("#subjectField"),
keywords: element.querySelector("#keywordsField"),
creationDate: element.querySelector("#creationDateField"),
modificationDate: element.querySelector("#modificationDateField"),
creator: element.querySelector("#creatorField"),
producer: element.querySelector("#producerField"),
version: element.querySelector("#versionField"),
pageCount: element.querySelector("#pageCountField"),
pageSize: element.querySelector("#pageSizeField"),
linearized: element.querySelector("#linearizedField"),
},
},
annotationEditorParams: {
editorFreeTextFontSize: element.querySelector('#editorFreeTextFontSize'),
editorFreeTextColor: element.querySelector('#editorFreeTextColor'),
editorInkColor: element.querySelector('#editorInkColor'),
editorInkThickness: element.querySelector('#editorInkThickness'),
editorInkOpacity: element.querySelector('#editorInkOpacity'),
editorFreeTextFontSize: element.querySelector("#editorFreeTextFontSize"),
editorFreeTextColor: element.querySelector("#editorFreeTextColor"),
editorInkColor: element.querySelector("#editorInkColor"),
editorInkThickness: element.querySelector("#editorInkThickness"),
editorInkOpacity: element.querySelector("#editorInkOpacity"),
},
errorWrapper:
typeof PDFJSDev === 'undefined' || !PDFJSDev.test('MOZCENTRAL')
? {
container: element.querySelector('#errorWrapper'),
errorMessage: element.querySelector('#errorMessage'),
closeButton: element.querySelector('#errorClose'),
errorMoreInfo: element.querySelector('#errorMoreInfo'),
moreInfoButton: element.querySelector('#errorShowMore'),
lessInfoButton: element.querySelector('#errorShowLess'),
}
: null,
printContainer: element.querySelector('#printContainer'),
printContainer: element.querySelector("#printContainer"),
openFileInput:
typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')
? element.querySelector('#fileInput')
typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")
? element.querySelector("#fileInput")
: null,
debuggerScriptPath: './debugger.js',
debuggerScriptPath: "./debugger.js",
}
}
// NOTE
function webViewerLoad (file, element, pdfPage, annoId) {
function webViewerLoad(file, element, pdfPage, annoId) {
const pdf = new PDFViewerApplication(pdfPage)
pdf.annoId = annoId
const config = getViewerConfiguration(element)
if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')) {
config.file = file
} else {
if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('GENERIC')) {
// Give custom implementations of the default viewer a simpler way to
// set various `AppOptions`, by dispatching an event once all viewer
// files are loaded but *before* the viewer initialization has run.
const event = document.createEvent('CustomEvent')
event.initCustomEvent('webviewerloaded', true, true, {
source: window,
})
try {
// Attempt to dispatch the event at the embedding `document`,
// in order to support cases where the viewer is embedded in
// a *dynamically* created <iframe> element.
parent.document.dispatchEvent(event)
} catch (ex) {
// The viewer could be in e.g. a cross-origin <iframe> element,
// fallback to dispatching the event at the current `document`.
console.error(`webviewerloaded: ${ex}`)
document.dispatchEvent(event)
}
const config = getViewerConfiguration(element);
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
// Give custom implementations of the default viewer a simpler way to
// set various `AppOptions`, by dispatching an event once all viewer
// files are loaded but *before* the viewer initialization has run.
const event = document.createEvent("CustomEvent");
event.initCustomEvent("webviewerloaded", true, true, {
source: window,
});
try {
// Attempt to dispatch the event at the embedding `document`,
// in order to support cases where the viewer is embedded in
// a *dynamically* created <iframe> element.
parent.document.dispatchEvent(event);
} catch (ex) {
// The viewer could be in e.g. a cross-origin <iframe> element,
// fallback to dispatching the event at the current `document`.
console.error(`webviewerloaded: ${ex}`);
document.dispatchEvent(event);
}
} else {
config.file = file
}
pdf.run(config)
initAnno(file, element, annoId, pdf, config);
@ -252,19 +207,19 @@ function webViewerLoad (file, element, pdfPage, annoId) {
// Block the "load" event until all pages are loaded, to ensure that printing
// works in Firefox; see https://bugzilla.mozilla.org/show_bug.cgi?id=1618553
document.blockUnblockOnload?.(true);
document.blockUnblockOnload?.(true)
// NOTE
// if (
// document.readyState === 'interactive' ||
// document.readyState === 'complete'
// document.readyState === "interactive" ||
// document.readyState === "complete"
// ) {
// webViewerLoad()
// webViewerLoad();
// } else {
// document.addEventListener('DOMContentLoaded', webViewerLoad, true)
// document.addEventListener("DOMContentLoaded", webViewerLoad, true);
// }
// NOTE
export {
webViewerLoad
}
webViewerLoad,
};

View file

@ -55,9 +55,9 @@ class XfaLayerBuilder {
* @param {string} intent (default value is 'display')
* @returns {Promise<Object | void>} A promise that is resolved when rendering
* of the XFA layer is complete. The first rendering will return an object
* with a `textDivs` property that can be used with the TextHighlighter.
* with a `textDivs` property that can be used with the TextHighlighter.
*/
render(viewport, intent = "display") {
async render(viewport, intent = "display") {
if (intent === "print") {
const parameters = {
viewport: viewport.clone({ dontFlip: true }),
@ -73,39 +73,33 @@ class XfaLayerBuilder {
this.pageDiv.append(div);
parameters.div = div;
const result = XfaLayer.render(parameters);
return Promise.resolve(result);
return XfaLayer.render(parameters);
}
// intent === "display"
return this.pdfPage
.getXfa()
.then(xfaHtml => {
if (this._cancelled || !xfaHtml) {
return { textDivs: [] };
}
const xfaHtml = await this.pdfPage.getXfa();
if (this._cancelled || !xfaHtml) {
return { textDivs: [] };
}
const parameters = {
viewport: viewport.clone({ dontFlip: true }),
div: this.div,
xfaHtml,
annotationStorage: this.annotationStorage,
linkService: this.linkService,
intent,
};
const parameters = {
viewport: viewport.clone({ dontFlip: true }),
div: this.div,
xfaHtml,
annotationStorage: this.annotationStorage,
linkService: this.linkService,
intent,
};
if (this.div) {
return XfaLayer.update(parameters);
}
// Create an xfa layer div and render the form
this.div = document.createElement("div");
this.pageDiv.append(this.div);
parameters.div = this.div;
return XfaLayer.render(parameters);
})
.catch(error => {
console.error(error);
});
if (this.div) {
return XfaLayer.update(parameters);
}
// Create an xfa layer div and render the form
this.div = document.createElement("div");
this.pageDiv.append(this.div);
parameters.div = this.div;
return XfaLayer.render(parameters);
}
cancel() {

View file

@ -398,12 +398,18 @@
}
}
.dropdownToolbarButton select {
min-width: 124px;
height: 24px;
margin: 4px 8px 0 0;
line-height: 24px;
padding: 0 8px;
.dropdownToolbarButton {
--scale-select-width: 140px;
width: var(--scale-select-width);
select {
width: inherit;
min-width: auto;
height: 24px;
margin: 4px 8px 0 0;
line-height: 24px;
padding: 0 8px;
}
}
#customScaleOption {

View file

@ -13,9 +13,65 @@
* limitations under the License.
*/
:root {
--annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.13);'/></svg>");
--input-focus-border-color: Highlight;
--input-focus-outline: 1px solid Canvas;
--input-unfocused-border-color: transparent;
--input-disabled-border-color: transparent;
--input-hover-border-color: black;
--link-outline: none;
}
@media screen and (forced-colors: active) {
:root {
--input-focus-border-color: CanvasText;
--input-unfocused-border-color: ActiveText;
--input-disabled-border-color: GrayText;
--input-hover-border-color: Highlight;
--link-outline: 1.5px solid LinkText;
}
.annotationLayer .linkAnnotation:hover {
backdrop-filter: invert(100%);
}
}
.annotationLayer {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
transform-origin: 0 0;
z-index: 3;
}
.annotationLayer[data-main-rotation="90"] .norotate {
transform: rotate(270deg) translateX(-100%);
}
.annotationLayer[data-main-rotation="180"] .norotate {
transform: rotate(180deg) translate(-100%, -100%);
}
.annotationLayer[data-main-rotation="270"] .norotate {
transform: rotate(90deg) translateY(-100%);
}
.annotationLayer canvas {
position: absolute;
width: 100%;
height: 100%;
}
.annotationLayer section {
position: absolute;
text-align: initial;
pointer-events: auto;
box-sizing: border-box;
transform-origin: 0 0;
}
.annotationLayer .linkAnnotation {
outline: var(--link-outline);
}
.annotationLayer .linkAnnotation > a,
@ -28,13 +84,6 @@
height: 100%;
}
.annotationLayer .buttonWidgetAnnotation.pushButton > canvas {
position: relative;
top: 0;
left: 0;
z-index: -1;
}
.annotationLayer .linkAnnotation > a:hover,
.annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
opacity: 0.2;
@ -45,6 +94,10 @@
.annotationLayer .textAnnotation img {
position: absolute;
cursor: pointer;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.annotationLayer .textWidgetAnnotation input,
@ -52,17 +105,24 @@
.annotationLayer .choiceWidgetAnnotation select,
.annotationLayer .buttonWidgetAnnotation.checkBox input,
.annotationLayer .buttonWidgetAnnotation.radioButton input {
background-color: rgba(0, 54, 255, 0.13);
border: 1px solid transparent;
background-color: rgba(0, 54, 255, 0.13); // NOTE
border: 2px solid var(--input-unfocused-border-color);
box-sizing: border-box;
font-size: 9px;
font: calc(9px * var(--scale-factor)) sans-serif;
height: 100%;
margin: 0;
padding: 0 3px;
vertical-align: top;
width: 100%;
}
.annotationLayer .textWidgetAnnotation input:required,
.annotationLayer .textWidgetAnnotation textarea:required,
.annotationLayer .choiceWidgetAnnotation select:required,
.annotationLayer .buttonWidgetAnnotation.checkBox input:required,
.annotationLayer .buttonWidgetAnnotation.radioButton input:required {
outline: 1.5px solid red;
}
.annotationLayer .choiceWidgetAnnotation select option {
padding: 0;
}
@ -72,8 +132,6 @@
}
.annotationLayer .textWidgetAnnotation textarea {
font: message-box;
font-size: 9px;
resize: none;
}
@ -83,7 +141,7 @@
.annotationLayer .buttonWidgetAnnotation.checkBox input[disabled],
.annotationLayer .buttonWidgetAnnotation.radioButton input[disabled] {
background: none;
border: 1px solid transparent;
border: 2px solid var(--input-disabled-border-color);
cursor: not-allowed;
}
@ -92,30 +150,45 @@
.annotationLayer .choiceWidgetAnnotation select:hover,
.annotationLayer .buttonWidgetAnnotation.checkBox input:hover,
.annotationLayer .buttonWidgetAnnotation.radioButton input:hover {
border: 1px solid rgba(0, 0, 0, 1);
border: 2px solid var(--input-hover-border-color);
}
.annotationLayer .textWidgetAnnotation input:hover,
.annotationLayer .textWidgetAnnotation textarea:hover,
.annotationLayer .choiceWidgetAnnotation select:hover,
.annotationLayer .buttonWidgetAnnotation.checkBox input:hover {
border-radius: 2px;
}
.annotationLayer .textWidgetAnnotation input:focus,
.annotationLayer .textWidgetAnnotation textarea:focus,
.annotationLayer .choiceWidgetAnnotation select:focus {
background: none;
border: 1px solid transparent;
border: 2px solid var(--input-focus-border-color);
border-radius: 2px;
outline: var(--input-focus-outline);
}
.annotationLayer .textWidgetAnnotation input :focus,
.annotationLayer .textWidgetAnnotation textarea :focus,
.annotationLayer .choiceWidgetAnnotation select :focus,
.annotationLayer .buttonWidgetAnnotation.checkBox :focus,
.annotationLayer .buttonWidgetAnnotation.radioButton :focus {
background-image: none;
background-color: transparent;
outline: auto;
}
.annotationLayer .buttonWidgetAnnotation.checkBox :focus {
border: 2px solid var(--input-focus-border-color);
border-radius: 2px;
outline: var(--input-focus-outline);
}
.annotationLayer .buttonWidgetAnnotation.radioButton :focus {
border: 2px solid var(--input-focus-border-color);
outline: var(--input-focus-outline);
}
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,
.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after,
.annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before {
background-color: rgba(0, 0, 0, 1);
background-color: rgba(0, 0, 0, 1); // NOTE
content: "";
display: block;
position: absolute;
@ -163,32 +236,43 @@
.annotationLayer .buttonWidgetAnnotation.checkBox input,
.annotationLayer .buttonWidgetAnnotation.radioButton input {
appearance: none;
padding: 0;
}
.annotationLayer .popupTriggerArea {
height: 100%;
width: 100%;
}
.annotationLayer .fileAttachmentAnnotation .popupTriggerArea {
position: absolute;
}
.annotationLayer .popupWrapper {
position: absolute;
width: 20em;
font-size: calc(9px * var(--scale-factor));
width: 100%;
min-width: calc(180px * var(--scale-factor));
pointer-events: none;
}
.annotationLayer .popup {
position: absolute;
z-index: 200;
max-width: 20em;
max-width: calc(180px * var(--scale-factor));
background-color: rgba(255, 255, 153, 1);
box-shadow: 0 2px 5px rgba(136, 136, 136, 1);
border-radius: 2px;
padding: 6px;
margin-left: 5px;
box-shadow: 0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor))
rgba(136, 136, 136, 1);
border-radius: calc(2px * var(--scale-factor));
padding: calc(6px * var(--scale-factor));
margin-left: calc(5px * var(--scale-factor));
cursor: pointer;
font: message-box;
font-size: 9px;
white-space: normal;
word-wrap: break-word;
pointer-events: auto;
}
.annotationLayer .popup > * {
font-size: 9px;
font-size: calc(9px * var(--scale-factor));
}
.annotationLayer .popup h1 {
@ -197,17 +281,18 @@
.annotationLayer .popupDate {
display: inline-block;
margin-left: 5px;
margin-left: calc(5px * var(--scale-factor));
}
.annotationLayer .popupContent {
border-top: 1px solid rgba(51, 51, 51, 1);
margin-top: 2px;
padding-top: 2px;
margin-top: calc(2px * var(--scale-factor));
padding-top: calc(2px * var(--scale-factor));
}
.annotationLayer .richText > * {
white-space: pre-wrap;
font-size: calc(9px * var(--scale-factor));
}
.annotationLayer .highlightAnnotation,
@ -226,3 +311,26 @@
.annotationLayer .fileAttachmentAnnotation {
cursor: pointer;
}
.annotationLayer section svg {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.annotationLayer .annotationTextContent {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
color: transparent;
user-select: none;
pointer-events: none;
}
.annotationLayer .annotationTextContent span {
width: 100%;
display: inline-block;
}

View file

@ -17,29 +17,46 @@
@import "xfa_layer_builder";
:root {
--viewer-container-height: 0;
--pdfViewer-padding-bottom: 0;
--page-margin: 1px auto -8px;
--page-border: 9px solid transparent;
--spreadHorizontalWrapped-margin-LR: -3.5px;
--zoom-factor: 1;
--viewport-scale-factor: 1;
--loading-icon-delay: 400ms;
}
@media screen and (forced-colors: active) {
:root {
--pdfViewer-padding-bottom: 9px;
--page-margin: 9px auto 0;
--page-margin: 8px auto -1px;
--page-border: none;
--spreadHorizontalWrapped-margin-LR: 4.5px;
--spreadHorizontalWrapped-margin-LR: 3.5px;
}
}
[data-main-rotation="90"] {
transform: rotate(90deg) translateY(-100%);
}
[data-main-rotation="180"] {
transform: rotate(180deg) translate(-100%, -100%);
}
[data-main-rotation="270"] {
transform: rotate(270deg) translateX(-100%);
}
.pdfViewer {
/* Define this variable here and not in :root to avoid to reflow all the UI
when scaling (see #15929). */
--scale-factor: 1;
padding-bottom: var(--pdfViewer-padding-bottom);
}
.pdfViewer .canvasWrapper {
overflow: hidden;
width: 100%;
height: 100%;
z-index: 1;
}
.pdfViewer .page {
@ -51,21 +68,23 @@
overflow: visible;
border: var(--page-border);
background-clip: content-box;
// border-image: url(images/shadow.png) 9 9 repeat;
// background-color: rgba(255, 255, 255, 1);
background-color: rgba(255, 255, 255, 1);
}
.pdfViewer .dummyPage {
position: relative;
width: 0;
/* The height is set via JS, see `BaseViewer.#ensurePageViewVisible`. */
height: var(--viewer-container-height);
}
/*#if GENERIC*/
.pdfViewer.removePageBorders .page {
margin: 0 auto 10px;
border: none;
}
/*#endif*/
/*#if COMPONENTS*/
.pdfViewer.singlePageView {
display: inline-block;
}
@ -74,12 +93,12 @@
margin: 0;
border: none;
}
/*#endif*/
.pdfViewer.scrollHorizontal,
.pdfViewer.scrollWrapped,
.spread {
margin-left: 3.5px;
margin-right: 3.5px;
margin-inline: 3.5px;
text-align: center;
}
@ -88,11 +107,12 @@
white-space: nowrap;
}
/*#if GENERIC*/
.pdfViewer.removePageBorders,
/*#endif*/
.pdfViewer.scrollHorizontal .spread,
.pdfViewer.scrollWrapped .spread {
margin-left: 0;
margin-right: 0;
margin-inline: 0;
}
.spread .page,
@ -108,37 +128,59 @@
.spread .page,
.pdfViewer.scrollHorizontal .page,
.pdfViewer.scrollWrapped .page {
margin-left: var(--spreadHorizontalWrapped-margin-LR);
margin-right: var(--spreadHorizontalWrapped-margin-LR);
margin-inline: var(--spreadHorizontalWrapped-margin-LR);
}
/*#if GENERIC*/
.pdfViewer.removePageBorders .spread .page,
.pdfViewer.removePageBorders.scrollHorizontal .page,
.pdfViewer.removePageBorders.scrollWrapped .page {
margin-left: 5px;
margin-right: 5px;
margin-inline: 5px;
}
/*#endif*/
.pdfViewer .page canvas {
margin: 0;
display: block;
}
.pdfViewer .page canvas .structTree {
contain: strict;
}
.pdfViewer .page canvas[hidden] {
display: none;
}
.pdfViewer .page .loadingIcon {
position: absolute;
display: block;
left: 0;
top: 0;
right: 0;
bottom: 0;
// background: url("images/loading-icon.gif") center no-repeat;
.pdfViewer .page canvas[zooming] {
width: 100%;
height: 100%;
}
.pdfViewer .page .loadingIcon.notVisible {
background: none;
.pdfViewer .page.loadingIcon:after {
position: absolute;
top: 0;
left: 0;
content: "";
width: 100%;
height: 100%;
// NOTE background: url("images/loading-icon.gif") center no-repeat;
display: none;
/* Using a delay with background-image doesn't work,
consequently we use the display. */
transition-property: display;
transition-delay: var(--loading-icon-delay);
z-index: 5;
contain: strict;
}
.pdfViewer .page.loading:after {
display: block;
}
.pdfViewer .page:not(.loading):after {
transition-property: none;
display: none;
}
.pdfViewer.enablePermissions .textLayer span {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,27 @@
// Copyright 2014 PDFium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,102 @@
Digitized data copyright (c) 2010 Google Corporation
with Reserved Font Arimo, Tinos and Cousine.
Copyright (c) 2012 Red Hat, Inc.
with Reserved Font Name Liberation.
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
PREAMBLE The goals of the Open Font License (OFL) are to stimulate
worldwide development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to provide
a free and open framework in which fonts may be shared and improved in
partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves.
The fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such.
This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components
as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting ? in part or in whole ?
any of the components of the Original Version, by changing formats or
by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer
or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this license, and must not be distributed
under any other license. The requirement for fonts to remain under
this license does not apply to any document created using the Font
Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.