KPS Fabrication Sheets

Edit panel
dimensions
in seconds.

Upload any KPS fab sheet PDF. Click to place red dimension labels,
drag them anywhere, then export a clean annotated PDF.

📄
Drop your PDF here
KPS Fabrication Sheet · Any order number
All processing happens in your browser — nothing is uploaded
100% client-side
No account required
Supports all KPS sheets
Red text overlay on export
Rendering pages…
Preparing…
Edit
Page 1 / ?
Place: click drawing to add a red label · Move: click label → follows cursor → click to drop · Edit: double-click label · Delete: hover → ✕ · iPad — Move: press & drag label · iPad — Edit: double-tap label · iPad — Zoom: pinch two fingers

Export PDF

Choose which pages to include in the exported PDF.

setStatus(`✅ Loaded ${total} edit${total !== 1 ? 's' : ''} from ${savedAt}`); } catch (err) { alert('Could not read session file: ' + err.message); } }; reader.readAsText(file); e.target.value = ''; }); }); window.addEventListener('resize', () => { if (document.getElementById('editorScreen').style.display !== 'none') renderOverlays(); }); // ───────────────────────────────────────────────────────────────── // TOUCH SUPPORT — PINCH ZOOM + LABEL DRAG // ───────────────────────────────────────────────────────────────── // ── Pinch-to-Zoom ──────────────────────────────────────────────── let pinchZoom = 1; let pinchStartZoom = 1; let pinchStartDist = 0; let pinching = false; let zoomBaseW = 0; let zoomBaseH = 0; function resetZoom() { const canvas = document.getElementById('pageCanvas'); const frame = document.getElementById('pageFrame'); if (!canvas || !frame) return; canvas.style.width = canvas.style.height = canvas.style.maxWidth = ''; frame.style.width = frame.style.height = ''; pinchZoom = 1; zoomBaseW = zoomBaseH = 0; } function applyZoom(newZoom) { pinchZoom = Math.min(6, Math.max(0.25, newZoom)); const canvas = document.getElementById('pageCanvas'); const frame = document.getElementById('pageFrame'); if (zoomBaseW === 0) { zoomBaseW = canvas.clientWidth; zoomBaseH = canvas.clientHeight; } const W = Math.round(zoomBaseW * pinchZoom); const H = Math.round(zoomBaseH * pinchZoom); canvas.style.width = W + 'px'; canvas.style.height = H + 'px'; canvas.style.maxWidth = 'none'; frame.style.width = W + 'px'; frame.style.height = H + 'px'; renderOverlays(); } const canvasWrapEl = document.getElementById('canvasWrap'); canvasWrapEl.addEventListener('touchstart', e => { if (e.touches.length === 2) { pinching = true; if (zoomBaseW === 0) { const canvas = document.getElementById('pageCanvas'); zoomBaseW = canvas.clientWidth; zoomBaseH = canvas.clientHeight; } pinchStartDist = Math.hypot( e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY ); pinchStartZoom = pinchZoom; e.preventDefault(); } }, { passive: false }); canvasWrapEl.addEventListener('touchmove', e => { if (pinching && e.touches.length === 2) { e.preventDefault(); const dist = Math.hypot( e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY ); applyZoom(pinchStartZoom * (dist / pinchStartDist)); } }, { passive: false }); canvasWrapEl.addEventListener('touchend', e => { if (e.touches.length < 2) pinching = false; }, { passive: true }); // ── Touch Label Drag ───────────────────────────────────────────── let ts = { dimId: null, startX: 0, startY: 0, dragging: false, lastX: 0, lastY: 0 }; const DRAG_THRESH = 12; let lastTapDimId = null; let lastTapTime = 0; function addTouchToLabel(label, dimId) { label.addEventListener('touchstart', e => { if (e.touches.length !== 1) return; e.preventDefault(); e.stopPropagation(); const t = e.touches[0]; ts = { dimId, startX: t.clientX, startY: t.clientY, dragging: false, lastX: t.clientX, lastY: t.clientY }; }, { passive: false }); } document.addEventListener('touchmove', e => { if (e.touches.length !== 1 || ts.dimId === null) return; const t = e.touches[0]; if (!ts.dragging) { if (Math.hypot(t.clientX - ts.startX, t.clientY - ts.startY) > DRAG_THRESH) { ts.dragging = true; if (movingDimId !== ts.dimId) { if (movingDimId !== null) stopMoving(); startMoving(ts.dimId, { clientX: t.clientX, clientY: t.clientY }); } } } if (ts.dragging) { e.preventDefault(); ts.lastX = t.clientX; ts.lastY = t.clientY; positionGhost(t.clientX, t.clientY); } }, { passive: false }); document.addEventListener('touchend', e => { if (ts.dimId === null) return;