Add image export option

This commit is contained in:
Christoph Hagen 2023-08-23 16:39:42 +02:00
parent e01da1ac4a
commit 7b1e86fe97

View File

@ -26,6 +26,9 @@ struct ContentView: View {
@State @State
var selectedLanguageIndex = 0 var selectedLanguageIndex = 0
@State
private var showExportFormatPicker = false
var colorStyle: ColorScheme { var colorStyle: ColorScheme {
darkModeEnabled ? .dark : .light darkModeEnabled ? .dark : .light
} }
@ -43,13 +46,21 @@ struct ContentView: View {
.tag(content.offset) .tag(content.offset)
} }
}.frame(maxWidth: 100) }.frame(maxWidth: 100)
Button(action: createAndSavePDF) { Button(action: {
Label("Save PDF", systemSymbol: .squareAndArrowDown) showExportFormatPicker = true
.padding(3) }) {
} Label("Export", systemSymbol: .squareAndArrowDown)
Button(action: exportData) {
Label("Export", systemSymbol: .squareAndArrowUp)
.padding(3) .padding(3)
}.confirmationDialog(
"Which format would you like?",
isPresented: $showExportFormatPicker
) {
Button("JSON data", action: exportJsonData)
Button("PDF", action: exportPdf)
Button("Image", action: exportImage)
Button("Cancel", role: .cancel, action: {})
} message: {
Text(" Choose the export format")
} }
Button(action: importData) { Button(action: importData) {
Label("Import", systemSymbol: .docBadgePlus) Label("Import", systemSymbol: .docBadgePlus)
@ -76,27 +87,6 @@ struct ContentView: View {
} }
} }
private func exportData() {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data: Data
do {
data = try encoder.encode(info)
} catch {
print("Failed to encode data: \(error)")
return
}
guard let url = showDataSavePanel() else {
print("No url to save data")
return
}
do {
try data.write(to: url)
} catch {
print("Failed to write data: \(error)")
}
}
private func importData() { private func importData() {
guard let url = showOpenFilePanel() else { guard let url = showOpenFilePanel() else {
print("No url to import") print("No url to import")
@ -125,19 +115,34 @@ struct ContentView: View {
selectedLanguageIndex = index selectedLanguageIndex = index
} }
private func createAndSavePDF() { private func exportPdf() {
DispatchQueue.main.async { DispatchQueue.main.async {
guard let pdfURL = renderPDF() else {
return
}
guard let url = showPdfSavePanel() else { guard let url = showPdfSavePanel() else {
print("No url to save PDF") print("No url to save PDF")
return return
} }
writePDF(at: pdfURL, to: url) savePDF(to: url)
} }
} }
private func exportImage() {
DispatchQueue.main.async {
guard let url = showJpgSavePanel() else {
print("No image url")
return
}
self.saveImage(to: url)
}
}
private func exportJsonData() {
guard let url = showJsonSavePanel() else {
print("No url to save data")
return
}
saveJson(to: url)
}
private func showOpenFilePanel() -> URL? { private func showOpenFilePanel() -> URL? {
let panel = NSOpenPanel() let panel = NSOpenPanel()
panel.allowedContentTypes = [.json] panel.allowedContentTypes = [.json]
@ -155,23 +160,31 @@ struct ContentView: View {
return panel.url return panel.url
} }
private func showDataSavePanel() -> URL? { private func showJsonSavePanel() -> URL? {
showPanel( showSavePanel(
type: .json, type: .json,
title: "Save JSON", title: "Save JSON",
fileName: "CV.json", fileName: "CV.json",
message: "Choose a location to save a JSON file of the resume data") message: "Choose a location to save a JSON file of the resume data")
} }
private func showJpgSavePanel() -> URL? {
showSavePanel(
type: .jpeg,
title: "Save image",
fileName: "CV.jpg",
message: "Choose a location to save an image of the resume")
}
private func showPdfSavePanel() -> URL? { private func showPdfSavePanel() -> URL? {
showPanel( showSavePanel(
type: .pdf, type: .pdf,
title: "Save PDF", title: "Save PDF",
fileName: "CV.pdf", fileName: "CV.pdf",
message: "Choose a location to save a PDF of the resume") message: "Choose a location to save a PDF of the resume")
} }
private func showPanel(type: UTType, title: String, fileName: String, message: String) -> URL? { private func showSavePanel(type: UTType, title: String, fileName: String, message: String) -> URL? {
let savePanel = NSSavePanel() let savePanel = NSSavePanel()
savePanel.allowedContentTypes = [type] savePanel.allowedContentTypes = [type]
savePanel.canCreateDirectories = true savePanel.canCreateDirectories = true
@ -189,32 +202,42 @@ struct ContentView: View {
return savePanel.url 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 { private var renderContent: some View {
CV(info: info, style: style) CV(info: info, style: style)
.frame(width: style.pageWidth, height: style.pageHeight) .frame(width: style.pageWidth, height: style.pageHeight)
.preferredColorScheme(.dark)
} }
@MainActor @MainActor
private func renderPDF() -> URL? { private func saveImage(to url: URL) {
let pdfURL = URL.documentsDirectory.appending(path: "cv.pdf") let renderer = ImageRenderer(content: renderContent)
renderer.scale = 3
guard let image = renderer.nsImage else {
print("No image from renderer")
return
}
let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)!
let bitmapRep = NSBitmapImageRep(cgImage: cgImage)
let data = bitmapRep.representation(using: .jpeg, properties: [:])!
do {
try data.write(to: url)
print("Data saved")
} catch {
print("Failed to save image: \(error)")
}
}
@MainActor
private func savePDF(to url: URL) {
let renderer = ImageRenderer(content: renderContent) let renderer = ImageRenderer(content: renderContent)
var didFinish = false var didFinish = false
renderer.render { size, context in renderer.render { size, context in
var box = CGRect(x: 0, y: 0, width: size.width, height: size.height) var box = CGRect(x: 0, y: 0, width: size.width, height: size.height)
guard let pdf = CGContext(pdfURL as CFURL, mediaBox: &box, nil) else { guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else {
print("Failed to create CGContext") print("Failed to create CGContext")
return return
} }
@ -230,10 +253,26 @@ struct ContentView: View {
didFinish = true didFinish = true
} }
guard didFinish else { guard didFinish else {
return nil return
} }
print("PDF created") print("PDF created")
return pdfURL }
private func saveJson(to url: URL) {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data: Data
do {
data = try encoder.encode(info)
} catch {
print("Failed to encode data: \(error)")
return
}
do {
try data.write(to: url)
} catch {
print("Failed to write data: \(error)")
}
} }
} }