Initial commit: Lageplan v1.0 - Next.js 15.5, React 19
This commit is contained in:
184
src/lib/export.ts
Normal file
184
src/lib/export.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
import html2canvas from 'html2canvas'
|
||||
import jsPDF from 'jspdf'
|
||||
import type { Project, DrawFeature } from '@/app/app/page'
|
||||
import { formatDateTime } from './utils'
|
||||
|
||||
export interface ExportOptions {
|
||||
includeTitle?: boolean
|
||||
includeLegend?: boolean
|
||||
includeTimestamp?: boolean
|
||||
author?: string
|
||||
}
|
||||
|
||||
export async function exportToPNG(
|
||||
mapElement: HTMLElement,
|
||||
project: Project,
|
||||
options: ExportOptions = {}
|
||||
): Promise<void> {
|
||||
try {
|
||||
const canvas = await html2canvas(mapElement, {
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
scale: 2,
|
||||
logging: false,
|
||||
backgroundColor: '#ffffff',
|
||||
})
|
||||
|
||||
const link = document.createElement('a')
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
||||
link.download = `Lageplan_${project.title.replace(/[^a-z0-9äöüß]/gi, '_')}_${timestamp}.png`
|
||||
link.href = canvas.toDataURL('image/png', 1.0)
|
||||
link.click()
|
||||
} catch (error) {
|
||||
console.error('PNG export failed:', error)
|
||||
throw new Error('PNG Export fehlgeschlagen')
|
||||
}
|
||||
}
|
||||
|
||||
export interface LegendEntry {
|
||||
name: string
|
||||
count: number
|
||||
iconUrl?: string
|
||||
unNummer?: string
|
||||
gefahrennummer?: string
|
||||
}
|
||||
|
||||
export async function exportToPDF(
|
||||
mapElement: HTMLElement,
|
||||
project: Project,
|
||||
features: DrawFeature[],
|
||||
legendEntries: LegendEntry[] = [],
|
||||
options: ExportOptions = {}
|
||||
): Promise<void> {
|
||||
try {
|
||||
const canvas = await html2canvas(mapElement, {
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
scale: 2,
|
||||
logging: false,
|
||||
backgroundColor: '#ffffff',
|
||||
})
|
||||
|
||||
const imgWidth = 190
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width
|
||||
|
||||
const pdf = new jsPDF('p', 'mm', 'a4')
|
||||
|
||||
// Title Block
|
||||
pdf.setFillColor(220, 38, 38) // Red header
|
||||
pdf.rect(0, 0, 210, 8, 'F')
|
||||
|
||||
pdf.setTextColor(255, 255, 255)
|
||||
pdf.setFontSize(12)
|
||||
pdf.setFont('helvetica', 'bold')
|
||||
pdf.text('LAGEPLAN - FEUERWEHR', 10, 5.5)
|
||||
|
||||
pdf.setTextColor(0, 0, 0)
|
||||
|
||||
// Project info box
|
||||
pdf.setFillColor(245, 245, 245)
|
||||
pdf.rect(10, 12, 190, 28, 'F')
|
||||
pdf.setDrawColor(200, 200, 200)
|
||||
pdf.rect(10, 12, 190, 28, 'S')
|
||||
|
||||
pdf.setFontSize(14)
|
||||
pdf.setFont('helvetica', 'bold')
|
||||
pdf.text(project.title, 15, 22)
|
||||
|
||||
pdf.setFontSize(10)
|
||||
pdf.setFont('helvetica', 'normal')
|
||||
|
||||
let infoY = 28
|
||||
if (project.location) {
|
||||
pdf.text(`Einsatzort: ${project.location}`, 15, infoY)
|
||||
infoY += 5
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
pdf.text(`Datum: ${now.toLocaleDateString('de-DE')}`, 15, infoY)
|
||||
pdf.text(`Uhrzeit: ${now.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}`, 80, infoY)
|
||||
|
||||
if (options.author) {
|
||||
pdf.text(`Ersteller: ${options.author}`, 130, infoY)
|
||||
}
|
||||
|
||||
// Map image
|
||||
const mapY = 45
|
||||
const maxMapHeight = legendEntries.length > 0 ? 180 : 220
|
||||
const actualMapHeight = Math.min(imgHeight, maxMapHeight)
|
||||
|
||||
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 10, mapY, imgWidth, actualMapHeight)
|
||||
pdf.setDrawColor(100, 100, 100)
|
||||
pdf.rect(10, mapY, imgWidth, actualMapHeight, 'S')
|
||||
|
||||
// Legend
|
||||
if (legendEntries.length > 0) {
|
||||
const legendY = mapY + actualMapHeight + 8
|
||||
|
||||
pdf.setFillColor(245, 245, 245)
|
||||
pdf.rect(10, legendY, 190, 6, 'F')
|
||||
|
||||
pdf.setFontSize(10)
|
||||
pdf.setFont('helvetica', 'bold')
|
||||
pdf.text('Legende', 15, legendY + 4.5)
|
||||
|
||||
pdf.setFontSize(8)
|
||||
pdf.setFont('helvetica', 'normal')
|
||||
|
||||
let y = legendY + 12
|
||||
const colWidth = 63
|
||||
let col = 0
|
||||
|
||||
legendEntries.forEach((entry, i) => {
|
||||
if (y < 280) {
|
||||
const x = 15 + (col * colWidth)
|
||||
let text = `• ${entry.name}`
|
||||
if (entry.count > 1) text += ` (${entry.count})`
|
||||
if (entry.unNummer) text += ` [UN ${entry.unNummer}]`
|
||||
|
||||
pdf.text(text, x, y)
|
||||
|
||||
col++
|
||||
if (col >= 3) {
|
||||
col = 0
|
||||
y += 5
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Footer
|
||||
pdf.setFontSize(7)
|
||||
pdf.setTextColor(128, 128, 128)
|
||||
pdf.text(
|
||||
`Erstellt mit Lageplan-App | ${now.toLocaleDateString('de-DE')} ${now.toLocaleTimeString('de-DE')}`,
|
||||
10,
|
||||
292
|
||||
)
|
||||
pdf.text('Seite 1 von 1', 180, 292)
|
||||
|
||||
const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
||||
pdf.save(`Lageplan_${project.title.replace(/[^a-z0-9äöüß]/gi, '_')}_${timestamp}.pdf`)
|
||||
} catch (error) {
|
||||
console.error('PDF export failed:', error)
|
||||
throw new Error('PDF Export fehlgeschlagen')
|
||||
}
|
||||
}
|
||||
|
||||
export async function exportToGeoJSON(projectId: string): Promise<void> {
|
||||
try {
|
||||
const res = await fetch(`/api/projects/${projectId}/export?format=geojson`)
|
||||
if (!res.ok) throw new Error('Export fehlgeschlagen')
|
||||
|
||||
const blob = await res.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `project_${projectId}.geojson`
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
} catch (error) {
|
||||
console.error('GeoJSON export failed:', error)
|
||||
throw new Error('GeoJSON Export fehlgeschlagen')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user