Textfeld für Unterschriftoverlay und gitignore

This commit is contained in:
2026-01-16 14:14:38 +00:00
parent 2e96470a60
commit ba767a374a
5 changed files with 269 additions and 3 deletions

126
.gitignore vendored Normal file
View File

@@ -0,0 +1,126 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# OS files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
*~
# IDE files
.idea/
.vscode/
*.swp
*.swo
*.swn
.project
.classpath
.settings/
# Logs
logs
*.log
# Temporary files
tmp/
temp/
*.tmp

View File

@@ -312,6 +312,21 @@ body {
pointer-events: none;
}
.signature-overlay .text-overlay-content {
position: absolute;
top: 5px;
left: 5px;
font-size: 14px;
color: #333;
background: rgba(255, 255, 255, 0.9);
padding: 5px 10px;
border-radius: 4px;
pointer-events: none;
max-width: calc(100% - 20px);
word-wrap: break-word;
z-index: 11;
}
.signature-overlay .handle {
position: absolute;
top: -10px;
@@ -339,6 +354,14 @@ body {
border-radius: 3px;
}
.text-input-section {
margin-bottom: 30px;
padding: 20px;
background: #f8f9ff;
border-radius: 12px;
border: 2px solid #667eea;
}
.signature-controls {
margin-top: 20px;
padding: 15px;

View File

@@ -14,7 +14,13 @@ services:
networks:
- pdf-signature-network
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:8080', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
test:
[
"CMD",
"node",
"-e",
"require('http').get('http://pdf-signature:8080', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"
]
interval: 30s
timeout: 3s
retries: 3

View File

@@ -39,6 +39,14 @@
Warte auf Unterschrift von der Signatur-Station...
</div>
<div class="text-input-section" id="textInputSection" style="display: none;">
<label for="textInput" style="display: block; margin-bottom: 10px; font-weight: 500; color: #667eea;">
📝 Text für Unterschrifts-Overlay (wird oben links angezeigt):
</label>
<input type="text" id="textInput"
style="width: 100%; padding: 12px; border: 2px solid #667eea; border-radius: 8px; font-size: 1em;">
</div>
<!-- Page Navigation -->
<div class="page-navigation" id="pageNavigation" style="display: none;">
<button class="nav-button" id="prevPageBtn" onclick="changePage(-1)">◀ Zurück</button>
@@ -64,6 +72,7 @@
</div>
<div class="signature-overlay" id="signatureOverlay">
<div class="handle" onclick="removeSignatureOverlay()" title="Unterschrift entfernen">×</div>
<div class="text-overlay-content" id="textOverlayContent" style="display: none;"></div>
<img id="signatureImage" src="" alt="Unterschrift">
<div class="resize-handle" id="resizeHandle"></div>
</div>
@@ -91,7 +100,7 @@
🗑️ Unterschrift entfernen
</button>
<button class="upload-button" id="placeSignatureButton" style="flex: 2; background: linear-gradient(135deg, #28a745 0%, #20c997 100%);">
✓ Unterschrift platzieren & PDF erstellen
✓ Unterschrift & Text platzieren & PDF erstellen
</button>
</div>
</div>

View File

@@ -16,6 +16,10 @@ let signatureDataUrl = null;
let signaturePos = { x: 0.5, y: 0.1 }; // Relative position (0-1)
let signatureScale = 0.3;
// Text overlay state (now part of signature overlay)
let textOverlayText = '';
let textFontSize = 14;
// WebSocket connection
let ws = null;
@@ -114,6 +118,10 @@ document.getElementById('pdfInput').addEventListener('change', async (e) => {
document.getElementById('uploadSection').style.display = 'none';
document.getElementById('statusSection').style.display = 'block';
// Show text input section and update with current date
document.getElementById('textInputSection').style.display = 'block';
updateTextInputWithDate();
// Show signature placeholder
document.getElementById('signaturePlaceholder').style.display = 'block';
@@ -298,6 +306,9 @@ async function renderPdfPreview(pdfBytes, pageNum = 1) {
placeholder.style.display = 'none';
}
}
// Update text in overlay if it exists
updateTextInOverlay();
}, 300); // Increased delay to ensure canvas is ready
console.log('PDF Seite', pageNum, 'erfolgreich gerendert');
@@ -504,6 +515,7 @@ async function changePage(direction) {
console.log('Overlay nach Seitenwechsel positioniert:', overlay.style.left, overlay.style.top);
}
}
}, 100);
// Send page change to signature station
@@ -567,6 +579,9 @@ async function showSignatureOverlay(signatureDataUrl) {
overlay.style.position = 'absolute'; // Ensure positioning works
overlay.style.zIndex = '10'; // Ensure it's on top
// Update text in overlay if text input has value
updateTextInOverlay();
document.getElementById('signatureControls').classList.add('show');
// Show page selector if multi-page
@@ -779,7 +794,7 @@ async function addSignatureToPdf() {
// Embed signature image once
const signatureImage = await pdfLibDoc.embedPng(signatureDataUrl);
// Add signature to each selected page
// Add signature and text to each selected page
for (const pageNum of pagesToSign) {
const page = pages[pageNum - 1]; // 0-indexed
const { width, height } = page.getSize();
@@ -818,6 +833,28 @@ async function addSignatureToPdf() {
});
console.log('✓ Unterschrift zu Seite', pageNum, 'hinzugefügt');
// Add text overlay if it exists (text is part of signature overlay, positioned top-left)
if (textOverlayText) {
// Text is positioned relative to signature overlay (top-left)
// Calculate text position: overlay left position + small offset, overlay top position - small offset
const textOffsetX = 5 * scaleX; // 5px offset from left
const textOffsetY = -5 * scaleY; // 5px offset from top (negative because PDF y is bottom-up)
const textX = x + textOffsetX;
// overlayTop is from top of canvas, convert to PDF coordinates (bottom-up)
const overlayTopInPdf = height - (overlayTop * scaleY);
const textY = overlayTopInPdf + textOffsetY; // Top of overlay - small offset
// Add text to page
page.drawText(textOverlayText, {
x: textX,
y: textY,
size: textFontSize * scaleY,
color: rgb(0, 0, 0),
});
console.log('✓ Text zu Seite', pageNum, 'hinzugefügt');
}
}
// Save PDF
@@ -846,6 +883,38 @@ async function addSignatureToPdf() {
}
}
function getCurrentDateString() {
const today = new Date();
const day = String(today.getDate()).padStart(2, '0');
const month = String(today.getMonth() + 1).padStart(2, '0');
const year = today.getFullYear();
return `${day}.${month}.${year}`;
}
function updateTextInputWithDate() {
const textInput = document.getElementById('textInput');
if (textInput) {
const currentDate = getCurrentDateString();
textInput.value = `Abgeholt am ${currentDate} durch:`;
}
}
function updateTextInOverlay() {
const textInput = document.getElementById('textInput');
const text = textInput ? textInput.value : '';
textOverlayText = text;
const textOverlayContent = document.getElementById('textOverlayContent');
if (textOverlayContent) {
if (text && text.trim()) {
textOverlayContent.textContent = text;
textOverlayContent.style.display = 'block';
} else {
textOverlayContent.style.display = 'none';
}
}
}
function removeSignatureOverlay() {
// Only remove the overlay, keep signedPdfBytes intact
const overlay = document.getElementById('signatureOverlay');
@@ -853,6 +922,12 @@ function removeSignatureOverlay() {
overlay.style.display = 'none'; // Explicitly hide overlay
document.getElementById('signatureControls').classList.remove('show');
// Hide text in overlay
const textOverlayContent = document.getElementById('textOverlayContent');
if (textOverlayContent) {
textOverlayContent.style.display = 'none';
}
// Reset placeholder to default state
const placeholder = document.getElementById('signaturePlaceholder');
const placeholderImg = document.getElementById('placeholderSignatureImage');
@@ -1004,9 +1079,15 @@ document.getElementById('downloadButton').addEventListener('click', async () =>
document.getElementById('uploadSection').style.display = 'block';
document.getElementById('statusSection').style.display = 'none';
// Hide text input section
document.getElementById('textInputSection').style.display = 'none';
// Reset file input
document.getElementById('pdfInput').value = '';
// Reset text input to default with current date
updateTextInputWithDate();
// Reset place signature button
const placeBtn = document.getElementById('placeSignatureButton');
placeBtn.textContent = '✓ Unterschrift platzieren & PDF erstellen';
@@ -1029,6 +1110,7 @@ document.getElementById('downloadButton').addEventListener('click', async () =>
pdfFileName = '';
pdfDoc = null;
currentPage = null;
textOverlayText = '';
console.log('Alles zurückgesetzt für nächste PDF');
} catch (error) {
@@ -1044,9 +1126,15 @@ function resetToStart() {
document.getElementById('uploadSection').style.display = 'block';
document.getElementById('statusSection').style.display = 'none';
// Hide text input section
document.getElementById('textInputSection').style.display = 'none';
// Reset file input
document.getElementById('pdfInput').value = '';
// Reset text input to default with current date
updateTextInputWithDate();
// Reset place signature button
const placeBtn = document.getElementById('placeSignatureButton');
placeBtn.textContent = '✓ Unterschrift platzieren & PDF erstellen';
@@ -1079,6 +1167,10 @@ function resetToStart() {
pdfDoc = null;
currentPage = null;
signatureDataUrl = null;
textOverlayText = '';
// Reset text input to default with current date
updateTextInputWithDate();
console.log('Zurück zum Anfang - bereit für neue PDF');
}
@@ -1112,5 +1204,15 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
// Update text in overlay when text input changes
document.addEventListener('DOMContentLoaded', () => {
const textInput = document.getElementById('textInput');
if (textInput) {
textInput.addEventListener('input', () => {
updateTextInOverlay();
});
}
});
// Connect WebSocket on load
connectWebSocket();