PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.49
E2Pdf – Export Pdf Tool for WordPress v1.32.49
1.32.49 1.32.48 1.32.43 1.32.40 1.32.34 1.32.32 1.32.31 1.32.26 1.32.22 1.32.23 1.32.18 1.32.17 1.32.15 trunk 1.00.00 1.00.13 1.01.01 1.02.02 1.03.07 1.04.07 1.05.03 1.06.02 1.07.11 1.08.00 1.08.06 All 72 releases
e2pdf / assets / pdf.js / web / debugger.js

debugger.js in E2Pdf – Export Pdf Tool for WordPress 1.32.49, at assets/pdf.js/web/debugger.js

628 lines 17.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Copyright 2012 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 const { OPS } = globalThis.pdfjsLib || (await import("pdfjs-lib"));
17
18 const opMap = Object.create(null);
19 for (const key in OPS) {
20 opMap[OPS[key]] = key;
21 }
22
23 const FontInspector = (function FontInspectorClosure() {
24 let fonts;
25 let active = false;
26 const fontAttribute = "data-font-name";
27 function removeSelection() {
28 const divs = document.querySelectorAll(`span[${fontAttribute}]`);
29 for (const div of divs) {
30 div.className = "";
31 }
32 }
33 function resetSelection() {
34 const divs = document.querySelectorAll(`span[${fontAttribute}]`);
35 for (const div of divs) {
36 div.className = "debuggerHideText";
37 }
38 }
39 function selectFont(fontName, show) {
40 const divs = document.querySelectorAll(
41 `span[${fontAttribute}=${fontName}]`
42 );
43 for (const div of divs) {
44 div.className = show ? "debuggerShowText" : "debuggerHideText";
45 }
46 }
47 function textLayerClick(e) {
48 if (
49 !e.target.dataset.fontName ||
50 e.target.tagName.toUpperCase() !== "SPAN"
51 ) {
52 return;
53 }
54 const fontName = e.target.dataset.fontName;
55 const selects = document.getElementsByTagName("input");
56 for (const select of selects) {
57 if (select.dataset.fontName !== fontName) {
58 continue;
59 }
60 select.checked = !select.checked;
61 selectFont(fontName, select.checked);
62 select.scrollIntoView();
63 }
64 }
65 return {
66 // Properties/functions needed by PDFBug.
67 id: "FontInspector",
68 name: "Font Inspector",
69 panel: null,
70 manager: null,
71 init() {
72 const panel = this.panel;
73 const tmp = document.createElement("button");
74 tmp.addEventListener("click", resetSelection);
75 tmp.textContent = "Refresh";
76 panel.append(tmp);
77
78 fonts = document.createElement("div");
79 panel.append(fonts);
80 },
81 cleanup() {
82 fonts.textContent = "";
83 },
84 enabled: false,
85 get active() {
86 return active;
87 },
88 set active(value) {
89 active = value;
90 if (active) {
91 document.body.addEventListener("click", textLayerClick, true);
92 resetSelection();
93 } else {
94 document.body.removeEventListener("click", textLayerClick, true);
95 removeSelection();
96 }
97 },
98 // FontInspector specific functions.
99 fontAdded(fontObj, url) {
100 function properties(obj, list) {
101 const moreInfo = document.createElement("table");
102 for (const entry of list) {
103 const tr = document.createElement("tr");
104 const td1 = document.createElement("td");
105 td1.textContent = entry;
106 tr.append(td1);
107 const td2 = document.createElement("td");
108 td2.textContent = obj[entry].toString();
109 tr.append(td2);
110 moreInfo.append(tr);
111 }
112 return moreInfo;
113 }
114
115 const moreInfo = fontObj.css
116 ? properties(fontObj, ["baseFontName"])
117 : properties(fontObj, ["name", "type"]);
118
119 const fontName = fontObj.loadedName;
120 const font = document.createElement("div");
121 const name = document.createElement("span");
122 name.textContent = fontName;
123 let download;
124 if (!fontObj.css) {
125 download = document.createElement("a");
126 if (url) {
127 url = /url\(['"]?([^)"']+)/.exec(url);
128 download.href = url[1];
129 } else if (fontObj.data) {
130 download.href = URL.createObjectURL(
131 new Blob([fontObj.data], { type: fontObj.mimetype })
132 );
133 }
134 download.textContent = "Download";
135 }
136
137 const logIt = document.createElement("a");
138 logIt.href = "";
139 logIt.textContent = "Log";
140 logIt.addEventListener("click", function (event) {
141 event.preventDefault();
142 console.log(fontObj);
143 });
144 const select = document.createElement("input");
145 select.setAttribute("type", "checkbox");
146 select.dataset.fontName = fontName;
147 select.addEventListener("click", function () {
148 selectFont(fontName, select.checked);
149 });
150 if (download) {
151 font.append(select, name, " ", download, " ", logIt, moreInfo);
152 } else {
153 font.append(select, name, " ", logIt, moreInfo);
154 }
155 fonts.append(font);
156 // Somewhat of a hack, should probably add a hook for when the text layer
157 // is done rendering.
158 setTimeout(() => {
159 if (this.active) {
160 resetSelection();
161 }
162 }, 2000);
163 },
164 };
165 })();
166
167 // Manages all the page steppers.
168 const StepperManager = (function StepperManagerClosure() {
169 let steppers = [];
170 let stepperDiv = null;
171 let stepperControls = null;
172 let stepperChooser = null;
173 let breakPoints = Object.create(null);
174 return {
175 // Properties/functions needed by PDFBug.
176 id: "Stepper",
177 name: "Stepper",
178 panel: null,
179 manager: null,
180 init() {
181 const self = this;
182 stepperControls = document.createElement("div");
183 stepperChooser = document.createElement("select");
184 stepperChooser.addEventListener("change", function (event) {
185 self.selectStepper(this.value);
186 });
187 stepperControls.append(stepperChooser);
188 stepperDiv = document.createElement("div");
189 this.panel.append(stepperControls, stepperDiv);
190 if (sessionStorage.getItem("pdfjsBreakPoints")) {
191 breakPoints = JSON.parse(sessionStorage.getItem("pdfjsBreakPoints"));
192 }
193 },
194 cleanup() {
195 stepperChooser.textContent = "";
196 stepperDiv.textContent = "";
197 steppers = [];
198 },
199 enabled: false,
200 active: false,
201 // Stepper specific functions.
202 create(pageIndex) {
203 const debug = document.createElement("div");
204 debug.id = "stepper" + pageIndex;
205 debug.hidden = true;
206 debug.className = "stepper";
207 stepperDiv.append(debug);
208 const b = document.createElement("option");
209 b.textContent = "Page " + (pageIndex + 1);
210 b.value = pageIndex;
211 stepperChooser.append(b);
212 const initBreakPoints = breakPoints[pageIndex] || [];
213 const stepper = new Stepper(debug, pageIndex, initBreakPoints);
214 steppers.push(stepper);
215 if (steppers.length === 1) {
216 this.selectStepper(pageIndex, false);
217 }
218 return stepper;
219 },
220 selectStepper(pageIndex, selectPanel) {
221 pageIndex |= 0;
222 if (selectPanel) {
223 this.manager.selectPanel(this);
224 }
225 for (const stepper of steppers) {
226 stepper.panel.hidden = stepper.pageIndex !== pageIndex;
227 }
228 for (const option of stepperChooser.options) {
229 option.selected = (option.value | 0) === pageIndex;
230 }
231 },
232 saveBreakPoints(pageIndex, bps) {
233 breakPoints[pageIndex] = bps;
234 sessionStorage.setItem("pdfjsBreakPoints", JSON.stringify(breakPoints));
235 },
236 };
237 })();
238
239 // The stepper for each page's operatorList.
240 class Stepper {
241 // Shorter way to create element and optionally set textContent.
242 #c(tag, textContent) {
243 const d = document.createElement(tag);
244 if (textContent) {
245 d.textContent = textContent;
246 }
247 return d;
248 }
249
250 #simplifyArgs(args) {
251 if (typeof args === "string") {
252 const MAX_STRING_LENGTH = 75;
253 return args.length <= MAX_STRING_LENGTH
254 ? args
255 : args.substring(0, MAX_STRING_LENGTH) + "...";
256 }
257 if (typeof args !== "object" || args === null) {
258 return args;
259 }
260 if ("length" in args) {
261 // array
262 const MAX_ITEMS = 10,
263 simpleArgs = [];
264 let i, ii;
265 for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
266 simpleArgs.push(this.#simplifyArgs(args[i]));
267 }
268 if (i < args.length) {
269 simpleArgs.push("...");
270 }
271 return simpleArgs;
272 }
273 const simpleObj = {};
274 for (const key in args) {
275 simpleObj[key] = this.#simplifyArgs(args[key]);
276 }
277 return simpleObj;
278 }
279
280 constructor(panel, pageIndex, initialBreakPoints) {
281 this.panel = panel;
282 this.breakPoint = 0;
283 this.nextBreakPoint = null;
284 this.pageIndex = pageIndex;
285 this.breakPoints = initialBreakPoints;
286 this.currentIdx = -1;
287 this.operatorListIdx = 0;
288 this.indentLevel = 0;
289 }
290
291 init(operatorList) {
292 const panel = this.panel;
293 const content = this.#c("div", "c=continue, s=step");
294 const table = this.#c("table");
295 content.append(table);
296 table.cellSpacing = 0;
297 const headerRow = this.#c("tr");
298 table.append(headerRow);
299 headerRow.append(
300 this.#c("th", "Break"),
301 this.#c("th", "Idx"),
302 this.#c("th", "fn"),
303 this.#c("th", "args")
304 );
305 panel.append(content);
306 this.table = table;
307 this.updateOperatorList(operatorList);
308 }
309
310 updateOperatorList(operatorList) {
311 const self = this;
312
313 function cboxOnClick() {
314 const x = +this.dataset.idx;
315 if (this.checked) {
316 self.breakPoints.push(x);
317 } else {
318 self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
319 }
320 StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
321 }
322
323 const MAX_OPERATORS_COUNT = 15000;
324 if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
325 return;
326 }
327
328 const chunk = document.createDocumentFragment();
329 const operatorsToDisplay = Math.min(
330 MAX_OPERATORS_COUNT,
331 operatorList.fnArray.length
332 );
333 for (let i = this.operatorListIdx; i < operatorsToDisplay; i++) {
334 const line = this.#c("tr");
335 line.className = "line";
336 line.dataset.idx = i;
337 chunk.append(line);
338 const checked = this.breakPoints.includes(i);
339 const args = operatorList.argsArray[i] || [];
340
341 const breakCell = this.#c("td");
342 const cbox = this.#c("input");
343 cbox.type = "checkbox";
344 cbox.className = "points";
345 cbox.checked = checked;
346 cbox.dataset.idx = i;
347 cbox.onclick = cboxOnClick;
348
349 breakCell.append(cbox);
350 line.append(breakCell, this.#c("td", i.toString()));
351 const fn = opMap[operatorList.fnArray[i]];
352 let decArgs = args;
353 if (fn === "showText") {
354 const glyphs = args[0];
355 const charCodeRow = this.#c("tr");
356 const fontCharRow = this.#c("tr");
357 const unicodeRow = this.#c("tr");
358 for (const glyph of glyphs) {
359 if (typeof glyph === "object" && glyph !== null) {
360 charCodeRow.append(this.#c("td", glyph.originalCharCode));
361 fontCharRow.append(this.#c("td", glyph.fontChar));
362 unicodeRow.append(this.#c("td", glyph.unicode));
363 } else {
364 // null or number
365 const advanceEl = this.#c("td", glyph);
366 advanceEl.classList.add("advance");
367 charCodeRow.append(advanceEl);
368 fontCharRow.append(this.#c("td"));
369 unicodeRow.append(this.#c("td"));
370 }
371 }
372 decArgs = this.#c("td");
373 const table = this.#c("table");
374 table.classList.add("showText");
375 decArgs.append(table);
376 table.append(charCodeRow, fontCharRow, unicodeRow);
377 } else if (fn === "constructPath") {
378 const [op, [path], minMax] = args;
379 decArgs = this.#c("td");
380 decArgs.append(JSON.stringify(this.#simplifyArgs(path)));
381 decArgs.append(this.#c("br"));
382 decArgs.append(`minMax: ${JSON.stringify(this.#simplifyArgs(minMax))}`);
383 decArgs.append(this.#c("br"));
384 decArgs.append(`${opMap[op]}`);
385 } else if (fn === "restore" && this.indentLevel > 0) {
386 this.indentLevel--;
387 }
388 line.append(this.#c("td", " ".repeat(this.indentLevel * 2) + fn));
389 if (fn === "save") {
390 this.indentLevel++;
391 }
392
393 if (decArgs instanceof HTMLElement) {
394 line.append(decArgs);
395 } else {
396 line.append(this.#c("td", JSON.stringify(this.#simplifyArgs(decArgs))));
397 }
398 }
399 if (operatorsToDisplay < operatorList.fnArray.length) {
400 const lastCell = this.#c("td", "...");
401 lastCell.colspan = 4;
402 chunk.append(lastCell);
403 }
404 this.operatorListIdx = operatorList.fnArray.length;
405 this.table.append(chunk);
406 }
407
408 getNextBreakPoint() {
409 this.breakPoints.sort((a, b) => a - b);
410 for (const breakPoint of this.breakPoints) {
411 if (breakPoint > this.currentIdx) {
412 return breakPoint;
413 }
414 }
415 return null;
416 }
417
418 breakIt(idx, callback) {
419 StepperManager.selectStepper(this.pageIndex, true);
420 this.currentIdx = idx;
421
422 const listener = evt => {
423 switch (evt.keyCode) {
424 case 83: // step
425 document.removeEventListener("keydown", listener);
426 this.nextBreakPoint = this.currentIdx + 1;
427 this.goTo(-1);
428 callback();
429 break;
430 case 67: // continue
431 document.removeEventListener("keydown", listener);
432 this.nextBreakPoint = this.getNextBreakPoint();
433 this.goTo(-1);
434 callback();
435 break;
436 }
437 };
438 document.addEventListener("keydown", listener);
439 this.goTo(idx);
440 }
441
442 goTo(idx) {
443 const allRows = this.panel.getElementsByClassName("line");
444 for (const row of allRows) {
445 if ((row.dataset.idx | 0) === idx) {
446 row.style.backgroundColor = "rgb(251,250,207)";
447 row.scrollIntoView();
448 } else {
449 row.style.backgroundColor = null;
450 }
451 }
452 }
453 }
454
455 const Stats = (function Stats() {
456 let stats = [];
457 function clear(node) {
458 node.textContent = ""; // Remove any `node` contents from the DOM.
459 }
460 function getStatIndex(pageNumber) {
461 for (const [i, stat] of stats.entries()) {
462 if (stat.pageNumber === pageNumber) {
463 return i;
464 }
465 }
466 return false;
467 }
468 return {
469 // Properties/functions needed by PDFBug.
470 id: "Stats",
471 name: "Stats",
472 panel: null,
473 manager: null,
474 init() {},
475 enabled: false,
476 active: false,
477 // Stats specific functions.
478 add(pageNumber, stat) {
479 if (!stat) {
480 return;
481 }
482 const statsIndex = getStatIndex(pageNumber);
483 if (statsIndex !== false) {
484 stats[statsIndex].div.remove();
485 stats.splice(statsIndex, 1);
486 }
487 const wrapper = document.createElement("div");
488 wrapper.className = "stats";
489 const title = document.createElement("div");
490 title.className = "title";
491 title.textContent = "Page: " + pageNumber;
492 const statsDiv = document.createElement("div");
493 statsDiv.textContent = stat.toString();
494 wrapper.append(title, statsDiv);
495 stats.push({ pageNumber, div: wrapper });
496 stats.sort((a, b) => a.pageNumber - b.pageNumber);
497 clear(this.panel);
498 for (const entry of stats) {
499 this.panel.append(entry.div);
500 }
501 },
502 cleanup() {
503 stats = [];
504 clear(this.panel);
505 },
506 };
507 })();
508
509 // Manages all the debugging tools.
510 class PDFBug {
511 static #buttons = [];
512
513 static #activePanel = null;
514
515 static tools = [FontInspector, StepperManager, Stats];
516
517 static enable(ids) {
518 const all = ids.length === 1 && ids[0] === "all";
519 const tools = this.tools;
520 for (const tool of tools) {
521 if (all || ids.includes(tool.id)) {
522 tool.enabled = true;
523 }
524 }
525 if (!all) {
526 // Sort the tools by the order they are enabled.
527 tools.sort(function (a, b) {
528 let indexA = ids.indexOf(a.id);
529 indexA = indexA < 0 ? tools.length : indexA;
530 let indexB = ids.indexOf(b.id);
531 indexB = indexB < 0 ? tools.length : indexB;
532 return indexA - indexB;
533 });
534 }
535 }
536
537 static init(container, ids) {
538 this.loadCSS();
539 this.enable(ids);
540 /*
541 * Basic Layout:
542 * PDFBug
543 * Controls
544 * Panels
545 * Panel
546 * Panel
547 * ...
548 */
549 const ui = document.createElement("div");
550 ui.id = "PDFBug";
551
552 const controls = document.createElement("div");
553 controls.setAttribute("class", "controls");
554 ui.append(controls);
555
556 const panels = document.createElement("div");
557 panels.setAttribute("class", "panels");
558 ui.append(panels);
559
560 container.append(ui);
561 container.style.right = "var(--panel-width)";
562
563 // Initialize all the debugging tools.
564 for (const tool of this.tools) {
565 const panel = document.createElement("div");
566 const panelButton = document.createElement("button");
567 panelButton.textContent = tool.name;
568 panelButton.addEventListener("click", event => {
569 event.preventDefault();
570 this.selectPanel(tool);
571 });
572 controls.append(panelButton);
573 panels.append(panel);
574 tool.panel = panel;
575 tool.manager = this;
576 if (tool.enabled) {
577 tool.init();
578 } else {
579 panel.textContent =
580 `${tool.name} is disabled. To enable add "${tool.id}" to ` +
581 "the pdfBug parameter and refresh (separate multiple by commas).";
582 }
583 this.#buttons.push(panelButton);
584 }
585 this.selectPanel(0);
586 }
587
588 static loadCSS() {
589 const { url } = import.meta;
590
591 const link = document.createElement("link");
592 link.rel = "stylesheet";
593 link.href = url.replace(/\.js$/, ".css");
594
595 document.head.append(link);
596 }
597
598 static cleanup() {
599 for (const tool of this.tools) {
600 if (tool.enabled) {
601 tool.cleanup();
602 }
603 }
604 }
605
606 static selectPanel(index) {
607 if (typeof index !== "number") {
608 index = this.tools.indexOf(index);
609 }
610 if (index === this.#activePanel) {
611 return;
612 }
613 this.#activePanel = index;
614 for (const [j, tool] of this.tools.entries()) {
615 const isActive = j === index;
616 this.#buttons[j].classList.toggle("active", isActive);
617 tool.active = isActive;
618 tool.panel.hidden = !isActive;
619 }
620 }
621 }
622
623 globalThis.FontInspector = FontInspector;
624 globalThis.StepperManager = StepperManager;
625 globalThis.Stats = Stats;
626
627 export { PDFBug };
628