151 lines
4.3 KiB
Swift
151 lines
4.3 KiB
Swift
import SwiftUI
|
|
import SFSafeSymbols
|
|
|
|
struct ContentView: View {
|
|
|
|
@Environment(\.colorScheme)
|
|
var defaultColorScheme: ColorScheme
|
|
|
|
let content: [CVInfo]
|
|
|
|
let style: CVStyle
|
|
|
|
init(content: [CVInfo], style: CVStyle) {
|
|
self.content = content
|
|
self.style = style
|
|
}
|
|
|
|
@State
|
|
var darkModeEnabled = true
|
|
|
|
@State
|
|
var didReadDarkMode = false
|
|
|
|
@State
|
|
var selectedLanguageIndex = 0
|
|
|
|
var colorStyle: ColorScheme {
|
|
darkModeEnabled ? .dark : .light
|
|
}
|
|
|
|
var info: CVInfo {
|
|
content[selectedLanguageIndex]
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading) {
|
|
HStack {
|
|
Picker("", selection: $selectedLanguageIndex) {
|
|
ForEach(Array(content.enumerated()), id: \.element) { content in
|
|
Text(content.element.language)
|
|
.tag(content.offset)
|
|
}
|
|
}.frame(width: 100)
|
|
Button(action: createAndSavePDF) {
|
|
Label("Export PDF", systemSymbol: .squareAndArrowDown)
|
|
}
|
|
.padding()
|
|
Spacer()
|
|
Toggle("Dark mode", isOn: $darkModeEnabled)
|
|
.toggleStyle(SwitchToggleStyle())
|
|
}
|
|
.padding(.horizontal)
|
|
ScrollView(.vertical) {
|
|
CV(info: info, style: style)
|
|
}.frame(width: style.pageWidth)
|
|
}
|
|
.preferredColorScheme(colorStyle)
|
|
.onAppear {
|
|
guard !didReadDarkMode else {
|
|
return
|
|
}
|
|
darkModeEnabled = defaultColorScheme == .dark
|
|
didReadDarkMode = true
|
|
}
|
|
}
|
|
|
|
private func createAndSavePDF() {
|
|
DispatchQueue.main.async {
|
|
guard let pdfURL = renderPDF() else {
|
|
return
|
|
}
|
|
guard let url = showSavePanel() else {
|
|
return
|
|
}
|
|
writePDF(at: pdfURL, to: url)
|
|
}
|
|
}
|
|
|
|
private func showSavePanel() -> URL? {
|
|
let savePanel = NSSavePanel()
|
|
savePanel.allowedContentTypes = [.pdf]
|
|
savePanel.canCreateDirectories = true
|
|
savePanel.isExtensionHidden = false
|
|
savePanel.allowsOtherFileTypes = false
|
|
savePanel.title = "Save PDF"
|
|
savePanel.message = "Choose a location to save a PDF of the resume"
|
|
savePanel.nameFieldLabel = "File name:"
|
|
savePanel.nameFieldStringValue = "CV.pdf"
|
|
|
|
let response = savePanel.runModal()
|
|
guard response == .OK else {
|
|
return nil
|
|
}
|
|
return savePanel.url
|
|
}
|
|
|
|
private func writePDF(at source: URL, to destination: URL) {
|
|
do {
|
|
if FileManager.default.fileExists(atPath: destination.path) {
|
|
try FileManager.default.removeItem(at: destination)
|
|
}
|
|
try FileManager.default.copyItem(at: source, to: destination)
|
|
} catch {
|
|
print("Failed to save pdf: \(error)")
|
|
}
|
|
}
|
|
|
|
private var renderContent: some View {
|
|
CV(info: info, style: style)
|
|
.frame(width: style.pageWidth, height: style.pageHeight)
|
|
}
|
|
|
|
@MainActor
|
|
private func renderPDF() -> URL? {
|
|
let pdfURL = URL.documentsDirectory.appending(path: "cv.pdf")
|
|
let renderer = ImageRenderer(content: renderContent)
|
|
|
|
var didFinish = false
|
|
renderer.render { size, context in
|
|
var box = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
|
|
|
guard let pdf = CGContext(pdfURL as CFURL, mediaBox: &box, nil) else {
|
|
print("Failed to create CGContext")
|
|
return
|
|
}
|
|
|
|
let options: [CFString: Any] = [
|
|
kCGPDFContextMediaBox: CGRect(origin: .zero, size: size)
|
|
]
|
|
|
|
pdf.beginPDFPage(options as CFDictionary)
|
|
context(pdf)
|
|
pdf.endPDFPage()
|
|
pdf.closePDF()
|
|
didFinish = true
|
|
}
|
|
guard didFinish else {
|
|
return nil
|
|
}
|
|
print("PDF created")
|
|
return pdfURL
|
|
}
|
|
}
|
|
|
|
struct ContentView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
ContentView(content: [cvInfoEnglish, cvInfoGerman], style: cvStyle)
|
|
.frame(width: 600, height: 600 * sqrt(2))
|
|
}
|
|
}
|