; (function (g, fn) { var version = "3.0.0", pdfjsVersion = "5.4.296"; console.info("pdfh5.js v" + version + " && pdf.js v" + pdfjsVersion + " https://pdfh5.gjtool.cn"); if (!g.document) { throw new Error("pdfh5 requires a window with a document"); } async function initPdfJs() { if (g.pdfjsLib) { return g.pdfjsLib; } try { let pdfjsLib = await import('./pdf.min.js'); let sandboxModule = await import('./pdf.sandbox.min.js'); let workerSrc = './js/pdf.worker.min.js'; let cMapUrl = '../cmaps/'; let standardFontDataUrl = '../standard_fonts/'; let iccUrl = '../iccs/'; let wasmUrl = '../wasm/'; // 集成沙箱功能 if (sandboxModule && sandboxModule.default) { pdfjsLib.Sandbox = sandboxModule.default; pdfjsLib.SandboxManager = sandboxModule.SandboxManager; } pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc; var resourcePaths = { workerSrc: workerSrc, cMapUrl: cMapUrl, standardFontDataUrl: standardFontDataUrl, iccUrl: iccUrl, wasmUrl: wasmUrl }; window._pdfh5ResourcePaths = resourcePaths; return pdfjsLib; } catch (error) { console.error('Failed to load PDF.js:', error); throw error; } } // 创建Pdfh5构造函数 var Pdfh5Constructor = fn(version, initPdfJs); if (typeof define === 'function' && define.amd) { define(function () { return Pdfh5Constructor; }); } else if (typeof module !== 'undefined' && module.exports) { module.exports = Pdfh5Constructor; } else { g.Pdfh5 = Pdfh5Constructor; } })(typeof window !== 'undefined' ? window : this, function (version, initPdfJs) { 'use strict'; var css = '.pdfjs {width: 100%;height: 100%;overflow: hidden;background: #fff;position: relative;}.pdfjs .viewerContainer {position: relative;width: 100%;height: 100%;overflow-y: auto;overflow-x: hidden;-webkit-overflow-scrolling: touch;transition: all .3s;}.pdfjs .pdfViewer {position: relative;top: 0;left: 0;padding: 10px 8px;}.pdfjs .pdfViewer .pageContainer {margin: 0px auto 8px auto;position: relative;overflow: visible;-webkit-box-shadow: darkgrey 0px 1px 3px 0px;-moz-box-shadow: darkgrey 0px 1px 3px 0px;box-shadow: darkgrey 0px 1px 3px 0px;background-color: white;box-sizing: border-box;}.pdfjs .pdfViewer .pageContainer img {width: 100%;height: 100%;position: relative;z-index: 100;user-select: none;pointer-events: none;}.pdfjs .pdfViewer .pageContainer canvas {width: 100%;height: 100%;position: relative;z-index: 100;user-select: none;pointer-events: none;}.pdfjs .pageNum {padding: 0px 7px;height: 26px;position: absolute;top: 20px;left: 15px;z-index: 997;border-radius: 8px;transition: all .3s;display: none;}.pdfjs .pageNum-bg, .pdfjs .pageNum-num {width: 100%;height: 100%;line-height: 26px;text-align: center;position: absolute;top: 0px;left: 0px;color: #fff;border-radius: 8px;font-size: 16px;}.pdfjs .pageNum-bg {background: rgba(0, 0, 0, 0.5);}.pdfjs .pageNum-num {position: relative;}.pdfjs .pageNum span {color: #fff;font-size: 16px;}.pdfjs .loadingBar {position: absolute;width: 100%;z-index: 1000;background: #fff !important;height: 4px;top: 0px;left: 0px;transition: all .3s;}.pdfjs .loadingBar .progress {background: #fff !important;position: absolute;top: 0;left: 0;width: 0%;height: 100%;overflow: hidden;transition: width 200ms;}.pdfjs .loadingBar .progress .glimmer {position: absolute;top: 0;left: 0;height: 100%;width: calc(100% + 150px);background: #7bcf34;}.pdfjs .backTop {width: 50px;height: 50px;line-height: 50px;text-align: center;position: absolute;bottom: 90px;right: 15px;font-size: 18px;z-index: 999;border-radius: 50%;background: rgba(0, 0, 0, 0.4) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAA+klEQVRYw+2WUQ2DMBCG2TIBSJiESkACEpCAg83BcLBJmIQ5gClgDpiDby9tciGkoaUtZOESXuhdv7+X/pdm2dYC6IgX7Zh3THy+w9oN/rMASqBcE26iSA1XwCAEDIBKBc8F/KE/gB7IU8BbDXyJf2Z2tFFFAE8N6iRIi/jotXssuGn1FzhPrCu9BtCEhlcCrix5hbiYVSh46bKpELvcniO71Q51zWJ7ju3mUe9vzym7eR7Az57CbohTXBzAt9GknG9PoLY8KK4z6htLfeXTTXMZAfoZuWYWKC+YZWMAQuWZSP0k2wXsAnYB2xNwci1wGTKhO/COlLtu/ABVfTFsxwwYRgAAAABJRU5ErkJggg==) no-repeat center;background-size: 50% 50%;transition: all .3s;display: none;}.pdfjs .loadEffect {width: 100px;height: 100px;position: absolute;top: 50%;left: 50%;margin-top: -50px;margin-left: -50px;z-index: 103;background: url(data:image/gif;base64,R0lGODlhgACAAKIAAP///93d3bu7u5mZmQAA/wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAEACwCAAIAfAB8AAAD/0i63P4wygYqmDjrzbtflvWNZGliYXiubKuloivPLlzReD7al+7/Eh5wSFQIi8hHYBkwHUmD6CD5YTJLz49USuVYraRsZ7vtar7XnQ1Kjpoz6LRHvGlz35O4nEPP2O94EnpNc2sef1OBGIOFMId/inB6jSmPdpGScR19EoiYmZobnBCIiZ95k6KGGp6ni4wvqxilrqBfqo6skLW2YBmjDa28r6Eosp27w8Rov8ekycqoqUHODrTRvXsQwArC2NLF29UM19/LtxO5yJd4Au4CK7DUNxPebG4e7+8n8iv2WmQ66BtoYpo/dvfacBjIkITBE9DGlMvAsOIIZjIUAixliv9ixYZVtLUos5GjwI8gzc3iCGghypQqrbFsme8lwZgLZtIcYfNmTJ34WPTUZw5oRxdD9w0z6iOpO15MgTh1BTTJUKos39jE+o/KS64IFVmsFfYT0aU7capdy7at27dw48qdS7eu3bt480I02vUbX2F/JxYNDImw4GiGE/P9qbhxVpWOI/eFKtlNZbWXuzlmG1mv58+gQ4seTbq06dOoU6vGQZJy0FNlMcV+czhQ7SQmYd8eMhPs5BxVdfcGEtV3buDBXQ+fURxx8oM6MT9P+Fh6dOrH2zavc13u9JXVJb520Vp8dvC76wXMuN5Sepm/1WtkEZHDefnzR9Qvsd9+/wi8+en3X0ntYVcSdAE+UN4zs7ln24CaLagghIxBaGF8kFGoIYV+Ybghh841GIyI5ICIFoklJsigihmimJOLEbLYIYwxSgigiZ+8l2KB+Ml4oo/w8dijjcrouCORKwIpnJIjMnkkksalNeR4fuBIm5UEYImhIlsGCeWNNJphpJdSTlkml1jWeOY6TnaRpppUctcmFW9mGSaZceYopH9zkjnjUe59iR5pdapWaGqHopboaYualqije67GJ6CuJAAAIfkEBQUABAAsCgACAFcAMAAAA/9Iutz+ML5Ag7w46z0r5WAoSp43nihXVmnrdusrv+s332dt4Tyo9yOBUJD6oQBIQGs4RBlHySSKyczVTtHoidocPUNZaZAr9F5FYbGI3PWdQWn1mi36buLKFJvojsHjLnshdhl4L4IqbxqGh4gahBJ4eY1kiX6LgDN7fBmQEJI4jhieD4yhdJ2KkZk8oiSqEaatqBekDLKztBG2CqBACq4wJRi4PZu1sA2+v8C6EJexrBAD1AOBzsLE0g/V1UvYR9sN3eR6lTLi4+TlY1wz6Qzr8u1t6FkY8vNzZTxaGfn6mAkEGFDgL4LrDDJDyE4hEIbdHB6ESE1iD4oVLfLAqPETIsOODwmCDJlv5MSGJklaS6khAQAh+QQFBQAEACwfAAIAVwAwAAAD/0i63A4QuEmrvTi3yLX/4MeNUmieITmibEuppCu3sDrfYG3jPKbHveDktxIaF8TOcZmMLI9NyBPanFKJp4A2IBx4B5lkdqvtfb8+HYpMxp3Pl1qLvXW/vWkli16/3dFxTi58ZRcChwIYf3hWBIRchoiHiotWj5AVkpIXi4xLjxiaiJR/T5ehoomcnZ+EGamqq6VGoK+pGqxCtaiiuJVBu7yaHrk4pxqwxMUzwcKbyrPMzZG90NGDrh/JH8t72dq3IN1jfCHb3L/e5ebh4ukmxyDn6O8g08jt7tf26ybz+m/W9GNXzUQ9fm1Q/APoSWAhhfkMAmpEbRhFKwsvCsmosRIHx444PoKcIXKkjIImjTzjkQAAIfkEBQUABAAsPAA8AEIAQgAAA/VIBNz+8KlJq72Yxs1d/uDVjVxogmQqnaylvkArT7A63/V47/m2/8CgcEgsGo/IpHLJbDqf0Kh0Sj0FroGqDMvVmrjgrDcTBo8v5fCZki6vCW33Oq4+0832O/at3+f7fICBdzsChgJGeoWHhkV0P4yMRG1BkYeOeECWl5hXQ5uNIAOjA1KgiKKko1CnqBmqqk+nIbCkTq20taVNs7m1vKAnurtLvb6wTMbHsUq4wrrFwSzDzcrLtknW16tI2tvERt6pv0fi48jh5h/U6Zs77EXSN/BE8jP09ZFA+PmhP/xvJgAMSGBgQINvEK5ReIZhQ3QEMTBLAAAh+QQFBQAEACwCAB8AMABXAAAD50i6DA4syklre87qTbHn4OaNYSmNqKmiqVqyrcvBsazRpH3jmC7yD98OCBF2iEXjBKmsAJsWHDQKmw571l8my+16v+CweEwum8+hgHrNbrvbtrd8znbR73MVfg838f8BeoB7doN0cYZvaIuMjY6PkJGSk2gClgJml5pjmp2YYJ6dX6GeXaShWaeoVqqlU62ir7CXqbOWrLafsrNctjIDwAMWvC7BwRWtNsbGFKc+y8fNsTrQ0dK3QtXAYtrCYd3eYN3c49/a5NVj5eLn5u3s6e7x8NDo9fbL+Mzy9/T5+tvUzdN3Zp+GBAAh+QQJBQAEACwCAAIAfAB8AAAD/0i63P4wykmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdArcQK2TOL7/nl4PSMwIfcUk5YhUOh3M5nNKiOaoWCuWqt1Ou16l9RpOgsvEMdocXbOZ7nQ7DjzTaeq7zq6P5fszfIASAYUBIYKDDoaGIImKC4ySH3OQEJKYHZWWi5iZG0ecEZ6eHEOio6SfqCaqpaytrpOwJLKztCO2jLi1uoW8Ir6/wCHCxMG2x7muysukzb230M6H09bX2Nna29zd3t/g4cAC5OXm5+jn3Ons7eba7vHt2fL16tj2+QL0+vXw/e7WAUwnrqDBgwgTKlzIsKHDh2gGSBwAccHEixAvaqTYcFCjRoYeNyoM6REhyZIHT4o0qPIjy5YTTcKUmHImx5cwE85cmJPnSYckK66sSAAj0aNIkypdyrSp06dQo0qdSrWq1atYs2rdyrWr169gwxZJAAA7) no-repeat center;background-size: 30% 30%;transition: all .3s;display:block;}.pdfjs .pdfViewer .pageContainer img.pdfLogo {position: absolute;z-index: 101;}.pdfjs .pdfViewer .pageContainer canvas.pdfLogo {position: absolute;z-index: 101;}.pdfjs .textLayer {position: absolute;left: 0;top: 0;right: 0;bottom: 0;overflow: hidden;opacity: 0.2;line-height: 1.0;z-index: 101;user-select: text;}.pdfjs .textLayer>span {color: transparent;position: absolute;white-space: pre;cursor: text;transform-origin: 0% 0%;}.pdfjs .textLayer .highlight {margin: -1px;padding: 1px;background-color: rgba(180, 0, 170, 1);border-radius: 4px;}.pdfjs .textLayer .highlight.begin {border-radius: 4px 0px 0px 4px;}.pdfjs .textLayer .highlight.end {border-radius: 0px 4px 4px 0px;}.pdfjs .textLayer .highlight.middle {border-radius: 0px;}.pdfjs .textLayer .highlight.selected {background-color: rgba(0, 100, 0, 1);}.pdfjs .textLayer ::selection {background: rgba(0, 0, 255, 0.3); }.pdfjs .textLayer .endOfContent {display: block;position: absolute;left: 0px;top: 100%;right: 0px;bottom: 0px;z-index: -1;cursor: default;}.pdfjs .textLayer .endOfContent.active {top: 0px;}'; var buildElement = function (html) { var div = document.createElement('div'); div.innerHTML = html; return div.firstChild; }; var triggerEvent = function (el, name) { var event = document.createEvent('HTMLEvents'); event.initEvent(name, true, false); el.dispatchEvent(event); }; var definePinchZoom = function () { var PinchZoom = function (el, options, pinchParentNode) { this.el = el; this.pinchParentNode = pinchParentNode; this.zoomFactor = 1; this.lastScale = 1; this.offset = { x: 0, y: 0 }; this.options = Object.assign({}, this.defaults, options); this.options.tapZoomFactor = isNaN(options.tapZoomFactor) ? 2 : options.tapZoomFactor; this.options.zoomOutFactor = isNaN(options.zoomOutFactor) ? 1.2 : options.zoomOutFactor; this.options.animationDuration = isNaN(options.animationDuration) ? 300 : options.animationDuration; this.options.maxZoom = isNaN(options.maxZoom) ? 4 : options.maxZoom; this.options.minZoom = isNaN(options.minZoom) ? 0.5 : options.minZoom; this.options.dampingFactor = isNaN(options.dampingFactor) ? 0.85 : options.dampingFactor; this.setupMarkup(); this.bindEvents(); this.update(); this.enable(); this.height = 0; this.load = false; this.direction = null; this.clientY = null; this.lastclientY = null; this.lastOffsets = []; for (var i = 0; i < 5; i++) { this.lastOffsets.push({ x: 0, y: 0, time: Date.now() }); } }, sum = function (a, b) { return a + b; }, isCloseTo = function (value, expected) { return value > expected - 0.01 && value < expected + 0.01; }; PinchZoom.prototype = { defaults: { tapZoomFactor: 2, zoomOutFactor: 1.2, animationDuration: 300, maxZoom: 4, minZoom: 0.5, draggableUnzoomed: true, lockDragAxis: false, use2d: true, zoomStartEventName: 'pz_zoomstart', zoomEndEventName: 'pz_zoomend', dragStartEventName: 'pz_dragstart', dragEndEventName: 'pz_dragend', doubleTapEventName: 'pz_doubletap' }, handleDragStart: function (event) { triggerEvent(this.el, this.options.dragStartEventName); this.stopAnimation(); this.lastDragPosition = false; this.hasInteraction = true; this.handleDrag(event); }, handleDrag: function (event) { if (this.zoomFactor > 1.0) { var touch = this.getTouches(event)[0]; this.drag(touch, this.lastDragPosition, event); this.offset = this.sanitizeOffset(this.offset); this.lastDragPosition = touch; } }, sanitizeOffset: function (offset) { var containerWidth = this.getContainerX(); var imageWidth = this.el.offsetWidth * this.zoomFactor; var maxX = Math.max(imageWidth - containerWidth, 0); var containerHeight = this.getContainerY(); var imageHeight = this.el.offsetHeight * this.zoomFactor; var maxY = Math.max(imageHeight - containerHeight, 0); var elasticFactor = 0.5; var newX = offset.x; var newY = offset.y; if (newX < 0) { newX = newX * elasticFactor; } else if (newX > maxX) { newX = maxX + (newX - maxX) * elasticFactor; } if (newY < 0) { newY = newY * elasticFactor; } else if (newY > maxY) { newY = maxY + (newY - maxY) * elasticFactor; } return { x: newX, y: newY }; }, handleDragEnd: function () { triggerEvent(this.el, this.options.dragEndEventName); this.end(); var now = Date.now(); var validRecords = this.lastOffsets.filter(r => now - r.time < 100); var velocityX = validRecords.length > 1 ? (validRecords[validRecords.length - 1].x - validRecords[0].x) / (validRecords[validRecords.length - 1].time - validRecords[0].time) : 0; this.startInertiaAnimation(velocityX); }, startInertiaAnimation: function (initialVelocity) { var minVelocity = 0.08; var decay = 0.92; var animateFrame = () => { if (Math.abs(initialVelocity) < minVelocity) return; this.addOffset({ x: initialVelocity * 16, y: 0 }); this.offset = this.sanitizeOffset(this.offset); this.update(); initialVelocity *= decay; requestAnimationFrame(animateFrame); }; requestAnimationFrame(animateFrame); }, handleZoomStart: function (event) { triggerEvent(this.el, this.options.zoomStartEventName); this.stopAnimation(); this.lastScale = 1; this.nthZoom = 0; this.lastZoomCenter = false; this.hasInteraction = true; }, handleZoom: function (event, newScale) { var touchCenter = this.getTouchCenter(this.getTouches(event)), scale = newScale / this.lastScale; this.lastScale = newScale; this.nthZoom += 1; if (this.nthZoom > 3) { this.scale(scale, touchCenter); this.drag(touchCenter, this.lastZoomCenter); } this.lastZoomCenter = touchCenter; }, handleZoomEnd: function () { triggerEvent(this.el, this.options.zoomEndEventName); this.end(); }, handleDoubleTap: function (event) { var center = this.getTouches(event)[0], zoomFactor = this.zoomFactor > 1 ? 1 : this.options.tapZoomFactor, startZoomFactor = this.zoomFactor, updateProgress = (function (progress) { this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center); }).bind(this); if (this.hasInteraction) { return; } if (startZoomFactor > zoomFactor) { center = this.getCurrentZoomCenter(); } this.animate(this.options.animationDuration, updateProgress, this.swing); triggerEvent(this.el, this.options.doubleTapEventName); }, scaleTo: function (zoomFactor, center) { this.scale(zoomFactor / this.zoomFactor, center); }, scale: function (scale, center) { scale = this.scaleZoomFactor(scale); this.addOffset({ x: (scale - 1) * (center.x + this.offset.x), y: (scale - 1) * (center.y + this.offset.y) }); this.done && this.done.call(this, this.getInitialZoomFactor() * this.zoomFactor); }, scaleZoomFactor: function (scale) { var originalZoomFactor = this.zoomFactor; this.zoomFactor *= scale; this.zoomFactor = Math.min(this.options.maxZoom, Math.max(this.zoomFactor, this.options.minZoom)); return this.zoomFactor / originalZoomFactor; }, canDrag: function () { return this.options.draggableUnzoomed || !isCloseTo(this.zoomFactor, 1); }, drag: function (center, lastCenter, event) { if (!lastCenter) return; var dx = -(center.x - lastCenter.x) * this.options.dampingFactor; var dy = -(center.y - lastCenter.y) * this.options.dampingFactor; this.addOffset({ x: dx, y: dy }); }, getTouchCenter: function (touches) { return this.getVectorAvg(touches); }, getVectorAvg: function (vectors) { return { x: vectors.map(function (v) { return v.x; }).reduce(sum) / vectors.length, y: vectors.map(function (v) { return v.y; }).reduce(sum) / vectors.length }; }, addOffset: function (offset) { this.offset = { x: this.offset.x + offset.x, y: this.offset.y + offset.y }; }, sanitize: function () { if (this.zoomFactor < this.options.zoomOutFactor) { this.zoomOutAnimation(); } else if (this.isInsaneOffset(this.offset)) { this.sanitizeOffsetAnimation(); } }, isInsaneOffset: function (offset) { var sanitizedOffset = this.sanitizeOffset(offset); return sanitizedOffset.x !== offset.x || sanitizedOffset.y !== offset.y; }, sanitizeOffsetAnimation: function () { var targetOffset = this.sanitizeOffset(this.offset), startOffset = { x: this.offset.x, y: this.offset.y }, updateProgress = (function (progress) { this.offset.x = startOffset.x + progress * (targetOffset.x - startOffset.x); this.offset.y = startOffset.y + progress * (targetOffset.y - startOffset.y); this.update(); }).bind(this); this.animate( this.options.animationDuration, updateProgress, this.swing ); }, zoomOutAnimation: function () { var startZoomFactor = this.zoomFactor, zoomFactor = 1, center = this.getCurrentZoomCenter(), updateProgress = (function (progress) { this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center); }).bind(this); this.animate( this.options.animationDuration, updateProgress, this.swing ); }, updateAspectRatio: function () { this.setContainerY(this.getContainerX() / this.getAspectRatio()); }, getInitialZoomFactor: function () { if (this.pinchContainer && this.el) { return this.pinchContainer.offsetWidth / this.el.offsetWidth; } else { return 0; } }, getAspectRatio: function () { if (this.el) { return this.pinchContainer.offsetWidth / this.el.offsetHeight; } else { return 0; } }, getCurrentZoomCenter: function () { var length = this.pinchContainer.offsetWidth * this.zoomFactor, offsetLeft = this.offset.x, offsetRight = length - offsetLeft - this.pinchContainer.offsetWidth, widthOffsetRatio = offsetLeft / offsetRight, centerX = widthOffsetRatio * this.pinchContainer.offsetWidth / (widthOffsetRatio + 1), height = this.pinchContainer.offsetHeight * this.zoomFactor, offsetTop = this.offset.y, offsetBottom = height - offsetTop - this.pinchContainer.offsetHeight, heightOffsetRatio = offsetTop / offsetBottom, centerY = heightOffsetRatio * this.pinchContainer.offsetHeight / (heightOffsetRatio + 1); if (offsetRight === 0) { centerX = this.pinchContainer.offsetWidth; } if (offsetBottom === 0) { centerY = this.pinchContainer.offsetHeight; } return { x: centerX, y: centerY }; }, getTouches: function (event) { var position = this.pinchContainer.getBoundingClientRect(); var posTop = position.top; var posLeft = position.left; return Array.prototype.slice.call(event.touches).map(function (touch) { return { x: touch.pageX - posLeft, y: touch.pageY - posTop, }; }); }, animate: function (duration, framefn, timefn, callback) { var startTime = new Date().getTime(), renderFrame = (function () { if (!this.inAnimation) { return; } var frameTime = new Date().getTime() - startTime, progress = frameTime / duration; if (frameTime >= duration) { framefn(1); if (callback) { callback(); } this.update(); this.stopAnimation(); } else { if (timefn) { progress = timefn(progress); } framefn(progress); this.update(); requestAnimationFrame(renderFrame); } }).bind(this); this.inAnimation = true; requestAnimationFrame(renderFrame); }, stopAnimation: function () { this.inAnimation = false; }, swing: function (p) { return -Math.cos(p * Math.PI) / 2 + 0.5; }, getContainerX: function () { if (this.el) { return this.el.offsetWidth; } else { return 0; } }, getContainerY: function () { return this.el.offsetHeight; }, setContainerY: function (y) { y = y.toFixed(2); return this.pinchContainer.style.height = y + 'px'; }, setupMarkup: function () { this.pinchContainer = buildElement('
'); this.el.parentNode.insertBefore(this.pinchContainer, this.el); this.pinchContainer.appendChild(this.el); this.pinchContainer.style.position = 'relative'; this.el.style.webkitTransformOrigin = '0% 0%'; this.el.style.mozTransformOrigin = '0% 0%'; this.el.style.msTransformOrigin = '0% 0%'; this.el.style.oTransformOrigin = '0% 0%'; this.el.style.transformOrigin = '0% 0%'; this.el.style.position = 'relative'; }, end: function () { this.hasInteraction = false; this.sanitize(); this.update(); }, bindEvents: function () { var self = this; detectGestures(this.pinchParentNode, this); this.resizeHandler = this.update.bind(this); window.addEventListener('resize', this.resizeHandler); Array.from(this.el.querySelectorAll('canvas')).forEach(function (imgEl) { self.update.bind(self); }); }, update: function () { if (this.updatePlaned) { return; } this.updatePlaned = true; setTimeout((function () { this.updatePlaned = false; this.updateAspectRatio(); var zoomFactor = this.getInitialZoomFactor() * this.zoomFactor, offsetX = (-this.offset.x / zoomFactor).toFixed(3), offsetY = (-this.offset.y / zoomFactor).toFixed(3); this.lastclientY = offsetY; var transform3d = 'scale3d(' + zoomFactor + ', ' + zoomFactor + ',1) ' + 'translate3d(' + offsetX + 'px,' + offsetY + 'px,0px)', transform2d = 'scale(' + zoomFactor + ', ' + zoomFactor + ') ' + 'translate(' + offsetX + 'px,' + offsetY + 'px)', removeClone = (function () { if (this.clone) { this.clone.remove(); delete this.clone; } }).bind(this); if (!this.options.use2d || this.hasInteraction || this.inAnimation) { this.is3d = true; this.el.style.webkitTransform = transform3d; this.el.style.mozTransform = transform2d; this.el.style.msTransform = transform2d; this.el.style.oTransform = transform2d; this.el.style.transform = transform3d; } else { this.el.style.webkitTransform = transform2d; this.el.style.mozTransform = transform2d; this.el.style.msTransform = transform2d; this.el.style.oTransform = transform2d; this.el.style.transform = transform2d; this.is3d = false; } this.lastOffsets.shift(); this.lastOffsets.push({ x: this.offset.x, y: this.offset.y, time: Date.now() }); }).bind(this), 0); }, enable: function () { this.enabled = true; }, disable: function () { this.enabled = false; }, destroy: function () { window.removeEventListener('resize', this.resizeHandler); if (this.pinchContainer) { var parentNode = this.pinchContainer.parentNode; var childNode = this.pinchContainer.firstChild; parentNode.appendChild(childNode); parentNode.removeChild(this.pinchContainer); } } }; var detectGestures = function (el, target) { var interaction = null, fingers = 0, lastTouchStart = null, startTouches = null, lastTouchY = null, clientY = null, lastclientY = 0, lastTop = 0, setInteraction = function (newInteraction, event) { if (interaction !== newInteraction) { if (interaction && !newInteraction) { switch (interaction) { case "zoom": target.handleZoomEnd(event); break; case 'drag': target.handleDragEnd(event); break; } } switch (newInteraction) { case 'zoom': target.handleZoomStart(event); break; case 'drag': target.handleDragStart(event); break; } } interaction = newInteraction; }, updateInteraction = function (event) { if (fingers === 2) { setInteraction('zoom'); } else if (fingers === 1 && target.canDrag()) { setInteraction('drag', event); } else { setInteraction(null, event); } }, targetTouches = function (touches) { return Array.prototype.slice.call(touches).map(function (touch) { return { x: touch.pageX, y: touch.pageY }; }); }, getDistance = function (a, b) { var x, y; x = a.x - b.x; y = a.y - b.y; return Math.sqrt(x * x + y * y); }, calculateScale = function (startTouches, endTouches) { var startDistance = getDistance(startTouches[0], startTouches[1]), endDistance = getDistance(endTouches[0], endTouches[1]); return endDistance / startDistance; }, cancelEvent = function (event) { event.stopPropagation(); event.preventDefault(); }, detectDoubleTap = function (event) { var time = (new Date()).getTime(); var pageY = event.changedTouches[0].pageY; var top = el.scrollTop || 0; if (fingers > 1) { lastTouchStart = null; lastTouchY = null; cancelEvent(event); } if (time - lastTouchStart < 300 && Math.abs(pageY - lastTouchY) < 10 && Math.abs(lastTop - top) < 10) { cancelEvent(event); target.handleDoubleTap(event); switch (interaction) { case "zoom": target.handleZoomEnd(event); break; case 'drag': target.handleDragEnd(event); break; } } if (fingers === 1) { lastTouchStart = time; lastTouchY = pageY; lastTop = top; } }, firstMove = true; el.addEventListener('touchstart', function (event) { if (target.enabled) { firstMove = true; fingers = event.touches.length; detectDoubleTap(event); clientY = event.changedTouches[0].clientY; if (fingers > 1) { cancelEvent(event); } } }); el.addEventListener('touchmove', function (event) { if (target.enabled) { lastclientY = event.changedTouches[0].clientY; if (firstMove) { updateInteraction(event); startTouches = targetTouches(event.touches); } else { switch (interaction) { case 'zoom': target.handleZoom(event, calculateScale(startTouches, targetTouches(event.touches))); break; case 'drag': target.handleDrag(event); break; } if (interaction) { target.update(lastclientY); } } if (fingers > 1) { cancelEvent(event); } firstMove = false; } }); el.addEventListener('touchend', function (event) { if (target.enabled) { fingers = event.touches.length; if (fingers > 1) { cancelEvent(event); } updateInteraction(event); } }); }; return PinchZoom; }; var PinchZoom = definePinchZoom(); var Pdfh5 = function (dom, options) { this.version = version; this.container = dom; this.options = options; this.thePDF = null; this.totalNum = null; this.pages = null; this.initTime = 0; this.currentNum = 1; this.loadedCount = 0; this.endTime = 0; this.pinchZoom = null; this.timer = null; this.docWidth = document.documentElement.clientWidth; this.cache = {}; this.eventType = {}; this.cacheNum = 1; this.resizeEvent = false; this.cacheData = null; this.pdfjsLibPromise = null; this.pdfjsLib = null; this.currentEditorMode = null; this.searchResults = []; this.currentSearchIndex = 0; this.currentSearchQuery = null; this.intersectionObserver = null; this.pageRenderStartTimes = {}; this.isZooming = false; this.zoomDisabled = false; this.scrollDisabled = false; this.zoomConstraints = { minScale: 0.5, maxScale: 4.0, step: 0.1 }; // 沙箱管理 this.sandboxManager = null; this.sandboxEnabled = true; // 密码保护相关 this.passwordPrompt = null; // 分段加载配置 this.progressiveLoading = false; this.chunkSize = 65536; // 64KB分块大小 this.maxMemoryPages = 5; // 最大内存页面数 this.loadedPages = new Map(); // 已加载页面缓存 this.loadingQueue = []; // 加载队列 this.memoryUsage = 0; // 内存使用量统计 // 编辑器相关属性 this.annotationEditorMode = 'NONE'; this.editorParams = { freeTextColor: '#000000', freeTextSize: 12, inkColor: '#000000', inkThickness: 1, inkOpacity: 1, stampImage: null }; this.editorAnnotations = []; // 存储所有注释 this.isEditing = false; this.selectedAnnotation = null; // 当前选择的注释 this.currentEditingTextInput = null; // 当前编辑的文本输入框 // 墨迹绘制相关属性 this.isDrawingInk = false; this.currentInkPath = null; // 注释编辑器UI管理器 this.annotationEditorUIManager = null; this.init(options); }; Pdfh5.prototype = { init: async function (options) { try { this.pdfjsLib = await initPdfJs(); // 初始化沙箱管理器 this.initSandbox(); } catch (error) { console.error('Failed to initialize PDF.js:', error); return; } if (this.container.pdfLoaded) { this.destroy(); } var $style = document.createElement('style'); $style.type = 'text/css'; $style.textContent = css; document.head.appendChild($style); this.container.pdfLoaded = false; this.container.classList.add("pdfjs"); this.initTime = new Date().getTime(); this.options = this.options ? this.options : {}; this.options.pdfurl = this.options.pdfurl ? this.options.pdfurl : null; this.options.data = this.options.data ? this.options.data : null; this.options.scale = this.options.scale ? this.options.scale : 1; this.options.zoomEnable = this.options.zoomEnable === false ? false : true; this.options.scrollEnable = this.options.scrollEnable === false ? false : true; this.options.loadingBar = this.options.loadingBar === false ? false : true; this.options.pageNum = this.options.pageNum === false ? false : true; // 密码配置项 this.options.password = this.options.password || null; this.options.backTop = this.options.backTop === false ? false : true; this.options.resize = this.options.resize === false ? false : true; this.options.textLayer = this.options.textLayer === true ? true : false; this.options.goto = isNaN(this.options.goto) ? 0 : this.options.goto; this.progressiveLoading = this.options.progressiveLoading === true ? true : false; if (this.options.chunkSize) this.chunkSize = this.options.chunkSize; if (this.options.maxMemoryPages) this.maxMemoryPages = this.options.maxMemoryPages; if (pdfjsLib && window._pdfh5ResourcePaths) { if (this.options.workerSrc) { pdfjsLib.GlobalWorkerOptions.workerSrc = this.options.workerSrc; } if (!this.options.cMapUrl) { this.options.cMapUrl = window._pdfh5ResourcePaths.cMapUrl; } if (!this.options.standardFontDataUrl) { this.options.standardFontDataUrl = window._pdfh5ResourcePaths.standardFontDataUrl; } if (!this.options.iccUrl) { this.options.iccUrl = window._pdfh5ResourcePaths.iccUrl; } if (!this.options.wasmUrl) { this.options.wasmUrl = window._pdfh5ResourcePaths.wasmUrl; } } this.createHTML(); this.bindEvents(); this.initEditorEvents(); this.loadPDF(); }, createHTML: function () { var html = ''; if (this.options.loadingBar) { html += '
' + '
' + '
' + '
' + '
' + '
'; } html += '
' + '
' + '
' + ' 1/' + '1' + '
' + '
' + '
' + '
' + '
'; this.container.innerHTML = html; var viewer = document.createElement("div"); viewer.className = 'pdfViewer'; var viewerContainer = document.createElement("div"); viewerContainer.className = 'viewerContainer'; viewerContainer.appendChild(viewer); this.container.appendChild(viewerContainer); this.viewer = viewer; this.viewerContainer = viewerContainer; this.pageNum = this.container.querySelector('.pageNum'); this.pageNow = this.pageNum.querySelector('.pageNow'); this.pageTotal = this.pageNum.querySelector('.pageTotal'); this.loadingBar = this.container.querySelector('.loadingBar'); if (this.loadingBar) { this.progress = this.loadingBar.querySelector('.progress'); } else { this.progress = null; } this.backTop = this.container.querySelector('.backTop'); this.loading = this.container.querySelector('.loading'); }, bindEvents: function () { var self = this; if (!this.options.scrollEnable) { this.viewerContainer.style.overflow = "hidden"; } else { this.viewerContainer.style.overflow = "auto"; } this.viewerContainer.addEventListener('scroll', function () { self.handleScroll(); }); this.backTop.addEventListener("click", function () { self.scrollToTop(); }); if (this.options.resize !== false) { var resizeTimeout; this.resizeHandler = function () { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(function () { if (self.container.pdfLoaded && self.thePDF) { self.updateAllPagesScale(); } }, 100); }; window.addEventListener('resize', this.resizeHandler); } }, updateAllPagesScale: function () { var self = this; if (!self.container.pdfLoaded || !self.thePDF) { return; } for (var pageNum = 1; pageNum <= self.totalNum; pageNum++) { var pageCache = self.cache[pageNum + ""]; if (pageCache && pageCache.container && pageCache.container.querySelector('canvas')) { self.updatePageScale(pageNum); } } }, updateVisiblePagesScale: function () { var self = this; if (!self.container.pdfLoaded || !self.thePDF) { return; } if (self.intersectionObserver) { var visiblePages = []; for (var pageNum = 1; pageNum <= self.totalNum; pageNum++) { var pageCache = self.cache[pageNum + ""]; if (pageCache && pageCache.container) { var rect = pageCache.container.getBoundingClientRect(); var containerRect = self.viewerContainer.getBoundingClientRect(); if (rect.top < containerRect.bottom && rect.bottom > containerRect.top) { visiblePages.push(pageNum); } } } visiblePages.forEach(function (pageNum) { var pageCache = self.cache[pageNum + ""]; if (pageCache && pageCache.container && pageCache.container.querySelector('canvas')) { self.updatePageScale(pageNum); } }); } else { self.updateAllPagesScale(); } }, updatePageScale: function (pageNum) { var self = this; var pageCache = self.cache[pageNum + ""]; if (!pageCache || !pageCache.container) { return; } if (self.loadingPages && self.loadingPages.has(pageNum)) { return; } if (!self.loadingPages) { self.loadingPages = new Set(); } self.loadingPages.add(pageNum); self.thePDF.getPage(pageNum).then(function (page) { var userScale = self.scale || 1.0; if (userScale === 1.0) { var baseViewport = page.getViewport({ scale: 1.0 }); var pageWidth = baseViewport.width; var pageHeight = baseViewport.height; var containerWidth = self.viewer.clientWidth || self.viewer.offsetWidth; var containerHeight = self.viewer.clientHeight || self.viewer.offsetHeight; // 修复PDF容器高度异常小的问题 if (containerHeight < 100) { // 如果容器高度异常小,使用窗口高度减去一些边距 containerHeight = window.innerHeight - 200; // 减去200px给其他元素留空间 } var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; var hPadding = SCROLLBAR_PADDING; var vPadding = VERTICAL_PADDING; var pageWidthScaleFactor = 1; var currentPageScale = 1.0; var pageWidthScale = (containerWidth - hPadding) / pageWidth * currentPageScale / pageWidthScaleFactor; var pageHeightScale = (containerHeight - vPadding) / pageHeight * currentPageScale; var isPortrait = pageHeight > pageWidth; var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale); userScale = Math.min(1.25, horizontalScale); } self.renderPage(page, pageNum, { scale: userScale, forceRerender: true }, 100 / self.totalNum); var textLayerDiv = pageCache.container.querySelector('.textLayer'); if (textLayerDiv && textLayerDiv.textLayer) { var PDF_TO_CSS_UNITS = 96.0 / 72.0; // 1.333... var newViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS }); var pageWidth = newViewport.rawDims.pageWidth; var pageHeight = newViewport.rawDims.pageHeight; textLayerDiv.textLayer.update({ viewport: newViewport }); var w = 'var(--total-scale-factor) * ' + pageWidth + 'px'; var h = 'var(--total-scale-factor) * ' + pageHeight + 'px'; textLayerDiv.style.width = w; textLayerDiv.style.height = h; textLayerDiv.style.transform = 'none'; textLayerDiv.style.transformOrigin = '0 0'; var containerScale = pageCache.container.style.getPropertyValue('--scale-factor') || newViewport.scale; textLayerDiv.style.setProperty('--scale-factor', containerScale); textLayerDiv.style.setProperty('--user-unit', '1'); textLayerDiv.style.setProperty('--total-scale-factor', 'calc(var(--scale-factor) * var(--user-unit))'); } if (self.loadingPages) { self.loadingPages.delete(pageNum); } }).catch(function (error) { console.error('Error updating page scale:', error); if (self.loadingPages) { self.loadingPages.delete(pageNum); } }); }, getContainerWidth: function (container) { var current = container; while (current && current !== document.body) { var computedStyle = window.getComputedStyle(current); var width = computedStyle.width; var maxWidth = computedStyle.maxWidth; if (width !== 'auto' && width !== '100%' && width !== '100vw') { var widthValue = parseFloat(width); if (widthValue > 0) { return widthValue; } } if (maxWidth !== 'none' && maxWidth !== '100%' && maxWidth !== '100vw') { var maxWidthValue = parseFloat(maxWidth); if (maxWidthValue > 0) { return maxWidthValue; } } current = current.parentElement; } var docWidth = document.documentElement.clientWidth; return docWidth; }, loadPDF: function () { var url = this.options.pdfurl; var data = this.options.data; if (url) { this.renderPdf(this.options, { url: url }); } else if (data) { this.renderPdf(this.options, { data: data }); } else { console.error("Expect options.pdfurl or options.data!"); } }, renderPdf: function (options, obj) { var self = this; this.container.pdfLoaded = true; // 字体配置 obj.cMapUrl = options.cMapUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.cMapUrl : '../cmaps/'); obj.standardFontDataUrl = options.standardFontDataUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.standardFontDataUrl : '../standard_fonts/'); // 颜色管理 obj.iccUrl = options.iccUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.iccUrl : '../iccs/'); // 图片渲染 obj.wasmUrl = options.wasmUrl || (window._pdfh5ResourcePaths ? window._pdfh5ResourcePaths.wasmUrl : '../wasm/'); obj.cMapPacked = options.cMapPacked !== false; // 默认启用压缩的CMap obj.disableFontFace = options.disableFontFace === true; // 默认不禁用字体 obj.enableXfa = options.enableXfa !== false; // 默认启用XFA obj.enableHWA = options.enableHWA !== false; // 默认启用硬件加速,对图片渲染很重要 obj.verbosity = options.verbosity || 1; // 设置详细程度 if (options.httpHeaders) { obj.httpHeaders = options.httpHeaders; } if (options.withCredentials) { obj.withCredentials = true; } if (options.password) { obj.password = options.password; } // 分段加载配置 if (self.progressiveLoading) { obj.disableStream = false; obj.disableAutoFetch = false; obj.rangeChunkSize = self.chunkSize; obj.maxImageSize = options.maxImageSize || 8388608; // 8388608最大图片大小,兼容iOS Safari obj.canvasMaxAreaInBytes = options.canvasMaxAreaInBytes || 8388608; // 8388608最大canvas面积 iOS Safari浏览器canvas限制约为16777216 } this.pdfjsLib.getDocument(obj).promise.then(function (pdf) { self.loading.style.display = "none"; self.thePDF = pdf; self.totalNum = pdf.numPages; if (options.limit > 0) { self.totalNum = options.limit; } self.pageTotal.innerText = self.totalNum; if (!self.eventBus) { self.eventBus = self; } // 初始化AnnotationEditorUIManager( if (self.pdfjsLib.AnnotationEditorUIManager && self.thePDF.annotationStorage && self.eventBus) { try { self.annotationEditorUIManager = new self.pdfjsLib.AnnotationEditorUIManager( self.container, self.pdfViewer, null, // viewerAlert null, // altTextManager null, // commentManager null, // signatureManager self.eventBus, self.thePDF, null, // pageColors null, // highlightColors false, // enableHighlightFloatingButton false, // enableUpdatedAddImage false, // enableNewAltTextWhenAddingImage null, // mlManager null, // editorUndoBar false // supportsPinchToZoom ); setTimeout(function () { for (var i = 1; i <= self.totalNum; i++) { self.createAnnotationEditorLayer(i); } }, 100); } catch (error) { } } if (self.thePDF && self.thePDF.annotationStorage) { self.thePDF.annotationStorage.onSetModified = function () { }; self.thePDF.annotationStorage.onResetModified = function () { }; } // 如果正在验证密码且PDF加载成功,隐藏密码框 if (self.passwordValidating) { self.hidePasswordPrompt(); self.passwordValidating = false; // 重置标志 } self.trigger('ready', { totalPages: self.totalNum }); if (self.progressiveLoading) { self.initProgressiveLoading(pdf, options); } else if (options.lazyLoad) { // 懒加载模式:只渲染可见页面 self.initLazyLoading(pdf, options); } else { // 传统模式:渲染所有页面 self.renderAllPages(pdf, options); } }).catch(function (err) { self.loading.style.display = "none"; // 处理密码错误 if (err.name === 'PasswordException') { self.handlePasswordError(err); } else { console.error('PDF loading error:', err); self.trigger('error', { message: 'PDF加载失败: ' + err.message }); } }); }, renderAllPages: function (pdf, options) { var self = this; // 初始化缓存 for (var i = 1; i <= self.totalNum; i++) { self.cache[i + ""] = { page: null, loaded: false, container: null, scaledViewport: null, canvas: null, imgWidth: null }; } // 初始化当前页码 self.currentNum = 1; // 检查是否有goto配置 if (self.options.goto && self.options.goto > 0 && self.options.goto <= self.totalNum) { self.currentNum = self.options.goto; } // 传统模式:一次性加载所有页面 var promise = Promise.resolve(); var num = Math.floor(100 / self.totalNum).toFixed(2); // 渲染所有页面 for (var i = 1; i <= self.totalNum; i++) { promise = promise.then(function (pageNum) { return pdf.getPage(pageNum).then(function (page) { return self.renderPage(page, pageNum, options, num); }); }.bind(null, i)); } return promise.then(function () { // 初始化TouchManager - 更好的手势缩放 if (self.options.zoomEnable) { self.initTouchManager(); } }); }, // 预计算所有页面尺寸 preCalculateAllPageSizes: function (pdf, options) { var self = this; // 获取pdfViewer的实际尺寸 var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth; var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight; // 修复PDF容器高度异常小的问题 if (pdfViewerHeight < 100) { pdfViewerHeight = window.innerHeight - 200; } // 计算pdfViewer的实际可用宽度(减去padding) var pdfViewerStyle = window.getComputedStyle(self.viewer); var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0; var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0; var availableWidth = pdfViewerWidth - paddingLeft - paddingRight; // 预计算所有页面的尺寸 var pageSizePromises = []; for (var i = 1; i <= self.totalNum; i++) { pageSizePromises.push( pdf.getPage(i).then(function (pageNum) { return function (page) { return self.calculatePageSize(page, pageNum, availableWidth, pdfViewerHeight, options); }; }(i)) ); } return Promise.all(pageSizePromises).then(function (pageSizes) { // 存储所有页面的预计算尺寸 self.preCalculatedSizes = pageSizes; return pageSizes; }); }, // 计算单个页面的尺寸 calculatePageSize: function (page, pageNum, availableWidth, pdfViewerHeight, options) { var self = this; var PDF_TO_CSS_UNITS = 96.0 / 72.0; var userScale = options.scale || 1.0; // 获取PDF页面的基础尺寸 var baseViewport = page.getViewport({ scale: 1.0 }); var pageWidth = baseViewport.width; var pageHeight = baseViewport.height; // 如果用户没有指定缩放值,使用与renderPage完全一致的自动缩放逻辑 if (userScale === 1.0) { // 使用与renderPage完全一致的缩放计算逻辑 var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; var hPadding = SCROLLBAR_PADDING; var vPadding = VERTICAL_PADDING; // 计算页面宽度缩放 var pageWidthScale = (availableWidth - hPadding) / pageWidth; var pageHeightScale = (pdfViewerHeight - vPadding) / pageHeight; // 使用官方的"auto"模式逻辑,与renderPage保持一致 var isPortrait = pageHeight > pageWidth; var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale); userScale = Math.min(1.25, horizontalScale); // MAX_AUTO_SCALE = 1.25 // 设置合理的最小缩放限制,确保内容可见 userScale = Math.max(userScale, 0.2); // 最小20% } // 计算最终的viewport var scaledViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS }); // 确保pageContainer宽度不超过pdfViewer的可用宽度 var finalWidth = Math.min(scaledViewport.width, availableWidth); var finalHeight = scaledViewport.height; // 如果宽度被限制,按比例调整高度 if (finalWidth < scaledViewport.width) { var scaleRatio = finalWidth / scaledViewport.width; finalHeight = scaledViewport.height * scaleRatio; } return { pageNum: pageNum, width: finalWidth, height: finalHeight, scale: userScale, viewport: scaledViewport }; }, // 懒加载初始化 initLazyLoading: function (pdf, options) { var self = this; // 初始化内存管理 self.loadedPages = new Map(); self.maxMemoryPages = self.options.maxMemoryPages || 5; // 默认最多保留5页 // 初始化缓存 for (var i = 1; i <= self.totalNum; i++) { self.cache[i + ""] = { page: null, loaded: false, container: null, scaledViewport: null, canvas: null, imgWidth: null, pageHeight: 0, // 存储页面实际高度 pageTop: 0 // 存储页面顶部位置 }; } // 初始化当前页码 self.currentNum = 1; // 预计算所有页面尺寸,然后创建容器 self.preCalculateAllPageSizes(pdf, options).then(function () { // 使用预计算的尺寸创建页面容器占位符 for (var i = 1; i <= self.totalNum; i++) { self.createPageContainerWithPreCalculatedSize(i, options); } // 继续原有的懒加载逻辑 self.continueLazyLoading(pdf, options); }); }, // 使用预计算尺寸创建页面容器 createPageContainerWithPreCalculatedSize: function (pageNum, options) { var self = this; var container = document.createElement('div'); container.className = 'pageContainer pageContainer' + pageNum; container.setAttribute('name', 'page=' + pageNum); container.setAttribute('data-page', pageNum); // 使用预计算的尺寸 if (self.preCalculatedSizes && self.preCalculatedSizes[pageNum - 1]) { var pageSize = self.preCalculatedSizes[pageNum - 1]; container.style.width = pageSize.width + 'px'; container.style.height = pageSize.height + 'px'; container["data-scale"] = pageSize.width / pageSize.height; // 设置CSS变量 container.style.setProperty('--scale-factor', pageSize.viewport.scale); } else { // 如果没有预计算尺寸,使用默认尺寸 container.style.width = '100%'; container.style.height = 'auto'; container["data-scale"] = 1.0; } self.cache[pageNum + ""].container = container; self.viewer.appendChild(container); }, // 继续懒加载逻辑 continueLazyLoading: function (pdf, options) { var self = this; // 立即渲染第一页(确保有内容显示) if (self.totalNum > 0) { // 检查是否有goto配置 var startPage = 1; if (self.options.goto && self.options.goto > 0 && self.options.goto <= self.totalNum) { startPage = self.options.goto; self.currentNum = startPage; } self.renderPageLazy(pdf, startPage, options); // 如果有goto配置,延迟滚动到指定页面 if (self.options.goto && self.options.goto > 1) { setTimeout(function () { self.scrollToPage(self.options.goto); }, 100); } } // 懒加载模式下隐藏loadingBar self.hideLoadingBarForLazyLoading(); // 使用滚动事件监听器替代 IntersectionObserver self.initScrollBasedLazyLoading(pdf, options); // 初始化TouchManager if (self.options.zoomEnable) { self.initTouchManager(); } }, // 懒加载模式下隐藏loadingBar hideLoadingBarForLazyLoading: function () { var self = this; // 设置进度条为100% if (self.options.loadingBar && self.progress) { self.progress.style.width = "100%"; } // 延迟隐藏loadingBar,确保用户能看到100%的进度 if (self.loadingBar) { setTimeout(function () { self.loadingBar.style.display = "none"; // 触发懒加载完成事件 var time = new Date().getTime(); self.endTime = time - self.initTime; // 触发完成事件 var arr1 = self.eventType["complete"]; if (arr1 && arr1 instanceof Array) { for (var i = 0; i < arr1.length; i++) { arr1[i] && arr1[i].call(self, "success", "pdf懒加载初始化完成", self.endTime); } } }, 300); } }, // 基于滚动位置的懒加载 initScrollBasedLazyLoading: function (pdf, options) { var self = this; // 获取所有页面的实际高度和位置 self.updatePagePositions(); // 防抖机制,避免频繁调用 var scrollTimeout = null; self.viewerContainer.addEventListener('scroll', function () { if (scrollTimeout) { clearTimeout(scrollTimeout); } scrollTimeout = setTimeout(function () { // 检查并加载可见页面 self.checkAndLoadVisiblePages(pdf, options); // 懒加载模式也需要内存管理 if (self.loadedPages && self.loadedPages.size > self.maxMemoryPages) { self.forceCleanupMemory(); } }, 100); // 100ms防抖 }); // 初始检查 setTimeout(function () { self.checkAndLoadVisiblePages(pdf, options); }, 100); }, // 更新页面位置信息 updatePagePositions: function () { var self = this; var currentTop = 0; // 获取第一页的实际高度作为默认高度 var defaultPageHeight = 400; // 更小的默认高度 if (self.cache["1"] && self.cache["1"].loaded && self.cache["1"].scaledViewport) { defaultPageHeight = self.cache["1"].scaledViewport.height; } for (var i = 1; i <= self.totalNum; i++) { var container = self.cache[i + ""].container; if (container) { // 如果页面已加载,使用实际高度 if (self.cache[i + ""].loaded && self.cache[i + ""].scaledViewport) { self.cache[i + ""].pageHeight = self.cache[i + ""].scaledViewport.height; } else { // 使用第一页的高度作为默认高度 self.cache[i + ""].pageHeight = defaultPageHeight; } self.cache[i + ""].pageTop = currentTop; currentTop += self.cache[i + ""].pageHeight + 8; // 8px是页面间距 } } }, // 检查并加载可见页面 checkAndLoadVisiblePages: function (pdf, options) { // 分段加载模式下,使用Intersection Observer处理页面加载 // 此方法仅在懒加载模式下使用 if (this.progressiveLoading) { return; } var self = this; var scrollTop = self.viewerContainer.scrollTop; var containerHeight = self.viewerContainer.clientHeight; // 只加载当前视口附近的页面,不要一次性加载太多 var buffer = 100; // 减小缓冲区域 var loadTop = scrollTop - buffer; var loadBottom = scrollTop + containerHeight + buffer; var loadedCount = 0; var toLoad = []; // 收集需要加载的页面 for (var i = 1; i <= self.totalNum; i++) { if (!self.cache[i + ""].loaded) { var container = self.cache[i + ""].container; if (container) { // 使用DOM元素的实际位置 var containerRect = container.getBoundingClientRect(); var viewerRect = self.viewerContainer.getBoundingClientRect(); // 计算相对于滚动容器的位置 var pageTop = containerRect.top - viewerRect.top + scrollTop; var pageBottom = pageTop + containerRect.height; // 检查页面是否在加载范围内 if (pageBottom > loadTop && pageTop < loadBottom) { toLoad.push(i); } } } else { loadedCount++; } } // 只加载最接近视口的页面,避免一次性加载太多 if (toLoad.length > 0) { // 按距离视口中心的距离排序 var center = scrollTop + containerHeight / 2; toLoad.sort(function (a, b) { var containerA = self.cache[a + ""].container; var containerB = self.cache[b + ""].container; var rectA = containerA.getBoundingClientRect(); var rectB = containerB.getBoundingClientRect(); var viewerRect = self.viewerContainer.getBoundingClientRect(); var posA = rectA.top - viewerRect.top + scrollTop; var posB = rectB.top - viewerRect.top + scrollTop; return Math.abs(posA - center) - Math.abs(posB - center); }); // 只加载前2个最接近的页面 var maxLoad = Math.min(2, toLoad.length); for (var j = 0; j < maxLoad; j++) { var pageNum = toLoad[j]; self.renderPageLazy(pdf, pageNum, options); } } }, // 创建页面容器占位符 createPageContainer: function (pageNum, options, page) { var self = this; var container = document.createElement('div'); container.className = 'pageContainer pageContainer' + pageNum; container.setAttribute('name', 'page=' + pageNum); container.setAttribute('data-page', pageNum); // 使用与官方viewer.mjs相同的缩放因子和自动缩放逻辑 var PDF_TO_CSS_UNITS = 96.0 / 72.0; var userScale = options.scale || 1.0; // 如果用户没有指定缩放值,使用官方的"auto"模式 if (userScale === 1.0) { // 使用默认的自动缩放值,这个值会在renderPage中重新计算 userScale = 1.0; } var effectiveScale = userScale * PDF_TO_CSS_UNITS; // 设置容器占位尺寸 - 使用与renderPage相同的计算逻辑 if (page) { // 获取PDF页面的基础尺寸 var baseViewport = page.getViewport({ scale: 1.0 }); var pageWidth = baseViewport.width; var pageHeight = baseViewport.height; // 获取pdfViewer的实际尺寸 var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth; var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight; // 修复PDF容器高度异常小的问题 if (pdfViewerHeight < 100) { // 如果容器高度异常小,使用窗口高度减去一些边距 pdfViewerHeight = window.innerHeight - 200; // 减去200px给其他元素留空间 } // 计算pdfViewer的实际可用宽度(减去padding) var pdfViewerStyle = window.getComputedStyle(self.viewer); var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0; var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0; var availableWidth = pdfViewerWidth - paddingLeft - paddingRight; // 计算缩放比例 var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; var hPadding = SCROLLBAR_PADDING; var vPadding = VERTICAL_PADDING; var pageWidthScale = (availableWidth - hPadding) / pageWidth; var pageHeightScale = (pdfViewerHeight - vPadding) / pageHeight; var isPortrait = pageHeight > pageWidth; var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale); var userScale = Math.min(1.25, horizontalScale); // 计算最终的viewport var scaledViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS }); // 确保pageContainer宽度不超过pdfViewer的可用宽度 var finalWidth = Math.min(scaledViewport.width, availableWidth); var finalHeight = scaledViewport.height; // 如果宽度被限制,按比例调整高度 if (finalWidth < scaledViewport.width) { var scaleRatio = finalWidth / scaledViewport.width; finalHeight = scaledViewport.height * scaleRatio; } container.style.width = finalWidth + 'px'; container.style.height = finalHeight + 'px'; container["data-scale"] = finalWidth / finalHeight; } else { // 如果没有page对象,使用默认尺寸 container.style.width = '100%'; container.style.height = 'auto'; container["data-scale"] = 1.0; } // 设置与官方viewer.mjs一致的CSS变量 container.style.setProperty('--scale-factor', effectiveScale); self.cache[pageNum + ""].container = container; self.viewer.appendChild(container); }, // 懒加载渲染页面 renderPageLazy: function (pdf, pageNum, options) { var self = this; if (self.cache[pageNum + ""].loaded) { return Promise.resolve(); } return pdf.getPage(pageNum).then(function (page) { var num = Math.floor(100 / self.totalNum).toFixed(2); return self.renderPage(page, pageNum, options, num); }).then(function () { // 懒加载模式下也需要更新loadedPages if (self.loadedPages) { self.loadedPages.set(pageNum, { page: self.cache[pageNum + ""].page, loaded: true, loadTime: new Date().getTime(), container: self.cache[pageNum + ""].container }); } }).catch(function (error) { console.error('Failed to render page', pageNum, error); }); }, renderPage: function (page, pageNum, options, progressNum) { var self = this; // 记录页面渲染开始时间 self.pageRenderStartTimes[pageNum] = new Date().getTime(); // 使用与官方viewer.mjs完全一致的缩放逻辑 var PDF_TO_CSS_UNITS = 96.0 / 72.0; var userScale = options.scale || 1.0; var baseViewport = page.getViewport({ scale: 1.0 }); // 如果用户没有指定缩放值,使用智能的"auto"模式 if (userScale === 1.0) { // 获取页面基础尺寸 var pageWidth = baseViewport.width; var pageHeight = baseViewport.height; // 获取pdfViewer的实际尺寸(与createPageContainer保持一致) var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth; var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight; // 修复PDF容器高度异常小的问题 if (pdfViewerHeight < 100) { // 如果容器高度异常小,使用窗口高度减去一些边距 pdfViewerHeight = window.innerHeight - 200; // 减去200px给其他元素留空间 } // 计算pdfViewer的实际可用宽度(减去padding) var pdfViewerStyle = window.getComputedStyle(self.viewer); var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0; var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0; var availableWidth = pdfViewerWidth - paddingLeft - paddingRight; // 使用统一的缩放计算逻辑 var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; var hPadding = SCROLLBAR_PADDING; var vPadding = VERTICAL_PADDING; // 计算页面宽度缩放 var pageWidthScale = (availableWidth - hPadding) / pageWidth; var pageHeightScale = (pdfViewerHeight - vPadding) / pageHeight; // 使用官方的"auto"模式逻辑 var isPortrait = pageHeight > pageWidth; var horizontalScale = isPortrait ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale); userScale = Math.min(1.25, horizontalScale); // MAX_AUTO_SCALE = 1.25 } // 如果用户指定了缩放值(如1.5、2.0等),直接使用用户设置的值 var scaledViewport = page.getViewport({ scale: userScale * PDF_TO_CSS_UNITS }); // 检查容器是否已存在(懒加载模式) var container = self.cache[pageNum + ""].container; if (!container) { container = document.createElement('div'); container.className = 'pageContainer pageContainer' + pageNum; container.setAttribute('name', 'page=' + pageNum); self.viewer.appendChild(container); } var pdfViewerWidth = self.viewer.clientWidth || self.viewer.offsetWidth; var pdfViewerHeight = self.viewer.clientHeight || self.viewer.offsetHeight; var pdfViewerStyle = window.getComputedStyle(self.viewer); var paddingLeft = parseFloat(pdfViewerStyle.paddingLeft) || 0; var paddingRight = parseFloat(pdfViewerStyle.paddingRight) || 0; var availableWidth = pdfViewerWidth - paddingLeft - paddingRight; var finalWidth = Math.min(scaledViewport.width, availableWidth); var finalHeight = scaledViewport.height; if (finalWidth < scaledViewport.width) { var scaleRatio = finalWidth / scaledViewport.width; finalHeight = scaledViewport.height * scaleRatio; } container.style.width = finalWidth + 'px'; container.style.height = finalHeight + 'px'; container.style.setProperty('--scale-factor', scaledViewport.scale); self.cache[pageNum + ""].container = container; self.cache[pageNum + ""].scaledViewport = scaledViewport; return self.renderCanvas(page, scaledViewport, pageNum, progressNum, container, options, scaledViewport); }, renderCanvas: function (page, viewport, pageNum, num, container, options, scaledViewport) { var self = this; // 检查是否已经存在canvas,避免重复创建 var existingCanvas = container.querySelector('canvas'); if (existingCanvas) { // 如果是resize更新,需要重新渲染canvas内容 if (options && options.forceRerender) { // 移除旧的canvas existingCanvas.remove(); } else { // 普通情况,避免重复创建 return Promise.resolve(); } } var canvas = document.createElement("canvas"); // 根据 enableHWA 设置 willReadFrequently,这对图片渲染很重要 var enableHWA = this.options.enableHWA !== false; var context = canvas.getContext('2d', { alpha: false, willReadFrequently: !enableHWA }); // 使用设备像素比设置 Canvas 实际尺寸,提高清晰度 var devicePixelRatio = window.devicePixelRatio || 1; // 根据pageContainer的实际尺寸重新计算viewport缩放比例 var containerWidth = container.clientWidth || container.offsetWidth; var containerHeight = container.clientHeight || container.offsetHeight; // 如果容器尺寸为0(初始渲染时),使用原始viewport if (containerWidth === 0 || containerHeight === 0) { var canvasWidth = viewport.width; var canvasHeight = viewport.height; var adjustedViewport = viewport; } else { // 计算缩放比例,确保完全显示在pageContainer内 var scaleX = containerWidth / viewport.width; var scaleY = containerHeight / viewport.height; var scale = Math.min(scaleX, scaleY); // 选择较小的缩放比例,确保完全显示 // 计算缩放后的canvas尺寸 var canvasWidth = viewport.width * scale; var canvasHeight = viewport.height * scale; // 重新计算viewport的缩放比例,让PDF内容也按比例缩小 var originalScale = viewport.scale; var adjustedScale = originalScale * scale; // 创建调整后的viewport,让PDF内容按比例渲染 var adjustedViewport = page.getViewport({ scale: adjustedScale }); } // 设置canvas的实际尺寸(考虑设备像素比) canvas.width = canvasWidth * devicePixelRatio; canvas.height = canvasHeight * devicePixelRatio; canvas.style.width = canvasWidth + 'px'; canvas.style.height = canvasHeight + 'px'; // 缩放上下文以匹配设备像素比 context.scale(devicePixelRatio, devicePixelRatio); var renderObj = { canvasContext: context, viewport: adjustedViewport }; return page.render(renderObj).promise.then(function () { self.loadedCount++; // 隐藏加载效果 var loadEffect = container.querySelector('.loadEffect'); if (loadEffect) { loadEffect.style.display = "none"; } canvas.className = "canvasImg" + pageNum; container.appendChild(canvas); // 页面渲染完成后,检查是否需要清理内存 if (self.loadedPages && self.loadedPages.size > self.maxMemoryPages) { // 延迟清理,避免影响当前渲染 setTimeout(function () { self.forceCleanupMemory(); }, 100); } // 标记页面为已加载 self.cache[pageNum + ""].loaded = true; self.cache[pageNum + ""].page = page; self.cache[pageNum + ""].canvas = canvas; self.cache[pageNum + ""].scaledViewport = viewport; // 更新页面位置信息(懒加载需要) if (self.options.lazyLoad) { self.updatePagePositions(); } // 渲染文本层(如果启用) if (self.options.textLayer) { // textLayer使用原始的scaledViewport,不需要调整 self.renderTextLayer(page, pageNum, container, scaledViewport); } // 更新进度条(分段加载模式下不显示进度条) if (self.options.loadingBar && self.progress && !self.progressiveLoading) { self.progress.style.width = num * self.loadedCount + "%"; } // 触发渲染事件 var time = new Date().getTime(); var totalTime = time - self.initTime; // 总耗时 var pageTime = time - (self.pageRenderStartTimes[pageNum] || self.initTime); // 单页耗时 var arr1 = self.eventType["render"]; if (arr1 && arr1 instanceof Array) { for (var i = 0; i < arr1.length; i++) { arr1[i] && arr1[i].call(self, pageNum, pageTime, totalTime, container); } } // 检查是否所有页面都加载完成(分段加载模式下不触发) if (!self.progressiveLoading && self.loadedCount === self.totalNum) { self.finalRender(options); } }); }, // 渲染文本层 - 使用与Canvas完全相同的viewport renderTextLayer: function (page, pageNum, container, viewport, options) { var self = this; // 设置默认的options options = options || {}; // 获取canvas尺寸(如果提供) var canvasWidth = options.canvasWidth; var canvasHeight = options.canvasHeight; // 验证参数 if (!container || typeof container.querySelector !== 'function') { return; } // 检查是否已存在文本层 var existingTextLayer = container.querySelector(".textLayer"); if (existingTextLayer && !options.forceRerender) { return; } // 如果是强制重新渲染,移除现有的textLayer if (existingTextLayer && options.forceRerender) { existingTextLayer.remove(); } // 创建文本层容器 - 使用与官方viewer.mjs完全一致的样式 var textLayerDiv = document.createElement('div'); textLayerDiv.setAttribute('class', 'textLayer'); textLayerDiv.setAttribute('role', 'presentation'); textLayerDiv.setAttribute('aria-label', 'Text layer'); textLayerDiv.tabIndex = 0; textLayerDiv.style.setProperty('--scale-factor', viewport.scale); textLayerDiv.style.setProperty('--user-unit', '1'); textLayerDiv.style.setProperty('--total-scale-factor', 'calc(var(--scale-factor) * var(--user-unit))'); textLayerDiv.style.position = 'absolute'; textLayerDiv.style.inset = '0'; textLayerDiv.style.overflow = 'clip'; textLayerDiv.style.opacity = '1'; textLayerDiv.style.lineHeight = '1'; textLayerDiv.style.userSelect = 'text'; textLayerDiv.style.pointerEvents = 'auto'; textLayerDiv.style.cursor = 'text'; textLayerDiv.style.transformOrigin = '0 0'; textLayerDiv.style.zIndex = '101'; textLayerDiv.style.textAlign = 'initial'; textLayerDiv.style.textSizeAdjust = 'none'; textLayerDiv.style.forcedColorAdjust = 'none'; container.appendChild(textLayerDiv); page.getTextContent({ includeMarkedContent: true, disableNormalization: true }).then(function (textContent) { // 检查pdfjsLib是否可用 if (!self.pdfjsLib || !self.pdfjsLib.TextLayer) { return; } // 注意:TextLayer内部会处理缩放,所以传递原始的viewport // 对于密码保护PDF,确保viewport与容器尺寸匹配 var textLayerViewport = viewport; if (self.options.password && container.clientWidth > 0 && container.clientHeight > 0) { // 密码保护PDF重新加载后,需要重新计算viewport var containerWidth = container.clientWidth; var containerHeight = container.clientHeight; var scaleX = containerWidth / viewport.width; var scaleY = containerHeight / viewport.height; var scale = Math.min(scaleX, scaleY); // 创建新的viewport,确保与容器尺寸匹配 textLayerViewport = page.getViewport({ scale: viewport.scale * scale }); } var textLayer = new self.pdfjsLib.TextLayer({ textContentSource: textContent, container: textLayerDiv, viewport: textLayerViewport, textDivs: [], textContentItemsStr: [] }); // 将textLayer对象存储到DOM元素上,方便后续访问 textLayerDiv.textLayer = textLayer; // 渲染文本层 textLayer.render().then(function () { // 根据pageContainer的实际尺寸来设置textLayer尺寸 var containerWidth = container.clientWidth || container.offsetWidth; var containerHeight = container.clientHeight || container.offsetHeight; // 如果容器尺寸为0(初始渲染时),使用viewport尺寸 if (containerWidth === 0 || containerHeight === 0) { var pageWidth = viewport.rawDims.pageWidth; var pageHeight = viewport.rawDims.pageHeight; var w = 'var(--total-scale-factor) * ' + pageWidth + 'px'; var h = 'var(--total-scale-factor) * ' + pageHeight + 'px'; } else { // 计算textLayer的缩放比例,确保完全显示在pageContainer内 var scaleX = containerWidth / viewport.width; var scaleY = containerHeight / viewport.height; var scale = Math.min(scaleX, scaleY); // 计算缩放后的textLayer尺寸 var scaledWidth = viewport.width * scale; var scaledHeight = viewport.height * scale; var w = scaledWidth + 'px'; var h = scaledHeight + 'px'; } textLayerDiv.style.width = w; textLayerDiv.style.height = h; // 移除手动transform,让CSS变量自动处理 textLayerDiv.style.transform = 'none'; textLayerDiv.style.transformOrigin = '0 0'; // 使用与pageContainer相同的scale,确保textLayer与pageContainer完全一致 // 从pageContainer获取--scale-factor值,确保一致性 var containerScale = container.style.getPropertyValue('--scale-factor') || viewport.scale; textLayerDiv.style.setProperty('--scale-factor', containerScale); textLayerDiv.style.setProperty('--user-unit', '1'); textLayerDiv.style.setProperty('--total-scale-factor', 'calc(var(--scale-factor) * var(--user-unit))'); // 处理密码保护PDF的markedContent包装层 // 移除class="markedContent"的包装,只保留role="presentation"的span var markedContentSpans = textLayerDiv.querySelectorAll('.markedContent'); markedContentSpans.forEach(function (markedContentSpan) { // 获取所有子元素 var children = Array.from(markedContentSpan.children); // 将子元素移动到父级 children.forEach(function (child) { markedContentSpan.parentNode.insertBefore(child, markedContentSpan); }); // 移除空的markedContent包装 markedContentSpan.remove(); }); // 触发文本层渲染完成事件 if (self.eventType && self.eventType["textlayerrendered"]) { var arr1 = self.eventType["textlayerrendered"]; if (arr1 && arr1 instanceof Array) { for (var i = 0; i < arr1.length; i++) { arr1[i] && arr1[i].call(self, { source: self, pageNumber: pageNum, numTextDivs: textLayerDiv.children.length }); } } } }).catch(function (error) { console.error('Text layer rendering error:', error); }); }).catch(function (error) { console.error('Text content extraction error:', error); }); }, finalRender: function (options) { var time = new Date().getTime(); var self = this; if (self.options.loadingBar && self.progress) { self.progress.style.width = "100%"; } if (self.loadingBar) { setTimeout(function () { self.loadingBar.style.display = "none"; }, 300); } self.endTime = time - self.initTime; // 触发完成事件 var arr1 = self.eventType["complete"]; if (arr1 && arr1 instanceof Array) { for (var i = 0; i < arr1.length; i++) { arr1[i] && arr1[i].call(self, "success", "pdf加载完成", self.endTime); } } }, handleScroll: function () { // 原有的滚动处理逻辑 var scrollTop = this.viewerContainer.scrollTop; var containerH = this.container.offsetHeight; var height = containerH * (1 / 3); // 触发滚动事件 this.trigger('scroll', { scrollTop: scrollTop, currentNum: this.currentNum }); if (scrollTop >= 150) { if (this.options.backTop) { this.backTop.style.display = "block"; } } else { if (this.options.backTop) { this.backTop.style.display = "none"; } } // 显示页码 clearTimeout(this.timer); if (this.options.pageNum && this.pageNum) { this.pageNum.style.display = "block"; } // 更新当前页码 - 修复懒加载模式下的页码计算 if (this.viewerContainer) { this.pages = this.viewerContainer.querySelectorAll('.pageContainer'); } if (this.pages) { this.pages.forEach(function (obj, index) { var rect = obj.getBoundingClientRect(); var top = rect.top; var bottom = rect.bottom; if (top <= height && bottom > height) { // 获取页面的实际页码,而不是数组索引 var pageNum = parseInt(obj.getAttribute('name').split('=')[1]) || (index + 1); if (this.options.pageNum) { this.pageNow.innerText = pageNum; } // 只有当页码真正改变时才更新和触发事件 if (this.currentNum !== pageNum) { this.currentNum = pageNum; this.trigger('pageChanged', { pageNumber: pageNum }); } } }.bind(this)); } // 处理特殊情况 - 修复懒加载模式下的页码计算 if (scrollTop === 0) { if (this.options.pageNum) { this.pageNow.innerText = 1; } if (this.currentNum !== 1) { this.currentNum = 1; this.trigger('pageChanged', { pageNumber: 1 }); } } else { // 在懒加载模式下,只有在真正滚动到底部且最后一页已渲染时才显示最后一页 var lastPageContainer = this.viewerContainer.querySelector('.pageContainer[name="page=' + this.totalNum + '"]'); if (lastPageContainer && this.cache[this.totalNum + ""].loaded) { var lastRect = lastPageContainer.getBoundingClientRect(); var viewerRect = this.viewerContainer.getBoundingClientRect(); // 更严格的检查:最后一页必须完全在视口内,且滚动位置接近底部 var isLastPageVisible = lastRect.bottom <= viewerRect.bottom && lastRect.top >= viewerRect.top; var isNearBottom = scrollTop + this.container.offsetHeight >= this.viewer.offsetHeight - 100; // 100px容差 var isLastPageLoaded = this.cache[this.totalNum + ""].loaded; // 只有在所有条件都满足时才显示最后一页 if (isLastPageVisible && isNearBottom && isLastPageLoaded) { // 添加额外的检查:确保没有其他页面在视口内 var hasOtherPagesInView = false; for (var i = 1; i < this.totalNum; i++) { var otherPage = this.viewerContainer.querySelector('.pageContainer[name="page=' + i + '"]'); if (otherPage && this.cache[i + ""].loaded) { var otherRect = otherPage.getBoundingClientRect(); if (otherRect.bottom > viewerRect.top && otherRect.top < viewerRect.bottom) { hasOtherPagesInView = true; break; } } } if (!hasOtherPagesInView) { if (this.options.pageNum) { this.pageNow.innerText = this.totalNum; } if (this.currentNum !== this.totalNum) { this.currentNum = this.totalNum; this.trigger('pageChanged', { pageNumber: this.totalNum }); } } } } } // 自动隐藏页码 this.timer = setTimeout(function () { if (this.options.pageNum && this.pageNum) { this.pageNum.style.display = "none"; } }.bind(this), 1500); }, scrollToTop: function () { this.viewerContainer.scrollTo({ top: 0, behavior: "smooth" }); }, // 滚动到指定页面 scrollToPage: function (pageNum) { var self = this; if (pageNum < 1 || pageNum > self.totalNum) { return; } // 确保页面已加载 if (!self.cache[pageNum + ""].loaded) { // 如果页面未加载,先加载它 self.renderPageLazy(self.thePDF, pageNum, self.options); } // 计算页面位置并滚动 setTimeout(function () { var container = self.cache[pageNum + ""].container; if (container) { var containerRect = container.getBoundingClientRect(); var viewerRect = self.viewerContainer.getBoundingClientRect(); var scrollTop = self.viewerContainer.scrollTop; // 计算页面相对于滚动容器的位置 var pageTop = containerRect.top - viewerRect.top + scrollTop; // 滚动到页面顶部 self.viewerContainer.scrollTo({ top: pageTop, behavior: "smooth" }); // 更新当前页码 self.currentNum = pageNum; if (self.options.pageNum) { self.pageNow.innerText = pageNum; } } }, 50); }, // 保持原有的其他方法... show: function (callback) { this.container.style.display = "block"; callback && callback.call(this); }, hide: function (callback) { this.container.style.display = "none"; callback && callback.call(this); }, on: function (type, callback) { if (this.eventType[type] && this.eventType[type] instanceof Array) { this.eventType[type].push(callback); } else { this.eventType[type] = [callback]; } }, off: function (type) { if (type !== undefined) { this.eventType[type] = [null]; } else { for (var i in this.eventType) { this.eventType[i] = [null]; } } }, trigger: function (event, data) { if (this.eventType[event]) { this.eventType[event].forEach(function (callback) { callback(data); }); } }, // 创建注释编辑器层 createAnnotationEditorLayer: function (pageNum) { var self = this; if (!self.annotationEditorUIManager) { return; } try { // 检查页面缓存是否存在 if (!self.cache || !self.cache[pageNum + ""]) { return; } // 获取页面容器 var pageContainer = self.cache[pageNum + ""].container; if (!pageContainer) { return; } // 创建注释编辑器层div var editorLayerDiv = document.createElement('div'); editorLayerDiv.className = 'annotationEditorLayer'; editorLayerDiv.style.position = 'absolute'; editorLayerDiv.style.top = '0'; editorLayerDiv.style.left = '0'; editorLayerDiv.style.width = '100%'; editorLayerDiv.style.height = '100%'; editorLayerDiv.style.pointerEvents = 'none'; editorLayerDiv.style.zIndex = '1000'; // 添加到页面容器 pageContainer.appendChild(editorLayerDiv); // 创建注释编辑器层 var editorLayer = new self.pdfjsLib.AnnotationEditorLayer({ uiManager: self.annotationEditorUIManager, div: editorLayerDiv, structTreeLayer: null, accessibilityManager: null, pageIndex: pageNum - 1, viewport: self.cache[pageNum + ""].scaledViewport }); // 将注释编辑器层添加到AnnotationEditorUIManager self.annotationEditorUIManager.addLayer(editorLayer); } catch (error) { } }, // 初始化手势缩放 - 使用老版本的PinchZoom实现 initTouchManager: function () { var self = this; // 销毁现有的PinchZoom if (this.pinchZoom) { this.pinchZoom.destroy(); this.pinchZoom = null; } // 确保viewerContainer存在 if (!this.viewerContainer) { return; } // 创建新的PinchZoom - 使用老版本的实现 this.pinchZoom = new PinchZoom(this.viewer, { tapZoomFactor: this.options.tapZoomFactor || 2, zoomOutFactor: this.options.zoomOutFactor || 1.2, animationDuration: this.options.animationDuration || 300, maxZoom: this.zoomConstraints.maxScale, minZoom: this.zoomConstraints.minScale }, this.viewerContainer); // 设置缩放完成回调 var timeout, firstZoom = true; this.pinchZoom.done = function (scale) { clearTimeout(timeout); timeout = setTimeout(function () { // 更新缩放值 self.scale = scale; // 智能更新策略:根据是否启用懒加载决定更新方式 if (self.thePDF && self.container.pdfLoaded) { if (self.lazyLoad || self.progressiveLoading) { // 懒加载模式:只更新可见页面,避免重新加载所有页面 self.updateVisiblePagesScale(); } else { // 普通模式:更新所有页面 self.updateAllPagesScale(); } } // 触发缩放事件 self.trigger('zoom', { scale: scale }); }, 100); // 减少延迟时间,提高响应性 if (scale == 1) { if (self.viewerContainer) { self.viewerContainer.style.webkitOverflowScrolling = "touch"; } } else { if (self.viewerContainer) { self.viewerContainer.style.webkitOverflowScrolling = "auto"; } } }; }, // 更新缩放 - 参考PDF.js官方实现 updateZoom: function (newScale, origin) { var self = this; // 使用官方的方式设置CSS变量 var PDF_TO_CSS_UNITS = 96.0 / 72.0; this.viewer.style.setProperty('--scale-factor', newScale * PDF_TO_CSS_UNITS); // 计算缩放中心点 var centerX, centerY; if (origin && origin.length >= 2) { // 使用手势中心点 centerX = origin[0]; centerY = origin[1]; } else { // 使用容器中心点 centerX = this.viewerContainer.clientWidth / 2; centerY = this.viewerContainer.clientHeight / 2; } // 将屏幕坐标转换为相对于容器的坐标 var containerRect = this.viewerContainer.getBoundingClientRect(); var relativeX = centerX - containerRect.left; var relativeY = centerY - containerRect.top; // 应用缩放变换 this.viewer.style.transform = 'scale(' + newScale + ')'; this.viewer.style.transformOrigin = relativeX + 'px ' + relativeY + 'px'; // 添加过渡效果,使缩放更平滑 this.viewer.style.transition = 'transform 0.1s ease-out'; // 重新渲染所有页面以应用新的缩放 if (this.thePDF && this.container.pdfLoaded) { this.updateAllPagesScale(); } // 触发缩放事件 this.trigger('zoom', { scale: newScale, origin: origin, centerX: relativeX, centerY: relativeY }); }, // ==================== 缩放控制API ==================== // 禁用/启用缩放手势 setZoomEnabled: function (enabled) { this.zoomDisabled = !enabled; return this; }, // 获取缩放状态 isZoomEnabled: function () { return !this.zoomDisabled; }, // 禁用/启用滚动 setScrollEnabled: function (enabled) { this.scrollDisabled = !enabled; if (this.viewerContainer) { this.viewerContainer.style.overflow = enabled ? 'auto' : 'hidden'; } return this; }, // 获取滚动状态 isScrollEnabled: function () { return !this.scrollDisabled; }, // 设置缩放约束 setZoomConstraints: function (constraints) { if (constraints.minScale !== undefined) { this.zoomConstraints.minScale = Math.max(0.1, constraints.minScale); } if (constraints.maxScale !== undefined) { this.zoomConstraints.maxScale = Math.min(20.0, constraints.maxScale); } if (constraints.step !== undefined) { this.zoomConstraints.step = Math.max(0.01, constraints.step); } return this; }, // 获取缩放约束 getZoomConstraints: function () { return Object.assign({}, this.zoomConstraints); }, // 检查是否正在缩放 isZooming: function () { return this.isZooming; }, // ==================== 新增功能API ==================== // 页面跳转功能API goToPage: function (pageNum) { var self = this; if (pageNum < 1 || pageNum > self.totalNum) { return false; } self.currentNum = pageNum; // 滚动到指定页面 var pageContainer = null; // 分段加载模式:使用loadedPages if (self.progressiveLoading && self.loadedPages.has(pageNum)) { var pageData = self.loadedPages.get(pageNum); pageContainer = pageData.container; } // 传统模式:使用cache else if (self.cache[pageNum + ""]) { pageContainer = self.cache[pageNum + ""].container; } // 如果页面未加载,先加载页面 if (!pageContainer) { if (self.progressiveLoading) { // 分段加载模式:异步加载页面 self.loadPageProgressive(pageNum, self.options).then(function () { // 页面加载完成后滚动 setTimeout(function () { self.scrollToPage(pageNum); }, 100); }); } else { // 传统模式:直接渲染页面 self.renderPage(self.thePDF.getPage(pageNum), pageNum, self.options); setTimeout(function () { self.scrollToPage(pageNum); }, 100); } } else { // 页面已加载,直接滚动 pageContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); } self.trigger('pageChanged', { pageNumber: pageNum }); return true; }, nextPage: function () { var self = this; if (self.currentNum < self.totalNum) { return self.goToPage(self.currentNum + 1); } return false; }, prevPage: function () { var self = this; if (self.currentNum > 1) { return self.goToPage(self.currentNum - 1); } return false; }, // 缩放功能API zoomIn: function () { var self = this; if (!self.thePDF) { console.warn('No PDF loaded'); return false; } // 防抖机制 if (self.zoomTimeout) { clearTimeout(self.zoomTimeout); } var newScale = Math.min(self.scale * 1.2, 4.0); self.zoomTimeout = setTimeout(function () { self.setScale(newScale); }, 100); return true; }, zoomOut: function () { var self = this; if (!self.thePDF) { console.warn('No PDF loaded'); return false; } // 防抖机制 if (self.zoomTimeout) { clearTimeout(self.zoomTimeout); } var newScale = Math.max(self.scale / 1.2, 0.25); self.zoomTimeout = setTimeout(function () { self.setScale(newScale); }, 100); return true; }, setScale: function (scale) { var self = this; if (!self.thePDF) { console.warn('No PDF loaded'); return false; } if (typeof scale === 'string') { // 处理特殊缩放模式 switch (scale) { case 'auto': scale = 1.0; break; case 'page-actual': scale = 1.0; break; case 'page-fit': // 使用PDF页面的实际尺寸计算缩放 var pageWidth = self.docWidth; // 这里应该使用PDF页面的实际宽度 scale = self.viewer.clientWidth / pageWidth; break; case 'page-width': // 使用PDF页面的实际尺寸计算缩放 var pageWidth = self.docWidth; // 这里应该使用PDF页面的实际宽度 scale = self.viewer.clientWidth / pageWidth; break; default: // 处理百分比字符串,如 "200%" -> 2.0 if (scale.includes('%')) { scale = parseFloat(scale) / 100; } else { scale = parseFloat(scale) || 1.0; } } } // 使用配置的缩放约束 var minScale = this.zoomConstraints.minScale || 0.5; var maxScale = this.zoomConstraints.maxScale || 4.0; self.scale = Math.max(minScale, Math.min(maxScale, scale)); // 触发缩放事件 self.trigger('scaleChanged', { scale: self.scale }); // 重新渲染已加载的页面 if (self.thePDF) { for (var i = 1; i <= self.totalNum; i++) { if (self.cache[i + ""].loaded) { self.thePDF.getPage(i).then(function (page) { var pageNum = page.pageNumber; // 使用与官方viewer.mjs相同的缩放因子 var PDF_TO_CSS_UNITS = 96.0 / 72.0; var scaledViewport = page.getViewport({ scale: self.scale * PDF_TO_CSS_UNITS }); var container = self.cache[pageNum + ""].container; if (container) { // 更新容器尺寸 // 更新CSS变量 container.style.setProperty('--scale-factor', scaledViewport.scale); // 重新渲染Canvas - 创建新的Canvas避免重复渲染错误 var canvas = container.querySelector('canvas'); if (canvas) { // 取消之前的渲染任务 if (self.cache[pageNum + ""].renderTask) { self.cache[pageNum + ""].renderTask.cancel(); } // 创建新的Canvas避免重复渲染 var newCanvas = document.createElement('canvas'); newCanvas.width = scaledViewport.width; newCanvas.height = scaledViewport.height; newCanvas.style.width = scaledViewport.width + 'px'; newCanvas.style.height = scaledViewport.height + 'px'; // 替换旧的Canvas canvas.parentNode.replaceChild(newCanvas, canvas); var enableHWA = self.options.enableHWA !== false; var renderObj = { canvasContext: newCanvas.getContext('2d', { alpha: false, willReadFrequently: !enableHWA }), viewport: scaledViewport }; // 存储渲染任务以便后续取消 self.cache[pageNum + ""].renderTask = page.render(renderObj); } // 重新渲染文本层 var textLayer = container.querySelector('.textLayer'); if (textLayer) { textLayer.innerHTML = ''; self.renderTextLayer(page, pageNum, container, scaledViewport); } } }); } } } self.trigger('zoom', { scale: self.scale }); return true; }, // 搜索功能API searchText: function (query) { var self = this; if (!self.thePDF || !query) { return false; } // 简单的文本搜索实现 self.currentSearchQuery = query; self.searchResults = []; // 遍历所有页面进行搜索 for (var i = 1; i <= self.totalNum; i++) { var pageContainer = self.cache[i + ""]; if (pageContainer && pageContainer.container) { var textLayer = pageContainer.container.querySelector('.textLayer'); if (textLayer) { var text = textLayer.textContent.toLowerCase(); var queryLower = query.toLowerCase(); if (text.includes(queryLower)) { self.searchResults.push(i); } } } } self.trigger('search', { query: query, results: self.searchResults, totalResults: self.searchResults.length }); return self.searchResults.length > 0; }, clearSearch: function () { var self = this; self.currentSearchQuery = null; self.searchResults = []; self.currentSearchIndex = 0; // 清除高亮 var textLayers = self.container.querySelectorAll('.textLayer'); textLayers.forEach(function (layer) { var highlights = layer.querySelectorAll('.highlight'); highlights.forEach(function (highlight) { highlight.classList.remove('highlight'); }); }); self.trigger('searchCleared'); }, findNext: function () { var self = this; if (!self.searchResults || self.searchResults.length === 0) { return false; } self.currentSearchIndex = (self.currentSearchIndex + 1) % self.searchResults.length; var pageNum = self.searchResults[self.currentSearchIndex]; self.goToPage(pageNum); self.trigger('findNext', { pageNumber: pageNum, index: self.currentSearchIndex, total: self.searchResults.length }); return true; }, findPrevious: function () { var self = this; if (!self.searchResults || self.searchResults.length === 0) { return false; } self.currentSearchIndex = self.currentSearchIndex === 0 ? self.searchResults.length - 1 : self.currentSearchIndex - 1; var pageNum = self.searchResults[self.currentSearchIndex]; self.goToPage(pageNum); self.trigger('findPrevious', { pageNumber: pageNum, index: self.currentSearchIndex, total: self.searchResults.length }); return true; }, // 打印功能API - 参考PDF.js官方实现 printPDF: function () { var self = this; if (!self.thePDF) { console.warn('No PDF loaded'); return false; } // 触发打印事件 self.trigger('print'); // 创建打印容器 var printContainer = document.createElement('div'); printContainer.id = 'pdfh5-print-container'; printContainer.style.position = 'absolute'; printContainer.style.left = '-9999px'; printContainer.style.top = '-9999px'; printContainer.style.width = '210mm'; // A4宽度 printContainer.style.height = '297mm'; // A4高度 printContainer.style.background = 'white'; printContainer.style.padding = '0'; printContainer.style.margin = '0'; // 添加打印样式 var printStyle = document.createElement('style'); printStyle.textContent = ` @media print { body * { visibility: hidden; } #pdfh5-print-container, #pdfh5-print-container * { visibility: visible; } #pdfh5-print-container { position: absolute; left: 0; top: 0; width: 100%; } } `; document.head.appendChild(printStyle); document.body.appendChild(printContainer); // 渲染所有页面到打印容器 self.renderPagesForPrint(printContainer).then(function () { // 延迟打印,确保页面渲染完成 setTimeout(function () { window.print(); // 清理打印容器 setTimeout(function () { if (printContainer.parentNode) { printContainer.parentNode.removeChild(printContainer); } if (printStyle.parentNode) { printStyle.parentNode.removeChild(printStyle); } }, 1000); }, 500); }).catch(function (error) { console.error('Print rendering failed:', error); // 清理打印容器 if (printContainer.parentNode) { printContainer.parentNode.removeChild(printContainer); } if (printStyle.parentNode) { printStyle.parentNode.removeChild(printStyle); } }); return true; }, // 为打印渲染页面 renderPagesForPrint: function (printContainer) { var self = this; var promises = []; // 为每一页创建打印内容 for (var i = 1; i <= self.totalNum; i++) { promises.push(self.renderPageForPrint(i, printContainer)); } return Promise.all(promises); }, // 渲染单页用于打印 renderPageForPrint: function (pageNum, printContainer) { var self = this; return self.thePDF.getPage(pageNum).then(function (page) { // 使用适合打印的缩放比例 var viewport = page.getViewport({ scale: 1.5 }); // 创建页面容器 var pageContainer = document.createElement('div'); pageContainer.style.width = viewport.width + 'px'; pageContainer.style.height = viewport.height + 'px'; pageContainer.style.margin = '0 auto 10px auto'; pageContainer.style.pageBreakAfter = 'always'; pageContainer.style.background = 'white'; pageContainer.style.boxShadow = '0 0 10px rgba(0,0,0,0.1)'; // 创建canvas var canvas = document.createElement('canvas'); // 打印时使用标准像素比 canvas.width = viewport.width; canvas.height = viewport.height; canvas.style.width = '100%'; canvas.style.height = '100%'; pageContainer.appendChild(canvas); printContainer.appendChild(pageContainer); // 渲染页面 var enableHWA = self.options.enableHWA !== false; var renderContext = { canvasContext: canvas.getContext('2d', { alpha: false, willReadFrequently: !enableHWA }), viewport: viewport }; return page.render(renderContext).promise; }); }, // 渐进式加载初始化 initProgressiveLoading: function (pdf, options) { var self = this; // 初始化缓存 for (var i = 1; i <= self.totalNum; i++) { self.cache[i + ""] = { page: null, loaded: false, container: null, scaledViewport: null, canvas: null, imgWidth: null }; } // 预计算所有页面尺寸,然后创建容器 self.preCalculateAllPageSizes(pdf, options).then(function () { // 使用预计算的尺寸创建页面容器占位符 for (var i = 1; i <= self.totalNum; i++) { self.createPageContainerWithPreCalculatedSize(i, options); } // 继续原有的分段加载逻辑 self.continueProgressiveLoading(pdf, options); }); }, // 分段加载模式下隐藏loadingBar hideLoadingBarForProgressiveLoading: function () { var self = this; // 设置进度条为100% if (self.options.loadingBar && self.progress) { self.progress.style.width = "100%"; } // 延迟隐藏loadingBar,确保用户能看到100%的进度 if (self.loadingBar) { setTimeout(function () { self.loadingBar.style.display = "none"; // 触发分段加载完成事件 var time = new Date().getTime(); self.endTime = time - self.initTime; // 触发完成事件 var arr1 = self.eventType["complete"]; if (arr1 && arr1 instanceof Array) { for (var i = 0; i < arr1.length; i++) { arr1[i] && arr1[i].call(self, "success", "pdf分段加载初始化完成", self.endTime); } } }, 300); } }, // 继续分段加载逻辑 continueProgressiveLoading: function (pdf, options) { var self = this; // 检查是否有goto配置 if (self.options.goto && self.options.goto > 0 && self.options.goto <= self.totalNum) { self.currentNum = self.options.goto; } // 分段加载模式下隐藏loadingBar self.hideLoadingBarForProgressiveLoading(); // 先加载前几页,优先加载goto页面 var initialPages = Math.min(self.maxMemoryPages, self.totalNum); var pagesToLoad = []; // 如果有goto配置,优先加载goto页面及其周围页面 if (self.options.goto && self.options.goto > 0) { var gotoPage = self.options.goto; pagesToLoad.push(gotoPage); // 加载goto页面周围的页面 for (var i = 1; i <= initialPages; i++) { if (i !== gotoPage && Math.abs(i - gotoPage) <= Math.floor(initialPages / 2)) { pagesToLoad.push(i); } } } else { // 没有goto配置,只加载第一页 pagesToLoad.push(1); } // 加载页面 pagesToLoad.forEach(function (pageNum) { self.loadPageProgressive(pageNum, options); }); // 设置滚动监听,动态加载页面 self.setupProgressiveScrollListener(options); // 初始化TouchManager - 手势缩放 if (self.options.zoomEnable) { self.initTouchManager(); } // 延迟检查内存使用情况,确保内存管理正常工作 setTimeout(function () { if (self.loadedPages.size > self.maxMemoryPages) { self.cleanupDistantPages(self.currentNum); } }, 1000); // 如果有goto配置,延迟滚动到指定页面 if (self.options.goto && self.options.goto > 1) { setTimeout(function () { self.scrollToPage(self.options.goto); }, 200); // 稍微延迟,确保页面已加载 } }, // 渐进式页面加载 loadPageProgressive: function (pageNum, options) { var self = this; // 检查是否已加载 if (self.loadedPages.has(pageNum)) { return Promise.resolve(); } // 限制同时加载的页面数量 if (self.loadingQueue.length >= 2) { return Promise.resolve(); } // 强制内存管理:如果超过最大页面数,清理最远的页面 if (self.loadedPages.size >= self.maxMemoryPages) { self.cleanupDistantPages(pageNum); } // 添加到加载队列 self.loadingQueue.push(pageNum); // 使用现有的renderPageLazy方法 return self.renderPageLazy(self.thePDF, pageNum, options).then(function () { // 标记为已加载 self.loadedPages.set(pageNum, { page: self.cache[pageNum + ""].page, loaded: true, loadTime: new Date().getTime(), container: self.cache[pageNum + ""].container }); // 从队列中移除 var index = self.loadingQueue.indexOf(pageNum); if (index > -1) { self.loadingQueue.splice(index, 1); } // 加载完成后再次检查内存使用情况 if (self.loadedPages.size > self.maxMemoryPages) { self.cleanupDistantPages(pageNum); } // 触发页面加载完成事件 self.trigger('pageLoaded', { pageNum: pageNum, memoryUsage: self.getMemoryUsage() }); }).catch(function (error) { console.error('分段加载 - 页面加载失败:', pageNum, error); }); }, // 设置渐进式滚动监听 setupProgressiveScrollListener: function (options) { var self = this; // 使用Intersection Observer监听页面可见性 if (self.intersectionObserver) { self.intersectionObserver.disconnect(); } self.intersectionObserver = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { var pageNum = parseInt(entry.target.getAttribute('data-page')); var intersectionRatio = entry.intersectionRatio; // 只有当页面真正可见时才加载(可见比例大于0.3) if (pageNum && !self.loadedPages.has(pageNum) && intersectionRatio > 0.3) { self.loadPageProgressive(pageNum, options); } } }); }, { rootMargin: '0px', // 不提前加载 threshold: [0.3, 0.5, 1.0] // 提高可见性阈值 }); // 监听所有页面容器 for (var i = 1; i <= self.totalNum; i++) { var container = self.cache[i + ""].container; if (container) { self.intersectionObserver.observe(container); } } // 添加滚动事件监听器,在滚动时自动清理内存 if (self.scrollListener) { self.viewerContainer.removeEventListener('scroll', self.scrollListener); } self.scrollListener = function () { // 延迟执行,避免频繁清理 clearTimeout(self.scrollTimeout); self.scrollTimeout = setTimeout(function () { if (self.loadedPages.size > self.maxMemoryPages) { self.forceCleanupMemory(); } }, 500); // 500ms延迟 }; self.viewerContainer.addEventListener('scroll', self.scrollListener); }, // 清理远距离页面 cleanupDistantPages: function (currentPage) { var self = this; var pagesToRemove = []; // 如果已加载页面数超过最大限制,清理最远的页面 if (self.loadedPages.size >= self.maxMemoryPages) { // 按距离排序,保留最近的页面 var sortedPages = Array.from(self.loadedPages.keys()).sort(function (a, b) { return Math.abs(a - currentPage) - Math.abs(b - currentPage); }); // 保留最近的maxMemoryPages个页面,清理其余的 var pagesToKeep = sortedPages.slice(0, self.maxMemoryPages); sortedPages.forEach(function (pageNum) { if (pagesToKeep.indexOf(pageNum) === -1) { pagesToRemove.push(pageNum); } }); } // 清理页面 pagesToRemove.forEach(function (pageNum) { self.cleanupPage(pageNum); }); }, // 清理单个页面 cleanupPage: function (pageNum) { var self = this; var container = self.cache[pageNum + ""].container; if (container) { // 只清理canvas和文本层,保留容器占位 var canvas = container.querySelector('canvas'); var textLayer = container.querySelector('.textLayer'); if (canvas) { // 清空canvas内容 var enableHWA = self.options.enableHWA !== false; var ctx = canvas.getContext('2d', { alpha: false, willReadFrequently: !enableHWA }); ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.width = 0; canvas.height = 0; canvas.remove(); } if (textLayer) { textLayer.innerHTML = ''; textLayer.remove(); } // 从缓存中移除 self.loadedPages.delete(pageNum); // 从加载状态中移除(如果存在) if (self.loadingPages) { self.loadingPages.delete(pageNum); } // 重置缓存状态 if (self.cache[pageNum + ""]) { self.cache[pageNum + ""].loaded = false; self.cache[pageNum + ""].canvas = null; self.cache[pageNum + ""].page = null; } // 触发页面清理事件 self.trigger('pageCleaned', { pageNum: pageNum, memoryUsage: self.getMemoryUsage() }); } }, // 获取内存使用情况 getMemoryUsage: function () { var self = this; var usage = { loadedPages: self.loadedPages.size, maxPages: self.maxMemoryPages, loadingQueue: self.loadingQueue.length, memoryUsage: self.memoryUsage }; return usage; }, // 强制清理内存 forceCleanupMemory: function () { var self = this; // 获取当前可见的页面 var visiblePages = []; for (var i = 1; i <= self.totalNum; i++) { var container = self.cache[i + ""].container; if (container) { var rect = container.getBoundingClientRect(); var viewerRect = self.viewerContainer.getBoundingClientRect(); // 检查页面是否在视口内(更严格的检查) var isVisible = rect.bottom > viewerRect.top && rect.top < viewerRect.bottom && rect.width > 0 && rect.height > 0; if (isVisible) { visiblePages.push(i); } } } // 如果已加载页面数超过限制,清理最远的页面 if (self.loadedPages.size > self.maxMemoryPages) { // 按距离当前可见页面中心的距离排序 var centerPage = visiblePages.length > 0 ? visiblePages[Math.floor(visiblePages.length / 2)] : self.currentNum; var sortedPages = Array.from(self.loadedPages.keys()).sort(function (a, b) { return Math.abs(a - centerPage) - Math.abs(b - centerPage); }); // 保留最近的maxMemoryPages个页面 var pagesToKeep = sortedPages.slice(0, self.maxMemoryPages); var pagesToRemove = []; sortedPages.forEach(function (pageNum) { if (pagesToKeep.indexOf(pageNum) === -1) { pagesToRemove.push(pageNum); } }); // 清理页面 pagesToRemove.forEach(function (pageNum) { self.cleanupPage(pageNum); }); } }, // 设置分段加载配置 setProgressiveLoading: function (enabled, options) { this.progressiveLoading = enabled; if (options) { if (options.chunkSize) this.chunkSize = options.chunkSize; if (options.maxMemoryPages) this.maxMemoryPages = options.maxMemoryPages; } return this; }, // 获取分段加载状态 getProgressiveLoadingStatus: function () { return { enabled: this.progressiveLoading, chunkSize: this.chunkSize, maxMemoryPages: this.maxMemoryPages, loadedPages: this.loadedPages.size, loadingQueue: this.loadingQueue.length, memoryUsage: this.getMemoryUsage() }; }, // 下载功能API downloadPDF: function (filename) { var self = this; if (!self.thePDF) { console.warn('No PDF loaded'); return Promise.reject('No PDF loaded'); } // 如果没有提供文件名,尝试从PDF URL中提取文件名 if (!filename) { if (self.options.pdfurl) { // 从URL中提取文件名 var urlParts = self.options.pdfurl.split('/'); var urlFilename = urlParts[urlParts.length - 1]; // 如果URL中有查询参数,需要去掉 if (urlFilename.includes('?')) { urlFilename = urlFilename.split('?')[0]; } // 如果提取到的文件名有效,使用它;否则使用默认名称 if (urlFilename && urlFilename.includes('.pdf')) { filename = urlFilename; } else { filename = 'document.pdf'; } } else { filename = 'document.pdf'; } } return self.getPDFWithAnnotations().then(function (pdfData) { var blob = new Blob([pdfData], { type: 'application/pdf' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); self.trigger('download', { url: url, filename: filename }); return { url: url, filename: filename }; }); }, getPDFWithAnnotations: function () { var self = this; return new Promise(function (resolve, reject) { if (!self.thePDF) { reject('No PDF loaded'); return; } // 检查是否有图章或墨迹注释需要保存 var stampAnnotations = self.editorAnnotations.filter(function (ann) { return ann.type === 'STAMP'; }); var inkAnnotations = self.editorAnnotations.filter(function (ann) { return ann.type === 'INK'; }); if (stampAnnotations.length > 0 || inkAnnotations.length > 0) { // 使用PDF.js官方的saveDocument方法 if (self.thePDF.saveDocument) { try { // 确保注释存储已修改 if (self.thePDF.annotationStorage) { self.thePDF.annotationStorage.setModified(true); } self.thePDF.saveDocument().then(function (pdfData) { resolve(pdfData); }).catch(function (error) { console.error('使用PDF.js saveDocument失败:', error.message); // 如果saveDocument失败,回退到原始PDF fetch(self.options.pdfurl) .then(function (response) { return response.arrayBuffer(); }) .then(function (data) { console.warn('注意:返回的PDF不包含注释'); resolve(data); }) .catch(reject); }); } catch (error) { console.error('保存PDF时发生错误:', error.message); // 回退到原始PDF if (self.options.pdfurl) { fetch(self.options.pdfurl) .then(function (response) { return response.arrayBuffer(); }) .then(function (data) { console.warn('注意:返回的PDF不包含注释'); resolve(data); }) .catch(reject); } else if (self.options.data) { // 如果使用data配置,直接使用原始数据 console.warn('注意:返回的PDF不包含注释'); resolve(self.options.data); } else { reject('无法获取PDF数据'); } } } else { // 如果没有saveDocument方法,回退到原始PDF if (self.options.pdfurl) { fetch(self.options.pdfurl) .then(function (response) { return response.arrayBuffer(); }) .then(function (data) { console.warn('注意:返回的PDF不包含注释'); resolve(data); }) .catch(reject); } else if (self.options.data) { // 如果使用data配置,直接使用原始数据 console.warn('注意:返回的PDF不包含注释'); resolve(self.options.data); } else { reject('无法获取PDF数据'); } } } else { // 没有注释,直接返回原始PDF数据 if (self.options.pdfurl) { fetch(self.options.pdfurl) .then(function (response) { return response.arrayBuffer(); }) .then(function (data) { resolve(data); }) .catch(reject); } else if (self.options.data) { // 如果使用data配置,直接使用原始数据 resolve(self.options.data); } else { reject('无法获取PDF数据'); } } }); }, // 使用PDF-lib库保存注释到PDF savePDFWithAnnotations: async function (annotations) { var self = this; // 检查PDF-lib是否可用 if (typeof PDFLib === 'undefined') { throw new Error('PDF-lib库未加载,请在HTML中引入:'); } // 1. 获取原始PDF数据 const response = await fetch(self.options.pdfurl); const pdfData = await response.arrayBuffer(); // 2. 加载PDF文档 const pdfDoc = await PDFLib.PDFDocument.load(pdfData); // 3. 遍历所有注释 for (const annotation of annotations) { const page = pdfDoc.getPage(annotation.pageNum - 1); const pageHeight = page.getHeight(); if (annotation.type === 'STAMP') { // 处理图章注释 if (annotation.imageData) { // 将base64图片转换为ArrayBuffer const imageDataUrl = annotation.imageData; const imageBytes = await fetch(imageDataUrl).then(r => r.arrayBuffer()); // 根据图片类型嵌入图片 let image; if (imageDataUrl.startsWith('data:image/png')) { image = await pdfDoc.embedPng(imageBytes); } else if (imageDataUrl.startsWith('data:image/jpeg') || imageDataUrl.startsWith('data:image/jpg')) { image = await pdfDoc.embedJpg(imageBytes); } else { console.warn('不支持的图片格式,跳过:', imageDataUrl.substring(0, 30)); continue; } // 计算PDF坐标(PDF坐标系原点在左下角) const pdfY = pageHeight - annotation.y - annotation.height; // 添加图章到页面 page.drawImage(image, { x: annotation.x, y: pdfY, width: annotation.width, height: annotation.height, }); } } else if (annotation.type === 'INK') { // 处理墨迹注释 if (annotation.path && annotation.path.length > 1) { // 构建SVG路径字符串 let pathData = `M ${annotation.path[0].x} ${pageHeight - annotation.path[0].y}`; for (let i = 1; i < annotation.path.length; i++) { const pdfY = pageHeight - annotation.path[i].y; pathData += ` L ${annotation.path[i].x} ${pdfY}`; } // 解析颜色 const color = annotation.color || '#000000'; const r = parseInt(color.substring(1, 3), 16) / 255; const g = parseInt(color.substring(3, 5), 16) / 255; const b = parseInt(color.substring(5, 7), 16) / 255; // 绘制墨迹路径 page.drawSvgPath(pathData, { borderColor: PDFLib.rgb(r, g, b), borderWidth: annotation.thickness || 1, borderOpacity: annotation.opacity || 1 }); } } } // 4. 保存PDF const pdfBytes = await pdfDoc.save(); return pdfBytes; }, // 获取当前状态 getStatus: function () { var self = this; return { version: self.version, totalPages: self.totalNum, currentPage: self.currentNum, scale: self.scale, loadedPages: self.loadedCount }; }, // 更新注释(当用户调整图章大小时) updateAnnotation: function (annotationId, updates) { var self = this; // 更新前端显示 var annotation = self.editorAnnotations.find(function (ann) { return ann.id === annotationId; }); if (annotation) { Object.assign(annotation, updates); // 更新PDF.js的annotationStorage if (self.thePDF && self.thePDF.annotationStorage) { var pdfAnnotation = self.thePDF.annotationStorage.getRawValue(annotationId); if (pdfAnnotation) { // 更新PDF注释数据 Object.assign(pdfAnnotation, updates); self.thePDF.annotationStorage.setValue(annotationId, pdfAnnotation); } } // 重新渲染注释 self.renderAnnotation(annotation); } }, // 删除注释 removeAnnotation: function (annotationId) { var self = this; // 从PDF.js的annotationStorage中删除 if (self.thePDF && self.thePDF.annotationStorage) { self.thePDF.annotationStorage.remove(annotationId); } // 从前端注释数组中删除 self.editorAnnotations = self.editorAnnotations.filter(function (ann) { return ann.id !== annotationId; }); // 从DOM中移除 var element = document.querySelector('[data-annotation-id="' + annotationId + '"]'); if (element) { element.remove(); } }, // 获取所有注释 getAllAnnotations: function () { var self = this; return self.editorAnnotations || []; }, // 获取PDF注释存储状态 getAnnotationStorageStatus: function () { var self = this; if (self.thePDF && self.thePDF.annotationStorage) { return { size: self.thePDF.annotationStorage.size, hasAnnotations: self.thePDF.annotationStorage.size > 0 }; } return { size: 0, hasAnnotations: false }; }, // 事件系统 // 编辑器功能API setEditorMode: function (mode) { var self = this; // 验证模式 var validModes = ['NONE', 'FREETEXT', 'INK', 'STAMP']; if (!validModes.includes(mode)) { return false; } // 更新模式 self.annotationEditorMode = mode; self.isEditing = (mode !== 'NONE'); // 触发事件 self.trigger('editorModeChanged', { mode: mode }); // 更新UI状态 self.updateEditorUI(); return true; }, getEditorMode: function () { return this.annotationEditorMode; }, setEditorParam: function (param, value) { var self = this; // 初始化编辑器参数对象 if (!self.editorParams) { self.editorParams = {}; } // 设置参数 self.editorParams[param] = value; self.trigger('editorParamChanged', { param: param, value: value }); return true; }, getEditorParam: function (param) { return this.editorParams[param] || null; }, updateEditorUI: function () { var self = this; // 更新所有页面容器的编辑器状态 for (var i = 1; i <= self.totalNum; i++) { var container = self.cache[i + ""].container; if (container) { // 移除之前的编辑器类 container.classList.remove('editor-freetext', 'editor-ink', 'editor-stamp'); // 添加新的编辑器类 if (self.isEditing) { container.classList.add('editor-' + self.annotationEditorMode.toLowerCase()); } // 更新鼠标样式 if (self.isEditing) { container.style.cursor = self.getEditorCursor(); } else { container.style.cursor = 'default'; } } } }, getEditorCursor: function () { switch (this.annotationEditorMode) { case 'FREETEXT': return 'text'; case 'INK': return 'crosshair'; case 'STAMP': return 'copy'; default: return 'default'; } }, // 添加注释 addAnnotation: function (annotation) { var self = this; // 生成唯一ID annotation.id = 'annotation_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); annotation.timestamp = new Date().toISOString(); // 添加到注释数组 self.editorAnnotations.push(annotation); // 触发事件 self.trigger('annotationAdded', { annotation: annotation }); return annotation.id; }, // 获取所有注释 getAnnotations: function () { return this.editorAnnotations; }, // 清除所有注释 clearAnnotations: function () { this.editorAnnotations = []; this.trigger('annotationsCleared'); }, // 初始化编辑器事件监听 initEditorEvents: function () { var self = this; // 监听页面点击事件 self.viewerContainer.addEventListener('click', function (e) { if (!self.isEditing) return; // 阻止事件冒泡,避免触发其他点击事件 e.stopPropagation(); var target = e.target; var pageContainer = target.closest('.pageContainer'); if (pageContainer) { var pageNum = parseInt(pageContainer.getAttribute('name').split('=')[1]); self.handleEditorClick(e, pageNum); } }); // 墨迹功能已移除 // 监听鼠标按下事件 self.viewerContainer.addEventListener('mousedown', function (e) { if (!self.isEditing) return; var target = e.target; var pageContainer = target.closest('.pageContainer'); if (pageContainer) { self.handleEditorMouseDown(e, pageContainer); } }); // 监听鼠标释放事件 self.viewerContainer.addEventListener('mouseup', function (e) { if (!self.isEditing) return; var target = e.target; var pageContainer = target.closest('.pageContainer'); if (pageContainer) { self.handleEditorMouseUp(e, pageContainer); } }); }, // 处理编辑器点击 handleEditorClick: function (e, pageNum) { var self = this; // 检查是否在编辑模式 if (!self.isEditing || self.annotationEditorMode === 'NONE') { return; } // 检查是否点击了现有注释 var existingAnnotation = self.findAnnotationAtPosition(e, pageNum); if (existingAnnotation) { self.selectAnnotation(existingAnnotation); return; } // 注释功能已移除 }, // 渲染注释到页面 renderAnnotation: function (annotation) { var self = this; // 获取页面容器 var pageContainer = self.cache[annotation.pageNum + ""].container; if (!pageContainer) { return; } // 创建图章显示元素 var stampElement = document.createElement('div'); stampElement.className = 'stamp-annotation-display'; stampElement.style.position = 'absolute'; stampElement.style.left = annotation.x + 'px'; stampElement.style.top = annotation.y + 'px'; stampElement.style.width = annotation.width + 'px'; stampElement.style.height = annotation.height + 'px'; stampElement.style.border = '2px solid #007bff'; stampElement.style.cursor = 'move'; stampElement.style.zIndex = '1000'; stampElement.setAttribute('data-annotation-id', annotation.id); // 创建图片元素 var imgElement = document.createElement('img'); imgElement.src = annotation.image; imgElement.style.width = '100%'; imgElement.style.height = '100%'; imgElement.style.objectFit = 'contain'; imgElement.draggable = false; stampElement.appendChild(imgElement); pageContainer.appendChild(stampElement); // 添加点击事件监听器 stampElement.addEventListener('click', function (e) { e.stopPropagation(); self.selectAnnotation(annotation); }); }, // 选择注释 selectAnnotation: function (annotation) { var self = this; // 移除之前选中的注释 var existingSelected = document.querySelector('.stamp-annotation-display.selected'); if (existingSelected) { existingSelected.classList.remove('selected'); } // 选中当前注释 var stampElement = document.querySelector('[data-annotation-id="' + annotation.id + '"]'); if (stampElement) { stampElement.classList.add('selected'); stampElement.style.border = '2px solid #ff0000'; // 创建控制点 self.createResizeHandles(stampElement, annotation); } }, // 创建调整大小的控制点 createResizeHandles: function (element, annotation) { var self = this; // 移除之前的控制点 var existingHandles = element.querySelectorAll('.resize-handle'); existingHandles.forEach(function (handle) { handle.remove(); }); // 创建控制点 var handles = ['nw', 'ne', 'sw', 'se', 'n', 's', 'e', 'w']; handles.forEach(function (handle) { var handleElement = document.createElement('div'); handleElement.className = 'resize-handle resize-handle-' + handle; handleElement.style.position = 'absolute'; handleElement.style.width = '8px'; handleElement.style.height = '8px'; handleElement.style.backgroundColor = '#007bff'; handleElement.style.border = '1px solid #fff'; handleElement.style.cursor = self.getResizeCursor(handle); handleElement.style.zIndex = '1001'; // 设置位置 var position = self.getHandlePosition(handle, element.offsetWidth, element.offsetHeight); handleElement.style.left = position.x + 'px'; handleElement.style.top = position.y + 'px'; element.appendChild(handleElement); }); }, // 获取调整大小的光标 getResizeCursor: function (handle) { var cursors = { 'nw': 'nw-resize', 'ne': 'ne-resize', 'sw': 'sw-resize', 'se': 'se-resize', 'n': 'n-resize', 's': 's-resize', 'e': 'e-resize', 'w': 'w-resize' }; return cursors[handle] || 'default'; }, // 获取控制点位置 getHandlePosition: function (handle, width, height) { var positions = { 'nw': { x: -4, y: -4 }, 'ne': { x: width - 4, y: -4 }, 'sw': { x: -4, y: height - 4 }, 'se': { x: width - 4, y: height - 4 }, 'n': { x: width / 2 - 4, y: -4 }, 's': { x: width / 2 - 4, y: height - 4 }, 'e': { x: width - 4, y: height / 2 - 4 }, 'w': { x: -4, y: height / 2 - 4 } }; return positions[handle] || { x: 0, y: 0 }; }, // 处理编辑器鼠标按下 handleEditorMouseDown: function (e, pageContainer) { var self = this; // 墨迹功能已移除 }, // 处理编辑器鼠标释放 handleEditorMouseUp: async function (e, pageContainer) { var self = this; // 墨迹功能已移除 }, // 查找指定位置的注释 findAnnotationAtPosition: function (e, pageNum) { var self = this; var rect = e.target.getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; // 查找该页面的所有注释 for (var i = 0; i < self.editorAnnotations.length; i++) { var annotation = self.editorAnnotations[i]; if (annotation.pageNum === pageNum) { // 检查点击位置是否在注释范围内 if (self.isPointInAnnotation(x, y, annotation)) { return annotation; } } } return null; }, // 检查点是否在注释范围内 isPointInAnnotation: function (x, y, annotation) { switch (annotation.type) { case 'FREETEXT': // 文本注释:检查是否在文本区域内 return x >= annotation.x && x <= annotation.x + annotation.width && y >= annotation.y && y <= annotation.y + annotation.height; case 'STAMP': // 图章注释:检查是否在图片区域内 return x >= annotation.x && x <= annotation.x + annotation.width && y >= annotation.y && y <= annotation.y + annotation.height; case 'INK': // 墨迹注释:检查是否在路径区域内 return this.isPointInInkPath(x, y, annotation); default: return false; } }, // 检查点是否在墨迹路径内 isPointInInkPath: function (x, y, annotation) { // 简化的墨迹路径检测 // 实际实现中可以使用更复杂的路径算法 if (!annotation.path || annotation.path.length === 0) return false; var tolerance = 10; // 容差范围 for (var i = 0; i < annotation.path.length; i++) { var point = annotation.path[i]; var distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2)); if (distance <= tolerance) { return true; } } return false; }, // 选择注释 selectAnnotation: function (annotation) { var self = this; // 取消之前的选择 self.deselectAllAnnotations(); // 设置当前选择的注释 self.selectedAnnotation = annotation; // 触发选择事件 self.trigger('annotationSelected', { annotation: annotation }); return annotation; }, // 取消选择所有注释 deselectAllAnnotations: function () { var self = this; if (self.selectedAnnotation) { var prevAnnotation = self.selectedAnnotation; self.selectedAnnotation = null; // 触发取消选择事件 self.trigger('annotationDeselected', { annotation: prevAnnotation }); } }, // 获取当前选择的注释 getSelectedAnnotation: function () { return this.selectedAnnotation; }, // 移动注释 moveAnnotation: function (annotationId, newX, newY) { var self = this; var index = self.editorAnnotations.findIndex(function (ann) { return ann.id === annotationId; }); if (index !== -1) { var annotation = self.editorAnnotations[index]; var oldX = annotation.x; var oldY = annotation.y; // 更新位置 annotation.x = newX; annotation.y = newY; // 触发移动事件 self.trigger('annotationMoved', { annotation: annotation, oldPosition: { x: oldX, y: oldY }, newPosition: { x: newX, y: newY } }); return true; } return false; }, // 调整注释大小 resizeAnnotation: function (annotationId, newWidth, newHeight) { var self = this; var index = self.editorAnnotations.findIndex(function (ann) { return ann.id === annotationId; }); if (index !== -1) { var annotation = self.editorAnnotations[index]; var oldWidth = annotation.width; var oldHeight = annotation.height; // 更新尺寸 annotation.width = newWidth; annotation.height = newHeight; // 触发调整大小事件 self.trigger('annotationResized', { annotation: annotation, oldSize: { width: oldWidth, height: oldHeight }, newSize: { width: newWidth, height: newHeight } }); return true; } return false; }, // 获取指定页面的所有注释 getAnnotationsByPage: function (pageNum) { return this.editorAnnotations.filter(function (annotation) { return annotation.pageNum === pageNum; }); }, // 获取指定类型的注释 getAnnotationsByType: function (type) { return this.editorAnnotations.filter(function (annotation) { return annotation.type === type; }); }, // 导出注释数据 exportAnnotations: function () { return { annotations: this.editorAnnotations.slice(), exportTime: new Date().toISOString(), version: this.version }; }, // 导入注释数据 importAnnotations: function (data) { var self = this; if (data && data.annotations && Array.isArray(data.annotations)) { // 清除现有注释 self.clearAnnotations(); // 导入新注释 self.editorAnnotations = data.annotations.slice(); // 触发导入事件 self.trigger('annotationsImported', { count: self.editorAnnotations.length, importTime: data.exportTime }); return true; } return false; }, // 初始化沙箱管理器 initSandbox: function () { var self = this; if (!self.sandboxEnabled || !self.pdfjsLib || !self.pdfjsLib.SandboxManager) { return; } try { // 创建沙箱管理器实例 self.sandboxManager = new self.pdfjsLib.SandboxManager({ // 沙箱配置 allowScripts: false, // 禁止JavaScript执行 allowForms: true, // 允许表单交互 allowPopups: false, // 禁止弹窗 allowSameOrigin: true, // 允许同源访问 // 安全策略 sandbox: 'allow-same-origin allow-scripts', referrerPolicy: 'strict-origin-when-cross-origin', // 容器配置 container: self.viewer, // 事件处理 onError: function (error) { console.warn('Sandbox error:', error); }, onSecurityViolation: function (violation) { console.warn('Security violation blocked:', violation); } }); console.info('PDF.js sandbox initialized successfully'); } catch (error) { console.warn('Failed to initialize sandbox:', error); self.sandboxEnabled = false; } }, // 启用/禁用沙箱 setSandboxEnabled: function (enabled) { this.sandboxEnabled = enabled; if (enabled && !this.sandboxManager) { this.initSandbox(); } else if (!enabled && this.sandboxManager) { this.destroySandbox(); } }, // 销毁沙箱 destroySandbox: function () { if (this.sandboxManager) { try { this.sandboxManager.destroy(); this.sandboxManager = null; } catch (error) { console.warn('Error destroying sandbox:', error); } } }, // 获取沙箱状态 getSandboxStatus: function () { return { enabled: this.sandboxEnabled, initialized: !!this.sandboxManager, active: this.sandboxManager ? this.sandboxManager.isActive() : false }; }, // 处理密码错误 handlePasswordError: function (error) { var self = this; // 如果正在验证密码,显示错误信息 if (self.passwordValidating) { self.showPasswordError('密码错误,请重新输入'); self.passwordValidating = false; // 重置标志 } else if (self.passwordPrompt && self.passwordPrompt.parentNode) { // 如果密码框已经存在,显示错误信息 self.showPasswordError('密码错误,请重新输入'); } else { // 如果没有密码框,显示密码输入框 self.showPasswordPrompt(); } }, // 显示密码输入框 showPasswordPrompt: function () { var self = this; // 如果已有密码框,先移除 if (self.passwordPrompt) { self.hidePasswordPrompt(); } // 使用原生DOM创建方式,避免innerHTML解析问题 self.passwordPrompt = document.createElement('div'); self.passwordPrompt.className = 'pdfh5-password-prompt'; self.passwordPrompt.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box; `; // 创建内容容器 var contentDiv = document.createElement('div'); contentDiv.style.cssText = ` background: white; padding: 24px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.12); width: 100%; max-width: 400px; text-align: center; box-sizing: border-box; `; // 移动端适配 if (window.innerWidth <= 768) { contentDiv.style.cssText += ` padding: 20px; margin: 0 8px; border-radius: 8px; `; } // 创建标题 var title = document.createElement('div'); title.textContent = '提示'; title.style.cssText = 'margin: 0 0 16px 0; color: #333; font-size: 18px; font-weight: 600;'; contentDiv.appendChild(title); // 创建错误提示信息(初始隐藏) var errorMessage = document.createElement('div'); errorMessage.id = 'pdfh5-password-error'; errorMessage.textContent = '密码错误,请重新输入'; errorMessage.style.cssText = ` margin: 0 0 16px 0; color: #dc3545; font-size: 14px; display: none; text-align: center; `; contentDiv.appendChild(errorMessage); // 创建密码输入框 var passwordInput = document.createElement('input'); passwordInput.type = 'password'; passwordInput.id = 'pdfh5-password-input'; passwordInput.placeholder = '请输入PDF文件的密码'; passwordInput.style.cssText = ` width: 100%; padding: 12px 16px; border: 2px solid #e1e5e9; border-radius: 8px; margin-bottom: 24px; font-size: 16px; box-sizing: border-box; outline: none; transition: border-color 0.3s ease; `; // 移动端输入框适配 if (window.innerWidth <= 768) { passwordInput.style.cssText += ` padding: 14px 16px; font-size: 16px; margin-bottom: 20px; `; } contentDiv.appendChild(passwordInput); // 创建按钮容器 var buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end;'; // 移动端按钮布局适配 if (window.innerWidth <= 768) { buttonContainer.style.cssText = 'display: flex; gap: 8px; justify-content: center; flex-direction: column;'; } // 创建确定按钮 var submitBtn = document.createElement('button'); submitBtn.id = 'pdfh5-password-submit'; submitBtn.textContent = '确定'; submitBtn.style.cssText = ` background: #007bff; color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease; min-width: 80px; `; // 移动端按钮适配 if (window.innerWidth <= 768) { submitBtn.style.cssText += ` padding: 14px 24px; font-size: 16px; width: 100%; min-width: auto; `; } buttonContainer.appendChild(submitBtn); contentDiv.appendChild(buttonContainer); self.passwordPrompt.appendChild(contentDiv); document.body.appendChild(self.passwordPrompt); // 确保DOM元素已添加到页面 if (!self.passwordPrompt.parentNode) { console.error('Failed to add password prompt to DOM'); return; } // 绑定事件 - 使用setTimeout确保DOM完全渲染 setTimeout(function () { // 重新获取元素引用,确保它们存在 var passwordInputEl = document.getElementById('pdfh5-password-input'); var submitBtnEl = document.getElementById('pdfh5-password-submit'); if (!passwordInputEl || !submitBtnEl) { console.error('Password prompt elements not found after creation'); return; } // 自动聚焦 passwordInputEl.focus(); // 输入框焦点效果 passwordInputEl.addEventListener('focus', function () { this.style.borderColor = '#007bff'; }); passwordInputEl.addEventListener('blur', function () { this.style.borderColor = '#e1e5e9'; }); // 按钮悬停效果 submitBtnEl.addEventListener('mouseenter', function () { this.style.background = '#0056b3'; this.style.transform = 'translateY(-1px)'; }); submitBtnEl.addEventListener('mouseleave', function () { this.style.background = '#007bff'; this.style.transform = 'translateY(0)'; }); // 回车提交 passwordInputEl.addEventListener('keypress', function (e) { if (e.key === 'Enter') { self.submitPassword(); } }); // 提交按钮 submitBtnEl.addEventListener('click', function () { self.submitPassword(); }); // 监听窗口大小变化,重新适配弹窗 var resizeHandler = function () { if (self.passwordPrompt && self.passwordPrompt.parentNode) { // 重新应用移动端样式 var isMobile = window.innerWidth <= 768; var contentDiv = self.passwordPrompt.querySelector('div'); var title = contentDiv.querySelector('h3'); var passwordInput = document.getElementById('pdfh5-password-input'); var buttonContainer = contentDiv.querySelector('div:last-child'); var submitBtn = document.getElementById('pdfh5-password-submit'); if (contentDiv) { if (isMobile) { contentDiv.style.cssText = ` background: white; padding: 20px; border-radius: 8px; box-shadow: 0 8px 32px rgba(0,0,0,0.12); width: 100%; max-width: 400px; text-align: center; box-sizing: border-box; margin: 0 8px; `; } else { contentDiv.style.cssText = ` background: white; padding: 24px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.12); width: 100%; max-width: 400px; text-align: center; box-sizing: border-box; `; } } if (title) { if (isMobile) { title.style.cssText = 'margin: 0 0 12px 0; color: #333; font-size: 16px; font-weight: 600;'; } else { title.style.cssText = 'margin: 0 0 16px 0; color: #333; font-size: 18px; font-weight: 600;'; } } if (passwordInput) { if (isMobile) { passwordInput.style.cssText = ` width: 100%; padding: 14px 16px; border: 2px solid #e1e5e9; border-radius: 8px; margin-bottom: 20px; font-size: 16px; box-sizing: border-box; outline: none; transition: border-color 0.3s ease; `; } else { passwordInput.style.cssText = ` width: 100%; padding: 12px 16px; border: 2px solid #e1e5e9; border-radius: 8px; margin-bottom: 24px; font-size: 16px; box-sizing: border-box; outline: none; transition: border-color 0.3s ease; `; } } if (buttonContainer) { if (isMobile) { buttonContainer.style.cssText = 'display: flex; gap: 8px; justify-content: flex-end; flex-direction: column;'; } else { buttonContainer.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end;'; } } if (submitBtn) { if (isMobile) { submitBtn.style.cssText = ` background: #007bff; color: white; border: none; padding: 14px 24px; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 500; transition: all 0.2s ease; width: 100%; `; } else { submitBtn.style.cssText = ` background: #007bff; color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease; min-width: 80px; `; } } } }; // 添加窗口大小变化监听 window.addEventListener('resize', resizeHandler); // 在密码框关闭时移除监听器 var originalHidePasswordPrompt = self.hidePasswordPrompt; self.hidePasswordPrompt = function () { window.removeEventListener('resize', resizeHandler); originalHidePasswordPrompt.call(self); }; }, 100); }, // 提交密码 submitPassword: function () { var self = this; var passwordInput = document.getElementById('pdfh5-password-input'); if (!passwordInput) { console.error('Password input not found'); return; } var password = passwordInput.value.trim(); if (!password) { self.showPasswordError('请输入密码'); return; } // 隐藏错误信息 self.hidePasswordError(); // 设置一个标志,表示正在验证密码 self.passwordValidating = true; // 重新加载PDF,使用新密码 self.loadPDFWithPassword(password); }, // 显示密码错误信息 showPasswordError: function (message) { var self = this; var errorElement = document.getElementById('pdfh5-password-error'); if (errorElement) { errorElement.textContent = message || '密码错误,请重新输入'; errorElement.style.display = 'block'; // 清空密码输入框 var passwordInput = document.getElementById('pdfh5-password-input'); if (passwordInput) { passwordInput.value = ''; passwordInput.focus(); } } }, // 隐藏密码错误信息 hidePasswordError: function () { var self = this; var errorElement = document.getElementById('pdfh5-password-error'); if (errorElement) { errorElement.style.display = 'none'; } }, // 隐藏密码输入框 hidePasswordPrompt: function () { var self = this; if (self.passwordPrompt && self.passwordPrompt.parentNode) { self.passwordPrompt.parentNode.removeChild(self.passwordPrompt); self.passwordPrompt = null; } }, // 使用密码重新加载PDF loadPDFWithPassword: function (password) { var self = this; // 更新选项中的密码 self.options.password = password; // 重新加载PDF self.loadPDF(); }, // 获取密码配置状态 getPasswordConfig: function () { return { hasPassword: !!this.options.password }; }, destroy: function (callback) { // 销毁沙箱 this.destroySandbox(); // 清理密码相关资源 this.hidePasswordPrompt(); // 销毁PinchZoom if (this.pinchZoom) { this.pinchZoom.destroy(); this.pinchZoom = null; } // 清理窗口大小变化监听器 if (this.resizeHandler) { window.removeEventListener('resize', this.resizeHandler); this.resizeHandler = null; } if (this.thePDF && this.thePDF.destroy) { this.thePDF.destroy(); this.thePDF = null; } if (this.viewerContainer) { this.viewerContainer.parentNode.removeChild(this.viewerContainer); this.viewerContainer = null; } if (this.container) { this.container.innerHTML = ""; } callback && callback.call(this); } }; return Pdfh5; });