Add image export option
This commit is contained in:
parent
e01da1ac4a
commit
7b1e86fe97
@ -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)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user