@progress/kendo-pdfviewer-common 1.0.0-develop.4 → 1.0.1-develop.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/annotations/annotation-layer-builder.js +6 -7
- package/dist/es/common/csp-styles.js +240 -0
- package/dist/es/common/dom.js +4 -1
- package/dist/es/text/text-layer-builder.js +4 -2
- package/dist/es/utils.js +10 -11
- package/dist/es/widget/pdfviewer.js +19 -8
- package/dist/es2015/annotations/annotation-layer-builder.js +6 -7
- package/dist/es2015/common/csp-styles.js +240 -0
- package/dist/es2015/common/dom.js +4 -1
- package/dist/es2015/text/text-layer-builder.js +4 -2
- package/dist/es2015/utils.js +10 -11
- package/dist/es2015/widget/pdfviewer.js +19 -8
- package/dist/npm/annotations/annotation-layer-builder.js +6 -7
- package/dist/npm/common/csp-styles.d.ts +8 -0
- package/dist/npm/common/csp-styles.js +249 -0
- package/dist/npm/common/dom.js +4 -1
- package/dist/npm/text/text-layer-builder.js +4 -2
- package/dist/npm/utils.js +10 -11
- package/dist/npm/widget/pdfviewer.js +19 -8
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib
|
|
|
22
22
|
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
|
23
23
|
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
|
24
24
|
import { convertToHtml } from "../common/core";
|
|
25
|
+
import { applyStyles } from "../common/csp-styles";
|
|
25
26
|
import { AnnotationLayer } from "./annotation-layer";
|
|
26
27
|
import { PresentationModeState } from "./shared/ui_utils";
|
|
27
28
|
// import { PresentationModeState } from "./shared/ui_utils";
|
|
@@ -132,13 +133,11 @@ class AnnotationLayerBuilder {
|
|
|
132
133
|
const pageView = (_a = page._pageInfo) === null || _a === void 0 ? void 0 : _a.view;
|
|
133
134
|
const pageWidthAnnotationLayer = (pageView[2] || 0) + "px";
|
|
134
135
|
const pageHeightAnnotationLayer = (pageView[3] || 0) + "px";
|
|
135
|
-
const div = convertToHtml(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</div>
|
|
141
|
-
`);
|
|
136
|
+
const div = convertToHtml(`<div class="k-annotation-layer" data-main-rotation="0"></div>`);
|
|
137
|
+
applyStyles(div, {
|
|
138
|
+
width: `round(var(--scale-factor) * ${pageWidthAnnotationLayer}, 1px)`,
|
|
139
|
+
height: `round(var(--scale-factor) * ${pageHeightAnnotationLayer}, 1px)`
|
|
140
|
+
});
|
|
142
141
|
this.div = div;
|
|
143
142
|
(_b = __classPrivateFieldGet(this, _AnnotationLayerBuilder_onAppend, "f")) === null || _b === void 0 ? void 0 : _b.call(this, div);
|
|
144
143
|
this.annotationLayer = new AnnotationLayer({
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
let sheet = null;
|
|
2
|
+
let counter = 0;
|
|
3
|
+
let sweepCounter = 0;
|
|
4
|
+
const rules = {};
|
|
5
|
+
const ATTR = 'data-kpv-sid';
|
|
6
|
+
let interceptorInstalled = false;
|
|
7
|
+
const interceptedRoots = new Set();
|
|
8
|
+
const elementRuleIds = new WeakMap();
|
|
9
|
+
const elementStyleAcc = new WeakMap();
|
|
10
|
+
const proxyCache = new WeakMap();
|
|
11
|
+
function camelToKebab(str) {
|
|
12
|
+
return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
function supportsAdoptedStylesheets() {
|
|
15
|
+
if (typeof document === 'undefined' || typeof CSSStyleSheet === 'undefined')
|
|
16
|
+
return false;
|
|
17
|
+
try {
|
|
18
|
+
new CSSStyleSheet();
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (_a) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function ensureSheet() {
|
|
26
|
+
if (!supportsAdoptedStylesheets())
|
|
27
|
+
return null;
|
|
28
|
+
if (!sheet) {
|
|
29
|
+
sheet = new CSSStyleSheet();
|
|
30
|
+
document.adoptedStyleSheets = [...(document.adoptedStyleSheets || []), sheet];
|
|
31
|
+
}
|
|
32
|
+
return sheet;
|
|
33
|
+
}
|
|
34
|
+
function syncSheet() {
|
|
35
|
+
if (sheet) {
|
|
36
|
+
sheet.replaceSync(Object.values(rules).join('\n'));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function applyStyles(element, styles, existingId) {
|
|
40
|
+
const id = existingId || ATTR + '-' + (++counter);
|
|
41
|
+
if (!ensureSheet()) {
|
|
42
|
+
Object.keys(styles).forEach(key => (element.style[key] = styles[key]));
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
element.setAttribute(ATTR, id);
|
|
46
|
+
const selector = `[${ATTR}="${id}"]`;
|
|
47
|
+
const cssText = Object.keys(styles)
|
|
48
|
+
.map(key => `${camelToKebab(key)}: ${styles[key]}`)
|
|
49
|
+
.join('; ');
|
|
50
|
+
rules[id] = `${selector} { ${cssText} }`;
|
|
51
|
+
syncSheet();
|
|
52
|
+
return id;
|
|
53
|
+
}
|
|
54
|
+
export function setCustomProperties(element, properties, ruleId) {
|
|
55
|
+
const id = ruleId || ATTR + '-' + (++counter);
|
|
56
|
+
if (!ensureSheet()) {
|
|
57
|
+
Object.keys(properties).forEach(key => element.style.setProperty(key, properties[key]));
|
|
58
|
+
return id;
|
|
59
|
+
}
|
|
60
|
+
element.setAttribute(ATTR, id);
|
|
61
|
+
const selector = `[${ATTR}="${id}"]`;
|
|
62
|
+
const cssText = Object.keys(properties)
|
|
63
|
+
.map(key => `${key}: ${properties[key]}`)
|
|
64
|
+
.join('; ');
|
|
65
|
+
rules[id] = `${selector} { ${cssText} }`;
|
|
66
|
+
syncSheet();
|
|
67
|
+
return id;
|
|
68
|
+
}
|
|
69
|
+
export function removeRule(id) {
|
|
70
|
+
delete rules[id];
|
|
71
|
+
syncSheet();
|
|
72
|
+
}
|
|
73
|
+
export function pruneRules() {
|
|
74
|
+
const toRemove = [];
|
|
75
|
+
for (const id of Object.keys(rules)) {
|
|
76
|
+
if (!document.querySelector(`[${ATTR}="${id}"]`)) {
|
|
77
|
+
toRemove.push(id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const id of toRemove) {
|
|
81
|
+
delete rules[id];
|
|
82
|
+
}
|
|
83
|
+
if (toRemove.length > 0) {
|
|
84
|
+
syncSheet();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function applyMergedCustomProp(element, prop, value) {
|
|
88
|
+
let acc = elementStyleAcc.get(element);
|
|
89
|
+
if (!acc) {
|
|
90
|
+
acc = {};
|
|
91
|
+
const existingId = element.getAttribute(ATTR);
|
|
92
|
+
if (existingId && rules[existingId]) {
|
|
93
|
+
const body = rules[existingId];
|
|
94
|
+
const startIdx = body.indexOf('{');
|
|
95
|
+
const endIdx = body.lastIndexOf('}');
|
|
96
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
97
|
+
body.substring(startIdx + 1, endIdx).split(';').forEach(decl => {
|
|
98
|
+
const colon = decl.indexOf(':');
|
|
99
|
+
if (colon > 0) {
|
|
100
|
+
const k = decl.substring(0, colon).trim();
|
|
101
|
+
const v = decl.substring(colon + 1).trim();
|
|
102
|
+
if (k) {
|
|
103
|
+
acc[k] = v;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
elementRuleIds.set(element, existingId);
|
|
109
|
+
}
|
|
110
|
+
elementStyleAcc.set(element, acc);
|
|
111
|
+
}
|
|
112
|
+
if (value === '' || value === null || value === undefined) {
|
|
113
|
+
delete acc[prop];
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
acc[prop] = value;
|
|
117
|
+
}
|
|
118
|
+
if ((++sweepCounter & 31) === 0) {
|
|
119
|
+
for (const rid of Object.keys(rules)) {
|
|
120
|
+
if (!document.querySelector(`[${ATTR}="${rid}"]`)) {
|
|
121
|
+
delete rules[rid];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const prevId = elementRuleIds.get(element);
|
|
126
|
+
const props = {};
|
|
127
|
+
const styles = {};
|
|
128
|
+
Object.keys(acc).forEach(k => {
|
|
129
|
+
if (k.startsWith('--')) {
|
|
130
|
+
props[k] = acc[k];
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
styles[k] = acc[k];
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
const cssText = []
|
|
137
|
+
.concat(Object.keys(styles).map(k => `${camelToKebab(k)}: ${styles[k]}`))
|
|
138
|
+
.concat(Object.keys(props).map(k => `${k}: ${props[k]}`))
|
|
139
|
+
.join('; ');
|
|
140
|
+
const id = prevId || ATTR + '-' + (++counter);
|
|
141
|
+
if (!ensureSheet()) {
|
|
142
|
+
Object.keys(styles).forEach(k => (element.style[k] = styles[k]));
|
|
143
|
+
Object.keys(props).forEach(k => element.style.setProperty(k, props[k]));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
element.setAttribute(ATTR, id);
|
|
147
|
+
rules[id] = `[${ATTR}="${id}"] { ${cssText} }`;
|
|
148
|
+
syncSheet();
|
|
149
|
+
if (!prevId) {
|
|
150
|
+
elementRuleIds.set(element, id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function isInIntercepted(el) {
|
|
154
|
+
if (!el || !el.nodeType)
|
|
155
|
+
return false;
|
|
156
|
+
for (const root of interceptedRoots) {
|
|
157
|
+
if (root === el || (root.contains && root.contains(el)))
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
export function installStyleInterceptor(root) {
|
|
163
|
+
if (root) {
|
|
164
|
+
interceptedRoots.add(root);
|
|
165
|
+
}
|
|
166
|
+
if (interceptorInstalled || typeof HTMLElement === 'undefined') {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'style')
|
|
170
|
+
|| Object.getOwnPropertyDescriptor(Element.prototype, 'style');
|
|
171
|
+
if (!descriptor || !descriptor.get) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
interceptorInstalled = true;
|
|
175
|
+
const origGetter = descriptor.get;
|
|
176
|
+
Object.defineProperty(HTMLElement.prototype, 'style', {
|
|
177
|
+
configurable: true,
|
|
178
|
+
enumerable: descriptor.enumerable,
|
|
179
|
+
get() {
|
|
180
|
+
const realStyle = origGetter.call(this);
|
|
181
|
+
if (!isInIntercepted(this)) {
|
|
182
|
+
return realStyle;
|
|
183
|
+
}
|
|
184
|
+
let proxy = proxyCache.get(realStyle);
|
|
185
|
+
if (proxy)
|
|
186
|
+
return proxy;
|
|
187
|
+
const element = this;
|
|
188
|
+
proxy = new Proxy(realStyle, {
|
|
189
|
+
set(target, prop, value) {
|
|
190
|
+
if (typeof prop !== 'string') {
|
|
191
|
+
target[prop] = value;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (prop === 'cssText') {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
applyMergedCustomProp(element, prop, value);
|
|
198
|
+
return true;
|
|
199
|
+
},
|
|
200
|
+
get(target, prop) {
|
|
201
|
+
if (prop === 'setProperty') {
|
|
202
|
+
return (name, value, priority) => {
|
|
203
|
+
applyMergedCustomProp(element, name, priority === 'important' ? `${value} !important` : value);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (prop === 'removeProperty') {
|
|
207
|
+
return (name) => {
|
|
208
|
+
const acc = elementStyleAcc.get(element);
|
|
209
|
+
const prev = acc ? acc[name] : '';
|
|
210
|
+
applyMergedCustomProp(element, name, '');
|
|
211
|
+
return prev || '';
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (prop === 'getPropertyValue') {
|
|
215
|
+
return (name) => {
|
|
216
|
+
const acc = elementStyleAcc.get(element);
|
|
217
|
+
return acc && acc[name] ? acc[name] : '';
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (typeof prop === 'string') {
|
|
221
|
+
const acc = elementStyleAcc.get(element);
|
|
222
|
+
if (acc && Object.prototype.hasOwnProperty.call(acc, prop)) {
|
|
223
|
+
return acc[prop];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const v = target[prop];
|
|
227
|
+
return typeof v === 'function' ? v.bind(target) : v;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
proxyCache.set(realStyle, proxy);
|
|
231
|
+
return proxy;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
export function uninstallStyleInterceptor(root) {
|
|
236
|
+
if (root) {
|
|
237
|
+
interceptedRoots.delete(root);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export { ATTR as STYLE_ATTR };
|
package/dist/es/common/dom.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyStyles } from './csp-styles';
|
|
1
2
|
export const addClass = (className, element) => {
|
|
2
3
|
element.classList.add(className);
|
|
3
4
|
};
|
|
@@ -14,7 +15,9 @@ export const createElement = function (name, className, styles) {
|
|
|
14
15
|
if (className) {
|
|
15
16
|
element.className = className;
|
|
16
17
|
}
|
|
17
|
-
Object.keys(styles).
|
|
18
|
+
if (styles && Object.keys(styles).length > 0) {
|
|
19
|
+
applyStyles(element, styles);
|
|
20
|
+
}
|
|
18
21
|
return element;
|
|
19
22
|
};
|
|
20
23
|
/**
|
|
@@ -16,6 +16,7 @@ var _TextLayerBuilder_instances, _a, _TextLayerBuilder_onAppend, _TextLayerBuild
|
|
|
16
16
|
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
17
17
|
import { TextLayer } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
18
18
|
import { addClass } from "../common/dom";
|
|
19
|
+
import { applyStyles } from "../common/csp-styles";
|
|
19
20
|
/**
|
|
20
21
|
* The text layer builder provides text selection functionality for the PDF.
|
|
21
22
|
* It does this by creating overlay divs over the PDF's text. These divs
|
|
@@ -49,8 +50,9 @@ class TextLayerBuilder {
|
|
|
49
50
|
// this.div.className = "textLayer";
|
|
50
51
|
// todo: fix classes
|
|
51
52
|
this.div.classList.add("k-text-layer");
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if (styles && Object.keys(styles).length > 0) {
|
|
54
|
+
applyStyles(this.div, styles);
|
|
55
|
+
}
|
|
54
56
|
}
|
|
55
57
|
/**
|
|
56
58
|
* Renders the text layer.
|
package/dist/es/utils.js
CHANGED
|
@@ -3,6 +3,7 @@ import { detectDesktopBrowser, detectMobileOS } from '@progress/kendo-common';
|
|
|
3
3
|
import { getDocument, TextLayer, PixelsPerInch } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
4
4
|
import { LinkAnnotation } from './annotations';
|
|
5
5
|
import { createElement, scrollToPage } from './common/dom';
|
|
6
|
+
import { applyStyles, STYLE_ATTR } from './common/csp-styles';
|
|
6
7
|
export { createElement, scrollToPage };
|
|
7
8
|
const MAX_CANVAS_WIDTH_HEIGHT_CHROME = 65535;
|
|
8
9
|
const MAX_CANVAS_AREA_CHROME_SAFARI = 268435456;
|
|
@@ -258,24 +259,22 @@ export const renderPage = (page, emptyPage, error) => {
|
|
|
258
259
|
container: textLayer,
|
|
259
260
|
viewport: viewport
|
|
260
261
|
}).render().then(() => {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// keeps font-size calculations intact while matching the page element exactly.
|
|
266
|
-
textLayer.style.width = styles.width;
|
|
267
|
-
textLayer.style.height = styles.height;
|
|
262
|
+
const ptStyles = {
|
|
263
|
+
width: styles.width,
|
|
264
|
+
height: styles.height
|
|
265
|
+
};
|
|
268
266
|
const rotation = textLayer.getAttribute('data-main-rotation') || '0';
|
|
269
267
|
if (transforms[rotation]) {
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
ptStyles.transform = transforms[rotation];
|
|
269
|
+
ptStyles.transformOrigin = 'top left';
|
|
272
270
|
}
|
|
271
|
+
applyStyles(textLayer, ptStyles, textLayer.getAttribute(STYLE_ATTR) || undefined);
|
|
273
272
|
textLayer.querySelectorAll('span').forEach((el) => {
|
|
274
273
|
if (el.style.fontSize) {
|
|
275
|
-
el
|
|
274
|
+
applyStyles(el, { fontSize: el.style.fontSize.replace(/px/g, 'pt') });
|
|
276
275
|
}
|
|
277
276
|
});
|
|
278
|
-
pageElement.prepend(textLayer);
|
|
277
|
+
pageElement.prepend(textLayer);
|
|
279
278
|
}).catch(error);
|
|
280
279
|
});
|
|
281
280
|
page.getAnnotations({ intent: 'display' }).then((annotations) => {
|
|
@@ -2,6 +2,7 @@ var _PdfViewer_instances, _PdfViewer_annotationEditorMode, _PdfViewer_switchAnno
|
|
|
2
2
|
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
3
|
import { deepExtend, Component, isString, toArray, noop, hasValue, toClassSelector, addClass, removeClass, mousewheelDelta, convertToHtml, clamp, createPromise } from "../common/main";
|
|
4
4
|
import { currentPage, parsePdfFromBase64String, adjustCanvasSize, createElement, scale as getScale, addAdoptedStylesheet } from "../utils";
|
|
5
|
+
import { setCustomProperties, applyStyles, pruneRules, STYLE_ATTR, installStyleInterceptor, uninstallStyleInterceptor } from "../common/csp-styles";
|
|
5
6
|
import { SearchService } from "../search";
|
|
6
7
|
import { Scroller, throttle } from "../scroller";
|
|
7
8
|
import { saveAs } from "@progress/kendo-file-saver";
|
|
@@ -353,6 +354,7 @@ export class PdfViewer extends Component {
|
|
|
353
354
|
}
|
|
354
355
|
this._resetPinchState();
|
|
355
356
|
};
|
|
357
|
+
installStyleInterceptor(element);
|
|
356
358
|
this.extendOptions(options);
|
|
357
359
|
this.throwIfInvalidOptions();
|
|
358
360
|
this.wrapper = this.element;
|
|
@@ -377,6 +379,8 @@ export class PdfViewer extends Component {
|
|
|
377
379
|
this.destroySearchService();
|
|
378
380
|
this.destroyAnnotationEditorUIManager();
|
|
379
381
|
this.destroyDocumentScroller();
|
|
382
|
+
pruneRules();
|
|
383
|
+
uninstallStyleInterceptor(this.element);
|
|
380
384
|
}
|
|
381
385
|
throwIfInvalidOptions() {
|
|
382
386
|
if (this.options.minZoom > this.options.maxZoom) {
|
|
@@ -905,12 +909,16 @@ export class PdfViewer extends Component {
|
|
|
905
909
|
canvas.height = adjustedHeight;
|
|
906
910
|
canvas.width = adjustedWidth;
|
|
907
911
|
if (printUnits) {
|
|
908
|
-
canvas
|
|
909
|
-
|
|
912
|
+
applyStyles(canvas, {
|
|
913
|
+
width: `${viewport.width / scaleNum}px`,
|
|
914
|
+
height: `${viewport.height / scaleNum}px`
|
|
915
|
+
});
|
|
910
916
|
}
|
|
911
917
|
else {
|
|
912
|
-
canvas
|
|
913
|
-
|
|
918
|
+
applyStyles(canvas, {
|
|
919
|
+
width: '100%',
|
|
920
|
+
height: '100%'
|
|
921
|
+
});
|
|
914
922
|
}
|
|
915
923
|
const canvasContext = canvas.getContext("2d");
|
|
916
924
|
if (printUnits) {
|
|
@@ -961,6 +969,7 @@ export class PdfViewer extends Component {
|
|
|
961
969
|
this.destroySearchService();
|
|
962
970
|
this.destroyAnnotationEditorUIManager();
|
|
963
971
|
this.clearDocumentState();
|
|
972
|
+
pruneRules();
|
|
964
973
|
}
|
|
965
974
|
clearDocumentState() {
|
|
966
975
|
var _a;
|
|
@@ -1457,10 +1466,12 @@ export class PdfViewer extends Component {
|
|
|
1457
1466
|
this.documentScroller.disablePanEventsTracking();
|
|
1458
1467
|
}
|
|
1459
1468
|
setScaleFactor(scaleFactor) {
|
|
1460
|
-
this.element
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1469
|
+
setCustomProperties(this.element, {
|
|
1470
|
+
'--scale-factor': String(scaleFactor * PixelsPerInch.PDF_TO_CSS_UNITS),
|
|
1471
|
+
'--total-scale-factor': String(scaleFactor * PixelsPerInch.PDF_TO_CSS_UNITS),
|
|
1472
|
+
'--scale-round-x': '1px',
|
|
1473
|
+
'--scale-round-y': '1px'
|
|
1474
|
+
}, this.element.getAttribute(STYLE_ATTR) || undefined);
|
|
1464
1475
|
}
|
|
1465
1476
|
_isTrackpadPinch(e) {
|
|
1466
1477
|
// Trackpad pinch gestures generate wheel events with synthetic ctrlKey=true.
|
|
@@ -22,6 +22,7 @@ import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib
|
|
|
22
22
|
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
|
23
23
|
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
|
24
24
|
import { convertToHtml } from "../common/core";
|
|
25
|
+
import { applyStyles } from "../common/csp-styles";
|
|
25
26
|
import { AnnotationLayer } from "./annotation-layer";
|
|
26
27
|
import { PresentationModeState } from "./shared/ui_utils";
|
|
27
28
|
// import { PresentationModeState } from "./shared/ui_utils";
|
|
@@ -132,13 +133,11 @@ class AnnotationLayerBuilder {
|
|
|
132
133
|
const pageView = (_a = page._pageInfo) === null || _a === void 0 ? void 0 : _a.view;
|
|
133
134
|
const pageWidthAnnotationLayer = (pageView[2] || 0) + "px";
|
|
134
135
|
const pageHeightAnnotationLayer = (pageView[3] || 0) + "px";
|
|
135
|
-
const div = convertToHtml(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</div>
|
|
141
|
-
`);
|
|
136
|
+
const div = convertToHtml(`<div class="k-annotation-layer" data-main-rotation="0"></div>`);
|
|
137
|
+
applyStyles(div, {
|
|
138
|
+
width: `round(var(--scale-factor) * ${pageWidthAnnotationLayer}, 1px)`,
|
|
139
|
+
height: `round(var(--scale-factor) * ${pageHeightAnnotationLayer}, 1px)`
|
|
140
|
+
});
|
|
142
141
|
this.div = div;
|
|
143
142
|
(_b = __classPrivateFieldGet(this, _AnnotationLayerBuilder_onAppend, "f")) === null || _b === void 0 ? void 0 : _b.call(this, div);
|
|
144
143
|
this.annotationLayer = new AnnotationLayer({
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
let sheet = null;
|
|
2
|
+
let counter = 0;
|
|
3
|
+
let sweepCounter = 0;
|
|
4
|
+
const rules = {};
|
|
5
|
+
const ATTR = 'data-kpv-sid';
|
|
6
|
+
let interceptorInstalled = false;
|
|
7
|
+
const interceptedRoots = new Set();
|
|
8
|
+
const elementRuleIds = new WeakMap();
|
|
9
|
+
const elementStyleAcc = new WeakMap();
|
|
10
|
+
const proxyCache = new WeakMap();
|
|
11
|
+
function camelToKebab(str) {
|
|
12
|
+
return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
function supportsAdoptedStylesheets() {
|
|
15
|
+
if (typeof document === 'undefined' || typeof CSSStyleSheet === 'undefined')
|
|
16
|
+
return false;
|
|
17
|
+
try {
|
|
18
|
+
new CSSStyleSheet();
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (_a) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function ensureSheet() {
|
|
26
|
+
if (!supportsAdoptedStylesheets())
|
|
27
|
+
return null;
|
|
28
|
+
if (!sheet) {
|
|
29
|
+
sheet = new CSSStyleSheet();
|
|
30
|
+
document.adoptedStyleSheets = [...(document.adoptedStyleSheets || []), sheet];
|
|
31
|
+
}
|
|
32
|
+
return sheet;
|
|
33
|
+
}
|
|
34
|
+
function syncSheet() {
|
|
35
|
+
if (sheet) {
|
|
36
|
+
sheet.replaceSync(Object.values(rules).join('\n'));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function applyStyles(element, styles, existingId) {
|
|
40
|
+
const id = existingId || ATTR + '-' + (++counter);
|
|
41
|
+
if (!ensureSheet()) {
|
|
42
|
+
Object.keys(styles).forEach(key => (element.style[key] = styles[key]));
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
element.setAttribute(ATTR, id);
|
|
46
|
+
const selector = `[${ATTR}="${id}"]`;
|
|
47
|
+
const cssText = Object.keys(styles)
|
|
48
|
+
.map(key => `${camelToKebab(key)}: ${styles[key]}`)
|
|
49
|
+
.join('; ');
|
|
50
|
+
rules[id] = `${selector} { ${cssText} }`;
|
|
51
|
+
syncSheet();
|
|
52
|
+
return id;
|
|
53
|
+
}
|
|
54
|
+
export function setCustomProperties(element, properties, ruleId) {
|
|
55
|
+
const id = ruleId || ATTR + '-' + (++counter);
|
|
56
|
+
if (!ensureSheet()) {
|
|
57
|
+
Object.keys(properties).forEach(key => element.style.setProperty(key, properties[key]));
|
|
58
|
+
return id;
|
|
59
|
+
}
|
|
60
|
+
element.setAttribute(ATTR, id);
|
|
61
|
+
const selector = `[${ATTR}="${id}"]`;
|
|
62
|
+
const cssText = Object.keys(properties)
|
|
63
|
+
.map(key => `${key}: ${properties[key]}`)
|
|
64
|
+
.join('; ');
|
|
65
|
+
rules[id] = `${selector} { ${cssText} }`;
|
|
66
|
+
syncSheet();
|
|
67
|
+
return id;
|
|
68
|
+
}
|
|
69
|
+
export function removeRule(id) {
|
|
70
|
+
delete rules[id];
|
|
71
|
+
syncSheet();
|
|
72
|
+
}
|
|
73
|
+
export function pruneRules() {
|
|
74
|
+
const toRemove = [];
|
|
75
|
+
for (const id of Object.keys(rules)) {
|
|
76
|
+
if (!document.querySelector(`[${ATTR}="${id}"]`)) {
|
|
77
|
+
toRemove.push(id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const id of toRemove) {
|
|
81
|
+
delete rules[id];
|
|
82
|
+
}
|
|
83
|
+
if (toRemove.length > 0) {
|
|
84
|
+
syncSheet();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function applyMergedCustomProp(element, prop, value) {
|
|
88
|
+
let acc = elementStyleAcc.get(element);
|
|
89
|
+
if (!acc) {
|
|
90
|
+
acc = {};
|
|
91
|
+
const existingId = element.getAttribute(ATTR);
|
|
92
|
+
if (existingId && rules[existingId]) {
|
|
93
|
+
const body = rules[existingId];
|
|
94
|
+
const startIdx = body.indexOf('{');
|
|
95
|
+
const endIdx = body.lastIndexOf('}');
|
|
96
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
97
|
+
body.substring(startIdx + 1, endIdx).split(';').forEach(decl => {
|
|
98
|
+
const colon = decl.indexOf(':');
|
|
99
|
+
if (colon > 0) {
|
|
100
|
+
const k = decl.substring(0, colon).trim();
|
|
101
|
+
const v = decl.substring(colon + 1).trim();
|
|
102
|
+
if (k) {
|
|
103
|
+
acc[k] = v;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
elementRuleIds.set(element, existingId);
|
|
109
|
+
}
|
|
110
|
+
elementStyleAcc.set(element, acc);
|
|
111
|
+
}
|
|
112
|
+
if (value === '' || value === null || value === undefined) {
|
|
113
|
+
delete acc[prop];
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
acc[prop] = value;
|
|
117
|
+
}
|
|
118
|
+
if ((++sweepCounter & 31) === 0) {
|
|
119
|
+
for (const rid of Object.keys(rules)) {
|
|
120
|
+
if (!document.querySelector(`[${ATTR}="${rid}"]`)) {
|
|
121
|
+
delete rules[rid];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const prevId = elementRuleIds.get(element);
|
|
126
|
+
const props = {};
|
|
127
|
+
const styles = {};
|
|
128
|
+
Object.keys(acc).forEach(k => {
|
|
129
|
+
if (k.startsWith('--')) {
|
|
130
|
+
props[k] = acc[k];
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
styles[k] = acc[k];
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
const cssText = []
|
|
137
|
+
.concat(Object.keys(styles).map(k => `${camelToKebab(k)}: ${styles[k]}`))
|
|
138
|
+
.concat(Object.keys(props).map(k => `${k}: ${props[k]}`))
|
|
139
|
+
.join('; ');
|
|
140
|
+
const id = prevId || ATTR + '-' + (++counter);
|
|
141
|
+
if (!ensureSheet()) {
|
|
142
|
+
Object.keys(styles).forEach(k => (element.style[k] = styles[k]));
|
|
143
|
+
Object.keys(props).forEach(k => element.style.setProperty(k, props[k]));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
element.setAttribute(ATTR, id);
|
|
147
|
+
rules[id] = `[${ATTR}="${id}"] { ${cssText} }`;
|
|
148
|
+
syncSheet();
|
|
149
|
+
if (!prevId) {
|
|
150
|
+
elementRuleIds.set(element, id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function isInIntercepted(el) {
|
|
154
|
+
if (!el || !el.nodeType)
|
|
155
|
+
return false;
|
|
156
|
+
for (const root of interceptedRoots) {
|
|
157
|
+
if (root === el || (root.contains && root.contains(el)))
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
export function installStyleInterceptor(root) {
|
|
163
|
+
if (root) {
|
|
164
|
+
interceptedRoots.add(root);
|
|
165
|
+
}
|
|
166
|
+
if (interceptorInstalled || typeof HTMLElement === 'undefined') {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'style')
|
|
170
|
+
|| Object.getOwnPropertyDescriptor(Element.prototype, 'style');
|
|
171
|
+
if (!descriptor || !descriptor.get) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
interceptorInstalled = true;
|
|
175
|
+
const origGetter = descriptor.get;
|
|
176
|
+
Object.defineProperty(HTMLElement.prototype, 'style', {
|
|
177
|
+
configurable: true,
|
|
178
|
+
enumerable: descriptor.enumerable,
|
|
179
|
+
get() {
|
|
180
|
+
const realStyle = origGetter.call(this);
|
|
181
|
+
if (!isInIntercepted(this)) {
|
|
182
|
+
return realStyle;
|
|
183
|
+
}
|
|
184
|
+
let proxy = proxyCache.get(realStyle);
|
|
185
|
+
if (proxy)
|
|
186
|
+
return proxy;
|
|
187
|
+
const element = this;
|
|
188
|
+
proxy = new Proxy(realStyle, {
|
|
189
|
+
set(target, prop, value) {
|
|
190
|
+
if (typeof prop !== 'string') {
|
|
191
|
+
target[prop] = value;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (prop === 'cssText') {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
applyMergedCustomProp(element, prop, value);
|
|
198
|
+
return true;
|
|
199
|
+
},
|
|
200
|
+
get(target, prop) {
|
|
201
|
+
if (prop === 'setProperty') {
|
|
202
|
+
return (name, value, priority) => {
|
|
203
|
+
applyMergedCustomProp(element, name, priority === 'important' ? `${value} !important` : value);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (prop === 'removeProperty') {
|
|
207
|
+
return (name) => {
|
|
208
|
+
const acc = elementStyleAcc.get(element);
|
|
209
|
+
const prev = acc ? acc[name] : '';
|
|
210
|
+
applyMergedCustomProp(element, name, '');
|
|
211
|
+
return prev || '';
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (prop === 'getPropertyValue') {
|
|
215
|
+
return (name) => {
|
|
216
|
+
const acc = elementStyleAcc.get(element);
|
|
217
|
+
return acc && acc[name] ? acc[name] : '';
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (typeof prop === 'string') {
|
|
221
|
+
const acc = elementStyleAcc.get(element);
|
|
222
|
+
if (acc && Object.prototype.hasOwnProperty.call(acc, prop)) {
|
|
223
|
+
return acc[prop];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const v = target[prop];
|
|
227
|
+
return typeof v === 'function' ? v.bind(target) : v;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
proxyCache.set(realStyle, proxy);
|
|
231
|
+
return proxy;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
export function uninstallStyleInterceptor(root) {
|
|
236
|
+
if (root) {
|
|
237
|
+
interceptedRoots.delete(root);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export { ATTR as STYLE_ATTR };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyStyles } from './csp-styles';
|
|
1
2
|
export const addClass = (className, element) => {
|
|
2
3
|
element.classList.add(className);
|
|
3
4
|
};
|
|
@@ -14,7 +15,9 @@ export const createElement = function (name, className, styles) {
|
|
|
14
15
|
if (className) {
|
|
15
16
|
element.className = className;
|
|
16
17
|
}
|
|
17
|
-
Object.keys(styles).
|
|
18
|
+
if (styles && Object.keys(styles).length > 0) {
|
|
19
|
+
applyStyles(element, styles);
|
|
20
|
+
}
|
|
18
21
|
return element;
|
|
19
22
|
};
|
|
20
23
|
/**
|
|
@@ -16,6 +16,7 @@ var _TextLayerBuilder_instances, _a, _TextLayerBuilder_onAppend, _TextLayerBuild
|
|
|
16
16
|
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
17
17
|
import { TextLayer } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
18
18
|
import { addClass } from "../common/dom";
|
|
19
|
+
import { applyStyles } from "../common/csp-styles";
|
|
19
20
|
/**
|
|
20
21
|
* The text layer builder provides text selection functionality for the PDF.
|
|
21
22
|
* It does this by creating overlay divs over the PDF's text. These divs
|
|
@@ -49,8 +50,9 @@ class TextLayerBuilder {
|
|
|
49
50
|
// this.div.className = "textLayer";
|
|
50
51
|
// todo: fix classes
|
|
51
52
|
this.div.classList.add("k-text-layer");
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if (styles && Object.keys(styles).length > 0) {
|
|
54
|
+
applyStyles(this.div, styles);
|
|
55
|
+
}
|
|
54
56
|
}
|
|
55
57
|
/**
|
|
56
58
|
* Renders the text layer.
|
package/dist/es2015/utils.js
CHANGED
|
@@ -3,6 +3,7 @@ import { detectDesktopBrowser, detectMobileOS } from '@progress/kendo-common';
|
|
|
3
3
|
import { getDocument, TextLayer, PixelsPerInch } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
4
4
|
import { LinkAnnotation } from './annotations';
|
|
5
5
|
import { createElement, scrollToPage } from './common/dom';
|
|
6
|
+
import { applyStyles, STYLE_ATTR } from './common/csp-styles';
|
|
6
7
|
export { createElement, scrollToPage };
|
|
7
8
|
const MAX_CANVAS_WIDTH_HEIGHT_CHROME = 65535;
|
|
8
9
|
const MAX_CANVAS_AREA_CHROME_SAFARI = 268435456;
|
|
@@ -258,24 +259,22 @@ export const renderPage = (page, emptyPage, error) => {
|
|
|
258
259
|
container: textLayer,
|
|
259
260
|
viewport: viewport
|
|
260
261
|
}).render().then(() => {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// keeps font-size calculations intact while matching the page element exactly.
|
|
266
|
-
textLayer.style.width = styles.width;
|
|
267
|
-
textLayer.style.height = styles.height;
|
|
262
|
+
const ptStyles = {
|
|
263
|
+
width: styles.width,
|
|
264
|
+
height: styles.height
|
|
265
|
+
};
|
|
268
266
|
const rotation = textLayer.getAttribute('data-main-rotation') || '0';
|
|
269
267
|
if (transforms[rotation]) {
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
ptStyles.transform = transforms[rotation];
|
|
269
|
+
ptStyles.transformOrigin = 'top left';
|
|
272
270
|
}
|
|
271
|
+
applyStyles(textLayer, ptStyles, textLayer.getAttribute(STYLE_ATTR) || undefined);
|
|
273
272
|
textLayer.querySelectorAll('span').forEach((el) => {
|
|
274
273
|
if (el.style.fontSize) {
|
|
275
|
-
el
|
|
274
|
+
applyStyles(el, { fontSize: el.style.fontSize.replace(/px/g, 'pt') });
|
|
276
275
|
}
|
|
277
276
|
});
|
|
278
|
-
pageElement.prepend(textLayer);
|
|
277
|
+
pageElement.prepend(textLayer);
|
|
279
278
|
}).catch(error);
|
|
280
279
|
});
|
|
281
280
|
page.getAnnotations({ intent: 'display' }).then((annotations) => {
|
|
@@ -2,6 +2,7 @@ var _PdfViewer_instances, _PdfViewer_annotationEditorMode, _PdfViewer_switchAnno
|
|
|
2
2
|
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
3
|
import { deepExtend, Component, isString, toArray, noop, hasValue, toClassSelector, addClass, removeClass, mousewheelDelta, convertToHtml, clamp, createPromise } from "../common/main";
|
|
4
4
|
import { currentPage, parsePdfFromBase64String, adjustCanvasSize, createElement, scale as getScale, addAdoptedStylesheet } from "../utils";
|
|
5
|
+
import { setCustomProperties, applyStyles, pruneRules, STYLE_ATTR, installStyleInterceptor, uninstallStyleInterceptor } from "../common/csp-styles";
|
|
5
6
|
import { SearchService } from "../search";
|
|
6
7
|
import { Scroller, throttle } from "../scroller";
|
|
7
8
|
import { saveAs } from "@progress/kendo-file-saver";
|
|
@@ -353,6 +354,7 @@ export class PdfViewer extends Component {
|
|
|
353
354
|
}
|
|
354
355
|
this._resetPinchState();
|
|
355
356
|
};
|
|
357
|
+
installStyleInterceptor(element);
|
|
356
358
|
this.extendOptions(options);
|
|
357
359
|
this.throwIfInvalidOptions();
|
|
358
360
|
this.wrapper = this.element;
|
|
@@ -377,6 +379,8 @@ export class PdfViewer extends Component {
|
|
|
377
379
|
this.destroySearchService();
|
|
378
380
|
this.destroyAnnotationEditorUIManager();
|
|
379
381
|
this.destroyDocumentScroller();
|
|
382
|
+
pruneRules();
|
|
383
|
+
uninstallStyleInterceptor(this.element);
|
|
380
384
|
}
|
|
381
385
|
throwIfInvalidOptions() {
|
|
382
386
|
if (this.options.minZoom > this.options.maxZoom) {
|
|
@@ -905,12 +909,16 @@ export class PdfViewer extends Component {
|
|
|
905
909
|
canvas.height = adjustedHeight;
|
|
906
910
|
canvas.width = adjustedWidth;
|
|
907
911
|
if (printUnits) {
|
|
908
|
-
canvas
|
|
909
|
-
|
|
912
|
+
applyStyles(canvas, {
|
|
913
|
+
width: `${viewport.width / scaleNum}px`,
|
|
914
|
+
height: `${viewport.height / scaleNum}px`
|
|
915
|
+
});
|
|
910
916
|
}
|
|
911
917
|
else {
|
|
912
|
-
canvas
|
|
913
|
-
|
|
918
|
+
applyStyles(canvas, {
|
|
919
|
+
width: '100%',
|
|
920
|
+
height: '100%'
|
|
921
|
+
});
|
|
914
922
|
}
|
|
915
923
|
const canvasContext = canvas.getContext("2d");
|
|
916
924
|
if (printUnits) {
|
|
@@ -961,6 +969,7 @@ export class PdfViewer extends Component {
|
|
|
961
969
|
this.destroySearchService();
|
|
962
970
|
this.destroyAnnotationEditorUIManager();
|
|
963
971
|
this.clearDocumentState();
|
|
972
|
+
pruneRules();
|
|
964
973
|
}
|
|
965
974
|
clearDocumentState() {
|
|
966
975
|
var _a;
|
|
@@ -1457,10 +1466,12 @@ export class PdfViewer extends Component {
|
|
|
1457
1466
|
this.documentScroller.disablePanEventsTracking();
|
|
1458
1467
|
}
|
|
1459
1468
|
setScaleFactor(scaleFactor) {
|
|
1460
|
-
this.element
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1469
|
+
setCustomProperties(this.element, {
|
|
1470
|
+
'--scale-factor': String(scaleFactor * PixelsPerInch.PDF_TO_CSS_UNITS),
|
|
1471
|
+
'--total-scale-factor': String(scaleFactor * PixelsPerInch.PDF_TO_CSS_UNITS),
|
|
1472
|
+
'--scale-round-x': '1px',
|
|
1473
|
+
'--scale-round-y': '1px'
|
|
1474
|
+
}, this.element.getAttribute(STYLE_ATTR) || undefined);
|
|
1464
1475
|
}
|
|
1465
1476
|
_isTrackpadPinch(e) {
|
|
1466
1477
|
// Trackpad pinch gestures generate wheel events with synthetic ctrlKey=true.
|
|
@@ -25,6 +25,7 @@ const tslib_1 = require("tslib");
|
|
|
25
25
|
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
|
26
26
|
/** @typedef {import("../src/display/editor/tools.js").AnnotationEditorUIManager} AnnotationEditorUIManager */
|
|
27
27
|
const core_1 = require("../common/core");
|
|
28
|
+
const csp_styles_1 = require("../common/csp-styles");
|
|
28
29
|
const annotation_layer_1 = require("./annotation-layer");
|
|
29
30
|
const ui_utils_1 = require("./shared/ui_utils");
|
|
30
31
|
// import { PresentationModeState } from "./shared/ui_utils";
|
|
@@ -135,13 +136,11 @@ class AnnotationLayerBuilder {
|
|
|
135
136
|
const pageView = (_a = page._pageInfo) === null || _a === void 0 ? void 0 : _a.view;
|
|
136
137
|
const pageWidthAnnotationLayer = (pageView[2] || 0) + "px";
|
|
137
138
|
const pageHeightAnnotationLayer = (pageView[3] || 0) + "px";
|
|
138
|
-
const div = (0, core_1.convertToHtml)(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
</div>
|
|
144
|
-
`);
|
|
139
|
+
const div = (0, core_1.convertToHtml)(`<div class="k-annotation-layer" data-main-rotation="0"></div>`);
|
|
140
|
+
(0, csp_styles_1.applyStyles)(div, {
|
|
141
|
+
width: `round(var(--scale-factor) * ${pageWidthAnnotationLayer}, 1px)`,
|
|
142
|
+
height: `round(var(--scale-factor) * ${pageHeightAnnotationLayer}, 1px)`
|
|
143
|
+
});
|
|
145
144
|
this.div = div;
|
|
146
145
|
(_b = tslib_1.__classPrivateFieldGet(this, _AnnotationLayerBuilder_onAppend, "f")) === null || _b === void 0 ? void 0 : _b.call(this, div);
|
|
147
146
|
this.annotationLayer = new annotation_layer_1.AnnotationLayer({
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const ATTR = "data-kpv-sid";
|
|
2
|
+
export declare function applyStyles(element: HTMLElement, styles: Record<string, string>, existingId?: string): string;
|
|
3
|
+
export declare function setCustomProperties(element: HTMLElement, properties: Record<string, string>, ruleId?: string): string;
|
|
4
|
+
export declare function removeRule(id: string): void;
|
|
5
|
+
export declare function pruneRules(): void;
|
|
6
|
+
export declare function installStyleInterceptor(root: HTMLElement): void;
|
|
7
|
+
export declare function uninstallStyleInterceptor(root: HTMLElement): void;
|
|
8
|
+
export { ATTR as STYLE_ATTR };
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.STYLE_ATTR = void 0;
|
|
4
|
+
exports.applyStyles = applyStyles;
|
|
5
|
+
exports.setCustomProperties = setCustomProperties;
|
|
6
|
+
exports.removeRule = removeRule;
|
|
7
|
+
exports.pruneRules = pruneRules;
|
|
8
|
+
exports.installStyleInterceptor = installStyleInterceptor;
|
|
9
|
+
exports.uninstallStyleInterceptor = uninstallStyleInterceptor;
|
|
10
|
+
let sheet = null;
|
|
11
|
+
let counter = 0;
|
|
12
|
+
let sweepCounter = 0;
|
|
13
|
+
const rules = {};
|
|
14
|
+
const ATTR = 'data-kpv-sid';
|
|
15
|
+
exports.STYLE_ATTR = ATTR;
|
|
16
|
+
let interceptorInstalled = false;
|
|
17
|
+
const interceptedRoots = new Set();
|
|
18
|
+
const elementRuleIds = new WeakMap();
|
|
19
|
+
const elementStyleAcc = new WeakMap();
|
|
20
|
+
const proxyCache = new WeakMap();
|
|
21
|
+
function camelToKebab(str) {
|
|
22
|
+
return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
23
|
+
}
|
|
24
|
+
function supportsAdoptedStylesheets() {
|
|
25
|
+
if (typeof document === 'undefined' || typeof CSSStyleSheet === 'undefined')
|
|
26
|
+
return false;
|
|
27
|
+
try {
|
|
28
|
+
new CSSStyleSheet();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (_a) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function ensureSheet() {
|
|
36
|
+
if (!supportsAdoptedStylesheets())
|
|
37
|
+
return null;
|
|
38
|
+
if (!sheet) {
|
|
39
|
+
sheet = new CSSStyleSheet();
|
|
40
|
+
document.adoptedStyleSheets = [...(document.adoptedStyleSheets || []), sheet];
|
|
41
|
+
}
|
|
42
|
+
return sheet;
|
|
43
|
+
}
|
|
44
|
+
function syncSheet() {
|
|
45
|
+
if (sheet) {
|
|
46
|
+
sheet.replaceSync(Object.values(rules).join('\n'));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function applyStyles(element, styles, existingId) {
|
|
50
|
+
const id = existingId || ATTR + '-' + (++counter);
|
|
51
|
+
if (!ensureSheet()) {
|
|
52
|
+
Object.keys(styles).forEach(key => (element.style[key] = styles[key]));
|
|
53
|
+
return id;
|
|
54
|
+
}
|
|
55
|
+
element.setAttribute(ATTR, id);
|
|
56
|
+
const selector = `[${ATTR}="${id}"]`;
|
|
57
|
+
const cssText = Object.keys(styles)
|
|
58
|
+
.map(key => `${camelToKebab(key)}: ${styles[key]}`)
|
|
59
|
+
.join('; ');
|
|
60
|
+
rules[id] = `${selector} { ${cssText} }`;
|
|
61
|
+
syncSheet();
|
|
62
|
+
return id;
|
|
63
|
+
}
|
|
64
|
+
function setCustomProperties(element, properties, ruleId) {
|
|
65
|
+
const id = ruleId || ATTR + '-' + (++counter);
|
|
66
|
+
if (!ensureSheet()) {
|
|
67
|
+
Object.keys(properties).forEach(key => element.style.setProperty(key, properties[key]));
|
|
68
|
+
return id;
|
|
69
|
+
}
|
|
70
|
+
element.setAttribute(ATTR, id);
|
|
71
|
+
const selector = `[${ATTR}="${id}"]`;
|
|
72
|
+
const cssText = Object.keys(properties)
|
|
73
|
+
.map(key => `${key}: ${properties[key]}`)
|
|
74
|
+
.join('; ');
|
|
75
|
+
rules[id] = `${selector} { ${cssText} }`;
|
|
76
|
+
syncSheet();
|
|
77
|
+
return id;
|
|
78
|
+
}
|
|
79
|
+
function removeRule(id) {
|
|
80
|
+
delete rules[id];
|
|
81
|
+
syncSheet();
|
|
82
|
+
}
|
|
83
|
+
function pruneRules() {
|
|
84
|
+
const toRemove = [];
|
|
85
|
+
for (const id of Object.keys(rules)) {
|
|
86
|
+
if (!document.querySelector(`[${ATTR}="${id}"]`)) {
|
|
87
|
+
toRemove.push(id);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const id of toRemove) {
|
|
91
|
+
delete rules[id];
|
|
92
|
+
}
|
|
93
|
+
if (toRemove.length > 0) {
|
|
94
|
+
syncSheet();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function applyMergedCustomProp(element, prop, value) {
|
|
98
|
+
let acc = elementStyleAcc.get(element);
|
|
99
|
+
if (!acc) {
|
|
100
|
+
acc = {};
|
|
101
|
+
const existingId = element.getAttribute(ATTR);
|
|
102
|
+
if (existingId && rules[existingId]) {
|
|
103
|
+
const body = rules[existingId];
|
|
104
|
+
const startIdx = body.indexOf('{');
|
|
105
|
+
const endIdx = body.lastIndexOf('}');
|
|
106
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
107
|
+
body.substring(startIdx + 1, endIdx).split(';').forEach(decl => {
|
|
108
|
+
const colon = decl.indexOf(':');
|
|
109
|
+
if (colon > 0) {
|
|
110
|
+
const k = decl.substring(0, colon).trim();
|
|
111
|
+
const v = decl.substring(colon + 1).trim();
|
|
112
|
+
if (k) {
|
|
113
|
+
acc[k] = v;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
elementRuleIds.set(element, existingId);
|
|
119
|
+
}
|
|
120
|
+
elementStyleAcc.set(element, acc);
|
|
121
|
+
}
|
|
122
|
+
if (value === '' || value === null || value === undefined) {
|
|
123
|
+
delete acc[prop];
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
acc[prop] = value;
|
|
127
|
+
}
|
|
128
|
+
if ((++sweepCounter & 31) === 0) {
|
|
129
|
+
for (const rid of Object.keys(rules)) {
|
|
130
|
+
if (!document.querySelector(`[${ATTR}="${rid}"]`)) {
|
|
131
|
+
delete rules[rid];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const prevId = elementRuleIds.get(element);
|
|
136
|
+
const props = {};
|
|
137
|
+
const styles = {};
|
|
138
|
+
Object.keys(acc).forEach(k => {
|
|
139
|
+
if (k.startsWith('--')) {
|
|
140
|
+
props[k] = acc[k];
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
styles[k] = acc[k];
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
const cssText = []
|
|
147
|
+
.concat(Object.keys(styles).map(k => `${camelToKebab(k)}: ${styles[k]}`))
|
|
148
|
+
.concat(Object.keys(props).map(k => `${k}: ${props[k]}`))
|
|
149
|
+
.join('; ');
|
|
150
|
+
const id = prevId || ATTR + '-' + (++counter);
|
|
151
|
+
if (!ensureSheet()) {
|
|
152
|
+
Object.keys(styles).forEach(k => (element.style[k] = styles[k]));
|
|
153
|
+
Object.keys(props).forEach(k => element.style.setProperty(k, props[k]));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
element.setAttribute(ATTR, id);
|
|
157
|
+
rules[id] = `[${ATTR}="${id}"] { ${cssText} }`;
|
|
158
|
+
syncSheet();
|
|
159
|
+
if (!prevId) {
|
|
160
|
+
elementRuleIds.set(element, id);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function isInIntercepted(el) {
|
|
164
|
+
if (!el || !el.nodeType)
|
|
165
|
+
return false;
|
|
166
|
+
for (const root of interceptedRoots) {
|
|
167
|
+
if (root === el || (root.contains && root.contains(el)))
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
function installStyleInterceptor(root) {
|
|
173
|
+
if (root) {
|
|
174
|
+
interceptedRoots.add(root);
|
|
175
|
+
}
|
|
176
|
+
if (interceptorInstalled || typeof HTMLElement === 'undefined') {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'style')
|
|
180
|
+
|| Object.getOwnPropertyDescriptor(Element.prototype, 'style');
|
|
181
|
+
if (!descriptor || !descriptor.get) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
interceptorInstalled = true;
|
|
185
|
+
const origGetter = descriptor.get;
|
|
186
|
+
Object.defineProperty(HTMLElement.prototype, 'style', {
|
|
187
|
+
configurable: true,
|
|
188
|
+
enumerable: descriptor.enumerable,
|
|
189
|
+
get() {
|
|
190
|
+
const realStyle = origGetter.call(this);
|
|
191
|
+
if (!isInIntercepted(this)) {
|
|
192
|
+
return realStyle;
|
|
193
|
+
}
|
|
194
|
+
let proxy = proxyCache.get(realStyle);
|
|
195
|
+
if (proxy)
|
|
196
|
+
return proxy;
|
|
197
|
+
const element = this;
|
|
198
|
+
proxy = new Proxy(realStyle, {
|
|
199
|
+
set(target, prop, value) {
|
|
200
|
+
if (typeof prop !== 'string') {
|
|
201
|
+
target[prop] = value;
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
if (prop === 'cssText') {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
applyMergedCustomProp(element, prop, value);
|
|
208
|
+
return true;
|
|
209
|
+
},
|
|
210
|
+
get(target, prop) {
|
|
211
|
+
if (prop === 'setProperty') {
|
|
212
|
+
return (name, value, priority) => {
|
|
213
|
+
applyMergedCustomProp(element, name, priority === 'important' ? `${value} !important` : value);
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (prop === 'removeProperty') {
|
|
217
|
+
return (name) => {
|
|
218
|
+
const acc = elementStyleAcc.get(element);
|
|
219
|
+
const prev = acc ? acc[name] : '';
|
|
220
|
+
applyMergedCustomProp(element, name, '');
|
|
221
|
+
return prev || '';
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (prop === 'getPropertyValue') {
|
|
225
|
+
return (name) => {
|
|
226
|
+
const acc = elementStyleAcc.get(element);
|
|
227
|
+
return acc && acc[name] ? acc[name] : '';
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (typeof prop === 'string') {
|
|
231
|
+
const acc = elementStyleAcc.get(element);
|
|
232
|
+
if (acc && Object.prototype.hasOwnProperty.call(acc, prop)) {
|
|
233
|
+
return acc[prop];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const v = target[prop];
|
|
237
|
+
return typeof v === 'function' ? v.bind(target) : v;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
proxyCache.set(realStyle, proxy);
|
|
241
|
+
return proxy;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function uninstallStyleInterceptor(root) {
|
|
246
|
+
if (root) {
|
|
247
|
+
interceptedRoots.delete(root);
|
|
248
|
+
}
|
|
249
|
+
}
|
package/dist/npm/common/dom.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.scrollToPage = exports.createElement = exports.hasClass = exports.removeClass = exports.addClass = void 0;
|
|
4
|
+
const csp_styles_1 = require("./csp-styles");
|
|
4
5
|
const addClass = (className, element) => {
|
|
5
6
|
element.classList.add(className);
|
|
6
7
|
};
|
|
@@ -20,7 +21,9 @@ const createElement = function (name, className, styles) {
|
|
|
20
21
|
if (className) {
|
|
21
22
|
element.className = className;
|
|
22
23
|
}
|
|
23
|
-
Object.keys(styles).
|
|
24
|
+
if (styles && Object.keys(styles).length > 0) {
|
|
25
|
+
(0, csp_styles_1.applyStyles)(element, styles);
|
|
26
|
+
}
|
|
24
27
|
return element;
|
|
25
28
|
};
|
|
26
29
|
exports.createElement = createElement;
|
|
@@ -19,6 +19,7 @@ exports.TextLayerBuilder = void 0;
|
|
|
19
19
|
const tslib_1 = require("tslib");
|
|
20
20
|
const pdf_mjs_1 = require("pdfjs-dist/legacy/build/pdf.mjs");
|
|
21
21
|
const dom_1 = require("../common/dom");
|
|
22
|
+
const csp_styles_1 = require("../common/csp-styles");
|
|
22
23
|
/**
|
|
23
24
|
* The text layer builder provides text selection functionality for the PDF.
|
|
24
25
|
* It does this by creating overlay divs over the PDF's text. These divs
|
|
@@ -52,8 +53,9 @@ class TextLayerBuilder {
|
|
|
52
53
|
// this.div.className = "textLayer";
|
|
53
54
|
// todo: fix classes
|
|
54
55
|
this.div.classList.add("k-text-layer");
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
if (styles && Object.keys(styles).length > 0) {
|
|
57
|
+
(0, csp_styles_1.applyStyles)(this.div, styles);
|
|
58
|
+
}
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* Renders the text layer.
|
package/dist/npm/utils.js
CHANGED
|
@@ -8,6 +8,7 @@ const annotations_1 = require("./annotations");
|
|
|
8
8
|
const dom_1 = require("./common/dom");
|
|
9
9
|
Object.defineProperty(exports, "createElement", { enumerable: true, get: function () { return dom_1.createElement; } });
|
|
10
10
|
Object.defineProperty(exports, "scrollToPage", { enumerable: true, get: function () { return dom_1.scrollToPage; } });
|
|
11
|
+
const csp_styles_1 = require("./common/csp-styles");
|
|
11
12
|
const MAX_CANVAS_WIDTH_HEIGHT_CHROME = 65535;
|
|
12
13
|
const MAX_CANVAS_AREA_CHROME_SAFARI = 268435456;
|
|
13
14
|
const MAX_CANVAS_WIDTH_HEIGHT_FIREFOX = 32767;
|
|
@@ -269,24 +270,22 @@ const renderPage = (page, emptyPage, error) => {
|
|
|
269
270
|
container: textLayer,
|
|
270
271
|
viewport: viewport
|
|
271
272
|
}).render().then(() => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
// keeps font-size calculations intact while matching the page element exactly.
|
|
277
|
-
textLayer.style.width = styles.width;
|
|
278
|
-
textLayer.style.height = styles.height;
|
|
273
|
+
const ptStyles = {
|
|
274
|
+
width: styles.width,
|
|
275
|
+
height: styles.height
|
|
276
|
+
};
|
|
279
277
|
const rotation = textLayer.getAttribute('data-main-rotation') || '0';
|
|
280
278
|
if (exports.transforms[rotation]) {
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
ptStyles.transform = exports.transforms[rotation];
|
|
280
|
+
ptStyles.transformOrigin = 'top left';
|
|
283
281
|
}
|
|
282
|
+
(0, csp_styles_1.applyStyles)(textLayer, ptStyles, textLayer.getAttribute(csp_styles_1.STYLE_ATTR) || undefined);
|
|
284
283
|
textLayer.querySelectorAll('span').forEach((el) => {
|
|
285
284
|
if (el.style.fontSize) {
|
|
286
|
-
el
|
|
285
|
+
(0, csp_styles_1.applyStyles)(el, { fontSize: el.style.fontSize.replace(/px/g, 'pt') });
|
|
287
286
|
}
|
|
288
287
|
});
|
|
289
|
-
pageElement.prepend(textLayer);
|
|
288
|
+
pageElement.prepend(textLayer);
|
|
290
289
|
}).catch(error);
|
|
291
290
|
});
|
|
292
291
|
page.getAnnotations({ intent: 'display' }).then((annotations) => {
|
|
@@ -5,6 +5,7 @@ exports.PdfViewer = void 0;
|
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
6
|
const main_1 = require("../common/main");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
|
+
const csp_styles_1 = require("../common/csp-styles");
|
|
8
9
|
const search_1 = require("../search");
|
|
9
10
|
const scroller_1 = require("../scroller");
|
|
10
11
|
const kendo_file_saver_1 = require("@progress/kendo-file-saver");
|
|
@@ -356,6 +357,7 @@ class PdfViewer extends main_1.Component {
|
|
|
356
357
|
}
|
|
357
358
|
this._resetPinchState();
|
|
358
359
|
};
|
|
360
|
+
(0, csp_styles_1.installStyleInterceptor)(element);
|
|
359
361
|
this.extendOptions(options);
|
|
360
362
|
this.throwIfInvalidOptions();
|
|
361
363
|
this.wrapper = this.element;
|
|
@@ -380,6 +382,8 @@ class PdfViewer extends main_1.Component {
|
|
|
380
382
|
this.destroySearchService();
|
|
381
383
|
this.destroyAnnotationEditorUIManager();
|
|
382
384
|
this.destroyDocumentScroller();
|
|
385
|
+
(0, csp_styles_1.pruneRules)();
|
|
386
|
+
(0, csp_styles_1.uninstallStyleInterceptor)(this.element);
|
|
383
387
|
}
|
|
384
388
|
throwIfInvalidOptions() {
|
|
385
389
|
if (this.options.minZoom > this.options.maxZoom) {
|
|
@@ -908,12 +912,16 @@ class PdfViewer extends main_1.Component {
|
|
|
908
912
|
canvas.height = adjustedHeight;
|
|
909
913
|
canvas.width = adjustedWidth;
|
|
910
914
|
if (printUnits) {
|
|
911
|
-
canvas
|
|
912
|
-
|
|
915
|
+
(0, csp_styles_1.applyStyles)(canvas, {
|
|
916
|
+
width: `${viewport.width / scaleNum}px`,
|
|
917
|
+
height: `${viewport.height / scaleNum}px`
|
|
918
|
+
});
|
|
913
919
|
}
|
|
914
920
|
else {
|
|
915
|
-
canvas
|
|
916
|
-
|
|
921
|
+
(0, csp_styles_1.applyStyles)(canvas, {
|
|
922
|
+
width: '100%',
|
|
923
|
+
height: '100%'
|
|
924
|
+
});
|
|
917
925
|
}
|
|
918
926
|
const canvasContext = canvas.getContext("2d");
|
|
919
927
|
if (printUnits) {
|
|
@@ -964,6 +972,7 @@ class PdfViewer extends main_1.Component {
|
|
|
964
972
|
this.destroySearchService();
|
|
965
973
|
this.destroyAnnotationEditorUIManager();
|
|
966
974
|
this.clearDocumentState();
|
|
975
|
+
(0, csp_styles_1.pruneRules)();
|
|
967
976
|
}
|
|
968
977
|
clearDocumentState() {
|
|
969
978
|
var _a;
|
|
@@ -1460,10 +1469,12 @@ class PdfViewer extends main_1.Component {
|
|
|
1460
1469
|
this.documentScroller.disablePanEventsTracking();
|
|
1461
1470
|
}
|
|
1462
1471
|
setScaleFactor(scaleFactor) {
|
|
1463
|
-
this.element
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1472
|
+
(0, csp_styles_1.setCustomProperties)(this.element, {
|
|
1473
|
+
'--scale-factor': String(scaleFactor * pdf_mjs_1.PixelsPerInch.PDF_TO_CSS_UNITS),
|
|
1474
|
+
'--total-scale-factor': String(scaleFactor * pdf_mjs_1.PixelsPerInch.PDF_TO_CSS_UNITS),
|
|
1475
|
+
'--scale-round-x': '1px',
|
|
1476
|
+
'--scale-round-y': '1px'
|
|
1477
|
+
}, this.element.getAttribute(csp_styles_1.STYLE_ATTR) || undefined);
|
|
1467
1478
|
}
|
|
1468
1479
|
_isTrackpadPinch(e) {
|
|
1469
1480
|
// Trackpad pinch gestures generate wheel events with synthetic ctrlKey=true.
|
package/package.json
CHANGED