-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from klep/klep/libscanline
Refactor core functionality into libscanline
- Loading branch information
Showing
18 changed files
with
870 additions
and
549 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
# Xcode | ||
.DS_Store | ||
scanline.xcodeproj/xcshareddata/ | ||
build/ | ||
derived_data/ | ||
*.pbxuser | ||
!default.pbxuser | ||
*.mode1v3 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
<key>NSHumanReadableCopyright</key> | ||
<string>Copyright © 2022 Scott J. Kleper. All rights reserved.</string> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// | ||
// ScanlineOutputProcessor.swift | ||
// libscanline | ||
// | ||
// Created by Scott J. Kleper on 5/13/21. | ||
// Copyright © 2021 Scott J. Kleper. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import AppKit | ||
import Quartz | ||
|
||
public class ScanlineOutputProcessor { | ||
let logger: Logger | ||
let configuration: ScanConfiguration | ||
let urls: [URL] | ||
|
||
public init(urls: [URL], configuration: ScanConfiguration, logger: Logger) { | ||
self.urls = urls | ||
self.configuration = configuration | ||
self.logger = logger | ||
} | ||
|
||
public func process() -> Bool { | ||
let wantsPDF = configuration.config[ScanlineConfigOptionJPEG] == nil && configuration.config[ScanlineConfigOptionTIFF] == nil | ||
if !wantsPDF { | ||
for url in urls { | ||
outputAndTag(url: url) | ||
} | ||
} else { | ||
// Combine into a single PDF | ||
if let combinedURL = combine(urls: urls) { | ||
outputAndTag(url: combinedURL) | ||
} else { | ||
logger.log("Error while creating PDF") | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
public func combine(urls: [URL]) -> URL? { | ||
let document = PDFDocument() | ||
|
||
for url in urls { | ||
if let page = PDFPage(image: NSImage(byReferencing: url)) { | ||
document.insert(page, at: document.pageCount) | ||
} | ||
} | ||
|
||
let tempFilePath = "\(NSTemporaryDirectory())/scan.pdf" | ||
document.write(toFile: tempFilePath) | ||
|
||
return URL(fileURLWithPath: tempFilePath) | ||
} | ||
|
||
public func outputAndTag(url: URL) { | ||
let gregorian = NSCalendar(calendarIdentifier: .gregorian)! | ||
let dateComponents = gregorian.components([.year, .hour, .minute, .second], from: Date()) | ||
|
||
let outputRootDirectory = configuration.config[ScanlineConfigOptionDir] as! String | ||
var path = outputRootDirectory | ||
|
||
// If there's a tag, move the file to the first tag location | ||
if configuration.tags.count > 0 { | ||
path = "\(path)/\(configuration.tags[0])/\(dateComponents.year!.fld())" | ||
} | ||
|
||
logger.verbose("Output path: \(path)") | ||
|
||
do { | ||
try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil) | ||
} catch { | ||
logger.log("Error while creating directory \(path)") | ||
return | ||
} | ||
|
||
let destinationFileExtension: String | ||
if configuration.config[ScanlineConfigOptionTIFF] != nil { | ||
destinationFileExtension = "tif" | ||
} else if configuration.config[ScanlineConfigOptionJPEG] != nil { | ||
destinationFileExtension = "jpg" | ||
} else { | ||
destinationFileExtension = "pdf" | ||
} | ||
|
||
let destinationFileRoot: String = { () -> String in | ||
if let fileName = self.configuration.config[ScanlineConfigOptionName] { | ||
return "\(path)/\(fileName)" | ||
} | ||
return "\(path)/scan_\(dateComponents.hour!.f02ld())\(dateComponents.minute!.f02ld())\(dateComponents.second!.f02ld())" | ||
}() | ||
|
||
var destinationFilePath = "\(destinationFileRoot).\(destinationFileExtension)" | ||
var i = 0 | ||
while FileManager.default.fileExists(atPath: destinationFilePath) { | ||
destinationFilePath = "\(destinationFileRoot).\(i).\(destinationFileExtension)" | ||
i += 1 | ||
} | ||
|
||
logger.verbose("About to copy \(url.absoluteString) to \(destinationFilePath)") | ||
|
||
let destinationURL = URL(fileURLWithPath: destinationFilePath) | ||
do { | ||
try FileManager.default.copyItem(at: url, to: destinationURL) | ||
} catch { | ||
logger.log("Error while copying file to \(destinationURL.absoluteString)") | ||
return | ||
} | ||
|
||
// Alias to all other tag locations | ||
// todo: this is super repetitive with above... | ||
if configuration.tags.count > 1 { | ||
for tag in configuration.tags.subarray(with: NSMakeRange(1, configuration.tags.count - 1)) { | ||
logger.verbose("Aliasing to tag \(tag)") | ||
let aliasDirPath = "\(outputRootDirectory)/\(tag)/\(dateComponents.year!.fld())" | ||
do { | ||
try FileManager.default.createDirectory(atPath: aliasDirPath, withIntermediateDirectories: true, attributes: nil) | ||
} catch { | ||
logger.log("Error while creating directory \(aliasDirPath)") | ||
return | ||
} | ||
let aliasFileRoot = { () -> String in | ||
if let name = configuration.config[ScanlineConfigOptionName] { | ||
return "\(aliasDirPath)/\(name)" | ||
} | ||
return "\(aliasDirPath)/scan_\(dateComponents.hour!.f02ld())\(dateComponents.minute!.f02ld())\(dateComponents.second!.f02ld())" | ||
}() | ||
var aliasFilePath = "\(aliasFileRoot).\(destinationFileExtension)" | ||
var i = 0 | ||
while FileManager.default.fileExists(atPath: aliasFilePath) { | ||
aliasFilePath = "\(aliasFileRoot).\(i).\(destinationFileExtension)" | ||
i += 1 | ||
} | ||
logger.verbose("Aliasing to \(aliasFilePath)") | ||
do { | ||
try FileManager.default.createSymbolicLink(atPath: aliasFilePath, withDestinationPath: destinationFilePath) | ||
} catch { | ||
logger.log("Error while creating alias at \(aliasFilePath)") | ||
return | ||
} | ||
} | ||
} | ||
|
||
if configuration.config[ScanlineConfigOptionOpen] != nil { | ||
logger.verbose("Opening file at \(destinationFilePath)") | ||
NSWorkspace.shared.openFile(destinationFilePath) | ||
} | ||
} | ||
} | ||
|
||
fileprivate extension Int { | ||
// format to 2 decimal places | ||
func f02ld() -> String { | ||
return String(format: "%02ld", self) | ||
} | ||
|
||
func fld() -> String { | ||
return String(format: "%ld", self) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.