diff --git a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeViewer.razor b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeViewer.razor index 6ffb64b4..dad7a54e 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeViewer.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/EnvelopeViewer.razor @@ -628,8 +628,8 @@ const int MaxThumbnailWidth = 400; _currentZoom = (int)(scale * 100); await InvokeAsync(StateHasChanged); - // Re-render signature buttons when zoom changes - await Task.Delay(100); + // Small delay for canvas render to complete (reduced from 100ms to 10ms) + await Task.Delay(10); await RenderSignatureButtonsAsync(); } @@ -652,6 +652,9 @@ const int MaxThumbnailWidth = 400; await JSRuntime.InvokeVoidAsync("pdfViewer.zoomIn"); var scale = await JSRuntime.InvokeAsync("pdfViewer.getScale"); _currentZoom = (int)(scale * 100); + + // Update signature overlay positions after zoom + await RenderSignatureButtonsAsync(); } async Task ZoomOut() { @@ -659,6 +662,9 @@ const int MaxThumbnailWidth = 400; await JSRuntime.InvokeVoidAsync("pdfViewer.zoomOut"); var scale = await JSRuntime.InvokeAsync("pdfViewer.getScale"); _currentZoom = (int)(scale * 100); + + // Update signature overlay positions after zoom + await RenderSignatureButtonsAsync(); } async Task SetZoom(int percentage) { @@ -670,6 +676,9 @@ const int MaxThumbnailWidth = 400; async Task OnZoomSliderChanged(ChangeEventArgs e) { if (int.TryParse(e.Value?.ToString(), out var zoom)) { await SetZoom(zoom); + + // Update signature overlay positions after zoom + await RenderSignatureButtonsAsync(); } } diff --git a/EnvelopeGenerator.ReceiverUI/wwwroot/js/pdf-viewer.js b/EnvelopeGenerator.ReceiverUI/wwwroot/js/pdf-viewer.js index f6284233..4a377dec 100644 --- a/EnvelopeGenerator.ReceiverUI/wwwroot/js/pdf-viewer.js +++ b/EnvelopeGenerator.ReceiverUI/wwwroot/js/pdf-viewer.js @@ -309,16 +309,16 @@ window.pdfViewer = { return true; }, - zoomIn() { + async zoomIn() { const step = this.qualityOptions.zoomStepPercentage / 100; this.scale = Math.min(this.scale + step, 3.0); - this.queueRenderPage(this.pageNum); + await this.renderPage(this.pageNum); }, - zoomOut() { + async zoomOut() { const step = this.qualityOptions.zoomStepPercentage / 100; this.scale = Math.max(this.scale - step, 0.5); - this.queueRenderPage(this.pageNum); + await this.renderPage(this.pageNum); }, setScale(scale) { @@ -480,7 +480,8 @@ window.pdfViewer = { // Signature button functionality signatureButtons: [], - appliedSignatures: [], // Track which signatures have been applied + appliedSignatures: [], // Track which signatures have been applied (ID list) + appliedSignatureElements: [], // ✅ NEW: Track applied signature DOM elements _lastViewedSignatureId: null, // Track last viewed signature for navigation /** @@ -765,12 +766,28 @@ window.pdfViewer = { signatureLayer.style.width = `${viewport.width / dpr}px`; signatureLayer.style.height = `${viewport.height / dpr}px`; + // Update applied signature coordinates for current zoom level + this.updateAppliedSignaturePositions(signatureLayer, currentPageNum); + // Create button for each UNSIGNED signature pageSignatures.forEach(sig => { // Coordinates are in PDF POINTS - convert to display pixels const xPx = (sig.x * this.scale); const yPx = (sig.y * this.scale); + // ✅ FIXED: Scale button size proportionally with zoom + const baseScale = 1.5; // Reference scale (initial load) + const scaleFactor = this.scale / baseScale; + const baseWidth = 150; + const baseHeight = 60; + const baseFontSize = 18; + const baseIconSize = 24; + + const scaledWidth = baseWidth * scaleFactor; + const scaledHeight = baseHeight * scaleFactor; + const scaledFontSize = Math.max(baseFontSize * scaleFactor, 10); // Min 10px + const scaledIconSize = baseIconSize * scaleFactor; + // Create button element const button = document.createElement('button'); button.className = 'signature-button'; @@ -780,8 +797,8 @@ window.pdfViewer = { button.style.position = 'absolute'; button.style.left = `${xPx}px`; button.style.top = `${yPx}px`; - button.style.width = '150px'; - button.style.height = '60px'; + button.style.width = `${scaledWidth}px`; // ✅ Scaled + button.style.height = `${scaledHeight}px`; // ✅ Scaled button.style.backgroundColor = '#4F46E5'; button.style.color = 'white'; button.style.border = 'none'; @@ -801,14 +818,14 @@ window.pdfViewer = { // Add text const textDiv = document.createElement('div'); textDiv.textContent = 'Unterschreiben'; - textDiv.style.fontSize = '18px'; + textDiv.style.fontSize = `${scaledFontSize}px`; // ✅ Scaled textDiv.style.fontWeight = '700'; // Add SVG icon const svgNS = 'http://www.w3.org/2000/svg'; const svg = document.createElementNS(svgNS, 'svg'); - svg.setAttribute('width', '24'); - svg.setAttribute('height', '24'); + svg.setAttribute('width', scaledIconSize); // ✅ Scaled + svg.setAttribute('height', scaledIconSize); // ✅ Scaled svg.setAttribute('viewBox', '0 8 32 36'); svg.setAttribute('fill', 'none'); svg.style.filter = 'drop-shadow(0 1px 2px rgba(0,0,0,0.2))'; @@ -851,6 +868,81 @@ window.pdfViewer = { } }, + /** + * Scales an applied signature container based on current zoom level + * @param {HTMLElement} container - Applied signature container + * @param {number} currentScale - Current PDF zoom scale + */ + scaleAppliedSignature(container, currentScale) { + const baseScale = 1.5; // Reference scale (initial load) + const scaleFactor = currentScale / baseScale; + + // Scale width + const baseWidth = parseInt(container.getAttribute('data-base-width') || 230); + container.style.width = `${baseWidth * scaleFactor}px`; + + // Scale padding + const basePadding = parseInt(container.getAttribute('data-base-padding') || 12); + container.style.padding = `${basePadding * scaleFactor}px`; + + // Scale border radius (subtle detail) + const baseBorderRadius = parseInt(container.getAttribute('data-base-border-radius') || 6); + container.style.borderRadius = `${baseBorderRadius * scaleFactor}px`; + + // Scale font size (with min 6px for readability) + const infoContainer = container.querySelector('.signature-info-text'); + if (infoContainer) { + const baseFontSize = parseInt(infoContainer.getAttribute('data-base-font-size') || 9); + const scaledFontSize = Math.max(baseFontSize * scaleFactor, 6); + infoContainer.style.fontSize = `${scaledFontSize}px`; + } + + // Scale image max height + const baseImgHeight = parseInt(container.getAttribute('data-base-img-height') || 70); + const img = container.querySelector('img'); + if (img) { + img.style.maxHeight = `${baseImgHeight * scaleFactor}px`; + } + + // Scale separator line margin + const baseSeparatorMargin = 6; + const separators = container.querySelectorAll('div[style*="border-top"]'); + separators.forEach(sep => { + sep.style.marginTop = `${baseSeparatorMargin * scaleFactor}px`; + sep.style.marginBottom = `${(baseSeparatorMargin + 2) * scaleFactor}px`; + }); + }, + + /** + * Updates applied signature positions based on current zoom level + * @param {HTMLElement} signatureLayer - Signature layer container + * @param {number} currentPageNum - Current page number + */ + updateAppliedSignaturePositions(signatureLayer, currentPageNum) { + if (!signatureLayer || !this._allSignatures) return; + + const appliedContainers = signatureLayer.querySelectorAll('.applied-signature'); + appliedContainers.forEach(container => { + const signatureId = parseInt(container.getAttribute('data-signature-id')); + const signature = this._allSignatures.find(s => s.id === signatureId); + + if (signature) { + // ✅ Position calculation (same as renderSignatureButtons) + const xPx = signature.x * this.scale; + const yPx = signature.y * this.scale; + + container.style.left = `${xPx}px`; + container.style.top = `${yPx}px`; + + // ✅ FIXED: Apply comprehensive scaling using helper method + this.scaleAppliedSignature(container, this.scale); + + // Show/hide based on current page + container.style.display = (signature.page === currentPageNum) ? '' : 'none'; + } + }); + }, + /** * Clears all signature buttons from the canvas. * Also hides applied signatures that don't belong to current page. @@ -864,24 +956,25 @@ window.pdfViewer = { }); this.signatureButtons = []; - // Hide/show applied signatures based on current page - const signatureLayer = document.getElementById('pdf-signature-layer'); - if (signatureLayer) { - const appliedContainers = signatureLayer.querySelectorAll('.applied-signature'); - appliedContainers.forEach(container => { - const signatureId = parseInt(container.getAttribute('data-signature-id')); - const signature = this._allSignatures?.find(s => s.id === signatureId); + // ✅ FIXED: Update applied signatures (position + scaling) + this.appliedSignatureElements.forEach(container => { + const signatureId = parseInt(container.getAttribute('data-signature-id')); + const signature = this._allSignatures?.find(s => s.id === signatureId); + + if (signature) { + // Update position + const xPx = signature.x * this.scale; + const yPx = signature.y * this.scale; + container.style.left = `${xPx}px`; + container.style.top = `${yPx}px`; - if (signature) { - // Show only if on current page, hide otherwise - if (signature.page === this.pageNum) { - container.style.display = ''; // Show - } else { - container.style.display = 'none'; // Hide - } - } - }); - } + // Update scaling + this.scaleAppliedSignature(container, this.scale); + + // Show/hide based on current page + container.style.display = (signature.page === this.pageNum) ? '' : 'none'; + } + }); }, /** @@ -938,6 +1031,14 @@ window.pdfViewer = { const signatureContainer = document.createElement('div'); signatureContainer.className = 'applied-signature'; signatureContainer.setAttribute('data-signature-id', signatureId); + + // ✅ FIXED: Store base values for scaling + signatureContainer.setAttribute('data-base-width', '230'); + signatureContainer.setAttribute('data-base-padding', '12'); + signatureContainer.setAttribute('data-base-font-size', '9'); + signatureContainer.setAttribute('data-base-img-height', '70'); + signatureContainer.setAttribute('data-base-border-radius', '6'); + signatureContainer.style.position = 'absolute'; signatureContainer.style.left = left; signatureContainer.style.top = top; @@ -969,6 +1070,8 @@ window.pdfViewer = { // Text information container const infoContainer = document.createElement('div'); + infoContainer.className = 'signature-info-text'; // ✅ Class ekle (querySelector için) + infoContainer.setAttribute('data-base-font-size', '9'); // ✅ Base font size sakla infoContainer.style.fontSize = '9px'; infoContainer.style.lineHeight = '1.4'; infoContainer.style.color = '#495057'; @@ -1001,6 +1104,12 @@ window.pdfViewer = { // Add to signature layer signatureLayer.appendChild(signatureContainer); + + // ✅ FIXED: Track applied signature element for zoom updates + this.appliedSignatureElements.push(signatureContainer); + + // ✅ FIXED: Apply initial scaling based on current zoom + this.scaleAppliedSignature(signatureContainer, this.scale); console.log(`Signature #${signatureId} applied successfully`);