// // NCUploadScanDocument.swift // Nextcloud // // Created by Marino Faggiana on 28/12/22. // Copyright © 2022 Marino Faggiana. All rights reserved. // // Author Marino Faggiana // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // import SwiftUI import NextcloudKit import Vision import VisionKit import Photos import PDFKit class NCHostingUploadScanDocumentView: NSObject { @objc func makeShipDetailsUI(images: [UIImage], account: String, serverUrl: String) -> UIViewController { let uploadScanDocument = NCUploadScanDocument(images: images) let details = UploadScanDocumentView(uploadScanDocument) let vc = UIHostingController(rootView: details) // vc.title = NSLocalizedString("_save_settings_", comment: "") return vc } } class NCUploadScanDocument: ObservableObject { @Published var isTextRecognition: Bool = false @Published var size: Int64 = 0 @Published var url: URL = Bundle.main.url(forResource: "Reasons to use Nextcloud", withExtension: "pdf")! let fileNameDefault = NSTemporaryDirectory() + "scandocument.pdf" var images: [UIImage] = [] init(images: [UIImage]) { self.images = images createPDF() } func createPDF(preview: Bool = true, password: String = "", textRecognition: Bool = false, quality: Double = 1) { guard !images.isEmpty else { return } let pdfData = NSMutableData() if password.isEmpty { UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil) } else { for char in password.unicodeScalars { if !char.isASCII { NCActivityIndicator.shared.stop() let error = NKError(errorCode: NCGlobal.shared.errorForbidden, errorDescription: "_password_ascii_") NCContentPresenter.shared.showError(error: error) return } } let info: [AnyHashable: Any] = [kCGPDFContextUserPassword as String: password, kCGPDFContextOwnerPassword as String: password] UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, info) } if preview { if var image = images.first { image = changeCompressionImage(image, quality: quality) let bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) UIGraphicsBeginPDFPageWithInfo(bounds, nil) image.draw(in: bounds) } } else { for var image in images { image = changeCompressionImage(image, quality: quality) let bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) if textRecognition { } else { UIGraphicsBeginPDFPageWithInfo(bounds, nil) image.draw(in: bounds) } } } UIGraphicsEndPDFContext() do { url = URL(fileURLWithPath: fileNameDefault) try pdfData.write(to: url, options: .atomic) } catch { print("error catched") } size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNameDefault) } func changeCompressionImage(_ image: UIImage, quality: Double) -> UIImage { var compressionQuality: CGFloat = 0.5 var baseHeight: Float = 595.2 // A4 var baseWidth: Float = 841.8 // A4 switch quality { case 0: baseHeight *= 1 baseWidth *= 1 compressionQuality = 0.3 case 1: baseHeight *= 2 baseWidth *= 2 compressionQuality = 0.6 case 2: baseHeight *= 4 baseWidth *= 4 compressionQuality = 0.8 case 3: baseHeight *= 6 baseWidth *= 6 compressionQuality = 0.9 case 4: baseHeight *= 8 baseWidth *= 8 compressionQuality = 1 default: break } var newHeight = Float(image.size.height) var newWidth = Float(image.size.width) var imgRatio: Float = newWidth / newHeight let baseRatio: Float = baseWidth / baseHeight if newHeight > baseHeight || newWidth > baseWidth { if imgRatio < baseRatio { imgRatio = baseHeight / newHeight newWidth = imgRatio * newWidth newHeight = baseHeight } else if imgRatio > baseRatio { imgRatio = baseWidth / newWidth newHeight = imgRatio * newHeight newWidth = baseWidth } else { newHeight = baseHeight newWidth = baseWidth } } let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(newWidth), height: CGFloat(newHeight)) UIGraphicsBeginImageContext(rect.size) image.draw(in: rect) let img = UIGraphicsGetImageFromCurrentImageContext() let imageData = img?.jpegData(compressionQuality: CGFloat(compressionQuality)) UIGraphicsEndImageContext() return UIImage(data: imageData!) ?? image } } extension NCUploadScanDocument: NCSelectDelegate { func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) { } } extension NCUploadScanDocument: NCCreateFormUploadConflictDelegate { func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) { } } // MARK: - Preview / Test struct UploadScanDocumentView: View { @State var quality = 2.0 @State var password: String = "" @State var filename: String = "" @State var isSecured: Bool = true @ObservedObject var uploadScanDocument: NCUploadScanDocument init(_ uploadScanDocument: NCUploadScanDocument) { self.uploadScanDocument = uploadScanDocument } var body: some View { GeometryReader { geo in List { Section(header: Text(NSLocalizedString("_file_creation_", comment: ""))) { HStack { Label { Text("/") .frame(maxWidth: .infinity, alignment: .trailing) } icon: { Image("folder") .renderingMode(.template) .resizable() .scaledToFit() .frame(width: NCBrandSettings.shared.settingsSizeImage, height: NCBrandSettings.shared.settingsSizeImage) .foregroundColor(Color(NCBrandColor.shared.brand)) } Spacer() } .contentShape(Rectangle()) .onTapGesture { // } .complexModifier { view in if #available(iOS 16, *) { view.alignmentGuide(.listRowSeparatorLeading) { _ in return 0 } } } HStack { Text(NSLocalizedString("_filename_", comment: "")) TextField(NSLocalizedString("_enter_filename_", comment: ""), text: $filename) .multilineTextAlignment(.trailing) } HStack { Group { Text(NSLocalizedString("_password_", comment: "")) if isSecured { SecureField(NSLocalizedString("_enter_password_", comment: ""), text: $password) .multilineTextAlignment(.trailing) } else { TextField(NSLocalizedString("_enter_password_", comment: ""), text: $password) .multilineTextAlignment(.trailing) } } Button(action: { isSecured.toggle() }) { Image(systemName: self.isSecured ? "eye.slash" : "eye") .accentColor(.gray) } } HStack { Toggle(NSLocalizedString("_text_recognition_", comment: ""), isOn: $uploadScanDocument.isTextRecognition) .toggleStyle(SwitchToggleStyle(tint: Color(NCBrandColor.shared.brand))) .onChange(of: uploadScanDocument.isTextRecognition) { newValue in } } } Section(header: Text(NSLocalizedString("_quality_image_title_", comment: ""))) { VStack { Slider(value: $quality, in: 0...4, step: 1).onChange(of: quality, perform: { quality in uploadScanDocument.createPDF(quality: quality) }) .accentColor(Color(NCBrandColor.shared.brand)) } PDFKitRepresentedView(uploadScanDocument.url) .frame(maxWidth: .infinity, minHeight: geo.size.height / 2.7) }.complexModifier { view in if #available(iOS 15, *) { view.listRowSeparator(.hidden) } } Button(NSLocalizedString("_save_", comment: "")) { print("ciao") } .buttonStyle(NCButton()) .frame(maxWidth: .infinity, alignment: .center) .listRowBackground(Color(UIColor.systemGroupedBackground)) } } .background(Color(UIColor.systemGroupedBackground)) } } struct NCButton: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .padding(.horizontal, 50) .padding(.vertical, 10) .background(Color(NCBrandColor.shared.brand)) .foregroundColor(.white) .clipShape(Capsule()) } } struct PDFKitRepresentedView: UIViewRepresentable { let url: URL init(_ url: URL) { self.url = url } func makeUIView(context: UIViewRepresentableContext) -> PDFKitRepresentedView.UIViewType { let pdfView = PDFView() pdfView.document = PDFDocument(url: self.url) pdfView.autoScales = true pdfView.backgroundColor = .clear pdfView.displayMode = .singlePage pdfView.displayDirection = .vertical return pdfView } func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext) { } } struct UploadScanDocumentView_Previews: PreviewProvider { static var previews: some View { let uploadScanDocument = NCUploadScanDocument(images: []) UploadScanDocumentView(uploadScanDocument) // .previewDevice(PreviewDevice(rawValue: "iPhone 14 Pro")) // .previewDisplayName("iPhone 14") } }