Skip to main content

Utilities | iOS Document Scanner

Besides the scanning modules, Scanbot SDK also contains some utility classes that can reduce your workload, hence making your life easier:

Zoom- and scrollable image view (SBSDKZoomingImageScrollView)

  • You can use it as a direct UIImageView replacement.
  • You can zoom in to the image, scroll around, and when you zoom out, the image is centered within the view.
  • You can add a transparent overlay that will act like a HUD above the image view.
  • You can specify the margins that the image should maintain when zoomed out.
import UIKit
import ScanbotSDK

class ZoomingImageScrollViewSwiftViewController: UIViewController {

var imageViewWithZoom: SBSDKZoomingImageScrollView?
let image = UIImage(named: "documentImage")

override func viewDidLoad() {
super.viewDidLoad()

// We instantiate our new class helper.
imageViewWithZoom = SBSDKZoomingImageScrollView()

// We set the desired image.
imageViewWithZoom?.image = image

// We can add some margins.
imageViewWithZoom?.margins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)

// We set an overlay on top of our image.
let overlayView = UIView()
overlayView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
imageViewWithZoom?.overlayView = overlayView

// We add our view to the subview with our desired constraints.
imageViewWithZoom?.translatesAutoresizingMaskIntoConstraints = false
if let imageViewWithZoom = imageViewWithZoom {
self.view.addSubview(imageViewWithZoom)
if let image = image {
let ratio = image.size.width / image.size.height
imageViewWithZoom.widthAnchor.constraint(equalToConstant: 320).isActive = true
imageViewWithZoom.heightAnchor.constraint(equalToConstant: 320 / ratio).isActive = true
imageViewWithZoom.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
imageViewWithZoom.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
}
}
}

Camera Device (SBSDKCameraDevice)

  • Provides the available camera devices to be used with any of the Classic or RTU-UI scanner components in Scanbot SDK.
  • You can enumerate available devices using their position (front or back) and/or their type (wide, tele, ultra-wide, dual, triple).
import UIKit
import ScanbotSDK

class CameraDeviceSwiftViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Get all the available camera devices.
let availableDevices = SBSDKCameraDevice.availableDevices

// Get all the available back camera devices.
let availableBackDevices = SBSDKCameraDevice.availableDevices(for: .back)

// Get all the desired cameras by providing the type and position.
let availableTripeCameraBackDevices = SBSDKCameraDevice.availableDevices(for: .triple, position: .back)

// Get the default back facing camera.
let defaultBackCamera = SBSDKCameraDevice.defaultBackFacingCamera

// Get the default front facing camera.
let defaultFrontCamera = SBSDKCameraDevice.defaultFrontFacingCamera

// Get the first triple back camera and create scanner components.
if let tripleCamera = availableTripeCameraBackDevices.first {
createRTUUIScanner(with: tripleCamera)
createClassicalScanner(with: tripleCamera)
}

}

func createRTUUIScanner(with device: SBSDKCameraDevice) {

// Create the camera configuration.
let cameraConfig = SBSDKUICameraConfiguration()

// Assign the device to the camera configuration.
cameraConfig.camera = device

// Assemble the scanner configuration and pass the camera configuration.
let configuration =
SBSDKUIDocumentScannerConfiguration(uiConfiguration: SBSDKUIDocumentScannerUIConfiguration(),
textConfiguration: SBSDKUIDocumentScannerTextConfiguration(),
behaviorConfiguration: SBSDKUIDocumentScannerBehaviorConfiguration(),
cameraConfiguration: cameraConfig)

// Create the RTU-UI scanner, passing the scanner configuration.
let scanner = SBSDKUIDocumentScannerViewController.createNew(configuration: configuration, delegate: nil)

}


func createClassicalScanner(with device: SBSDKCameraDevice) {

// Create the classical scanner.
let scanner = SBSDKDocumentScannerViewController(parentViewController: self,
parentView: self.view,
delegate: nil)

// Assign the device to the scanner.
scanner?.cameraDevice = device
}
}

Image Metadata Processor (SBSDKImageMetadataProcessor)

  • Lets you read, modify and write image metadata.
import UIKit
import ScanbotSDK

class ImageMetadataProcessorSwiftViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Set the desired image.
let image = UIImage(named: "documentImage")
if let image {

// Convert the image to data.
let imageData = image.jpegData(compressionQuality: 0)

// Extract the metadata.
if let imageData, let oldExtractedMetadata = SBSDKImageMetadataProcessor.extractMetadata(from: imageData) {

self.printExtractedMetadata(metadata: oldExtractedMetadata, withTitle: "OLD metadata")

// Create new metadata or modify the extracted metadata.
if let injectedMetadata = SBSDKImageMetadata(with: oldExtractedMetadata.metadataDictionary) {
injectedMetadata.title = "Scanbot SDK"
injectedMetadata.originalDate = Date()

// Inject the new metadata into the image data.
let newImageData = SBSDKImageMetadataProcessor.imageDataByInjecting(metadata: injectedMetadata, into: imageData)

// Re-extract the new metadata again.
if let newImageData,
let newExtractedMetadata = SBSDKImageMetadataProcessor.extractMetadata(from: newImageData) {
self.printExtractedMetadata(metadata: newExtractedMetadata, withTitle: "NEW metadata")
}
}
}
}
}

// Print some of the metadata fields.
func printExtractedMetadata(metadata: SBSDKImageMetadata?, withTitle title: String) {
if let metadata {
print("Begin of \(title) *************")
print("altitude: \(String(describing: metadata.altitude))")
print("latitude: \(String(describing: metadata.latitude))")
print("longitude: \(String(describing: metadata.longitude))")
print("aperture: \(String(describing: metadata.aperture))")
print("digitalizationDate: \(String(describing: metadata.digitalizationDate?.description))")
print("exposureTime: \(String(describing: metadata.exposureTime))")
print("focalLength: \(String(describing: metadata.focalLength))")
print("focalLength35mm: \(String(describing: metadata.focalLength35mm))")
print("imageHeight: \(String(describing: metadata.imageHeight))")
print("imageWidth: \(String(describing: metadata.imageWidth))")
print("isoValue: \(String(describing: metadata.ISOValue))")
print("lensMaker: \(String(describing: metadata.lensMaker))")
print("lensModel: \(String(describing: metadata.lensModel))")
print("orientation: \(String(describing: metadata.orientation))")
print("originalDate: \(String(describing: metadata.originalDate?.description))")
print("title: \(String(describing: metadata.title))")
print("metadataDictionary: \(String(describing: metadata.metadataDictionary))")
print("End of \(title) *************")
}
}
}

PDF Metadata Processor (SBSDKPDFMetadataProcessor)

  • Lets you read, modify and write a PDF document's metadata.
  • If you want to use this feature your app must import the PDFKit module.
import UIKit
import ScanbotSDK
import PDFKit

class PDFMetadataProcessorSwiftViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Get the URL of your desired PDF file.
guard let url = Bundle.main.url(forResource: "document", withExtension: "pdf") else { return }

// Initiate the pdf metadata editor using the url.
let pdfMetadataEditor = SBSDKPDFMetadataEditor(pdfURL: url)

// Handle your pdf metadata, in this case, we are just printing them.
self.printMetadata(metadata: pdfMetadataEditor?.metadata, title: "oldMetadata")

// Edit the pdf metadata.
pdfMetadataEditor?.title = "A Scanbot Demo PDF"
pdfMetadataEditor?.author = "ScanbotSDK Development"
pdfMetadataEditor?.creator = "ScanbotSDK for iOS"
pdfMetadataEditor?.subject = "A demonstration of ScanbotSDK PDF creation."

// Inject the new pdf metadata in your pdf at the same url or in a new created url.
// You could erase the old metadata or merge them with the new ones.
do {
try pdfMetadataEditor?.save()
}
catch {
// Catch the error.
}

// Extract the new pdf metadata again.
let newPDFMetadata = pdfMetadataEditor?.metadata

// Handle the new pdf metadata. In this case lets just print them.
self.printMetadata(metadata: newPDFMetadata, title: "newPDFMetadata")

}

// Print the PDF metadata.
func printMetadata(metadata: [AnyHashable: Any]?, title: String) {
print("\n*********\(title)********")
if let metadata {
for (key, value) in metadata {
print("\(key) : \(value)")
}
}
}
}

Text Orientation Recognition (SBSDKTextLayoutRecognizer)

  • Determines the orientation of text in an image.
import UIKit
import ScanbotSDK

class TextOrientationRecognizerSwiftViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Initialize an instance of the recognizer.
let recognizer = SBSDKTextLayoutRecognizer()

// Set the desired image.
if let image = UIImage(named: "testDocument") {

// Recognize the text orientation.
let oldOrientation = recognizer.recognizeTextOrientation(on: image)

// Handle the result.
self.printOrientationResult(orientation: oldOrientation)

// Recognize the text orientation, but request a minimum confidence of 2.0.
let orientationWithConfidence = recognizer.recognizeTextOrientation(on: image, with: 2.0)

// Handle the result.
self.printOrientationResult(orientation: orientationWithConfidence)

// Rotate the image to portrait mode if possible.
let newImage = self.rotateImageToPortraitMode(image: image, orientation: orientationWithConfidence)
}
}

// Rotate the image according to the recognized orientation.
func rotateImageToPortraitMode(image: UIImage, orientation: SBSDKTextOrientation) -> UIImage? {
switch orientation {
case .notRecognized, .lowConfidence, .up:
return image
case .right:
return image.sbsdk_imageRotatedCounterClockwise(1)
case .down:
return image.sbsdk_imageRotatedClockwise(2)
case .left:
return image.sbsdk_imageRotatedCounterClockwise(3)
default:
return image
}
}

// Print the recognized orientation to the console.
func printOrientationResult(orientation: SBSDKTextOrientation) {
switch orientation {
case .notRecognized:
print("Text orientation was not recognized (bad image quality, etc).")
case .lowConfidence:
print("Text was recognized, but the confidence of recognition is too low.")
case .up:
print("Text is not rotated.")
case .right:
print("Text is rotated 90 degrees clockwise.")
case .down:
print("Text is rotated 180 degrees clockwise.")
case .left:
print("Text is rotated 270 degrees clockwise.")
default:
print(orientation.rawValue)
}
}
}

Document Quality Analyzer (SBSDKDocumentQualityAnalyzer)

  • Analyzes the quality of text in a document on a still image.
  • The result is an enum of type SBSDKDocumentQuality.
import UIKit
import ScanbotSDK

class DocumentQualityAnalyzerSwiftViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Initialize the analyzer.
let analyzer = SBSDKDocumentQualityAnalyzer()

// Set the desired image.
if let image = UIImage(named: "testDocument") {

// Analyze the quality of the image.
let quality = analyzer.analyze(on: image)

// Handle the result.
self.printResult(quality: quality)
}
}

// Print the result.
func printResult(quality: SBSDKDocumentQuality) {

switch quality {
case .noDocument:
print("No document was found")
case .veryPoor:
print("The quality of the document is very poor")
case .poor:
print("The quality of the document is poor")
case .reasonable:
print("The quality of the document is reasonable")
case .good:
print("The quality of the document is good")
case .excellent:
print("The quality of the document is excellent")
@unknown default: break
}
}
}

Image Storages (SBSDKIndexedImageStorage, SBSDKKeyedImageStorage)

  • SBSDKIndexedImageStorage and SBSDKKeyedImageStorage are thread-safe and persistent storage for images.
  • They can be used to store and retrieve images in an array-fashioned style (indexed) or dictionary-fashioned style (keyed) to and from the device disk.
  • Both allow the usage of an encrypter which encrypts the data when writing it to the disk and decrypts the data when reading it from the disk.
  • These classes conform to the SBSDKImageStoring protocol. Custom image storage can be implemented and used within Scanbot SDK by conforming to this protocol.
import UIKit
import ScanbotSDK

class ImageStoringSwiftViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// Initialize a folder URL to persist the images to.
let documentsURL = SBSDKStorageLocation.applicationDocumentsFolderURL.appendingPathComponent("Images", isDirectory: true)

// Create a storage location object. This will create the folder on the filesystem if neccessary.
let documentsLocation = SBSDKStorageLocation(baseURL: documentsURL)

// Initialize an indexed image storage at this location.
// The indexed image storage is an array-like storage.
let imageStorage = SBSDKIndexedImageStorage(storageLocation: documentsLocation,
fileFormat: .PNG,
encrypter: nil)!

if let image = UIImage(named: "testDocument") {
// Save an image to the storage.
let isAdded = imageStorage.add(image)
// Check the result.
print("Image added successfully: \(isAdded)")
}
// Check the number of images in the storage.
print("Images in storage: \(imageStorage.imageCount)")


// Create and attach an encrypter to the storage. This will encrypt the image data, before it is written to disk
// and decrypt it after it is read from disk.
// Setting the encrypter does not encrypt existing images in the storage, only the images that are added to the
// storage after setting the encrypter.
imageStorage.encrypter = SBSDKAESEncrypter(password: "xxxxx", mode: .AES256)

// Store an image from a URL. This does not load the image and has a very low memory footprint.
if let url = Bundle.main.url(forResource: "imageDocument", withExtension: "png") {
// Copy the image from the URL to the storage.
let isAdded = imageStorage.addImage(from: url)
// Check the result.
print("Image from URL was added successfully : \(isAdded)")
}

// Make sure that the indices are valid before moving an image from one index to another.
if imageStorage.imageCount > 1 {
// Move the image at index 1 to index 0.
let isMoved = imageStorage.moveImage(from: 1, to: 0)
print("Image was moved successfully: \(isMoved)")
}

// Make sure that the index is valid.
if imageStorage.imageCount > 1 {
// Remove the image at index 1.
imageStorage.removeImage(at: 1)
}

// The image storage is persisted on disk.
// When the images are no longer needed they should be removed to free the disk space.
// Remove all images from the storage.
imageStorage.removeAll()
}
}

Sound Controller (SBSDKSoundController)

  • Can play a beep sound, custom sounds loaded from a URL and/or a vibration.
import UIKit
import ScanbotSDK

class SoundControllerSwiftViewController: UIViewController {

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// Initialize the sound controller.
let soundController = SBSDKSoundController()

// Play the beep sound and vibrate after 2 seconds.
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {

// Beep.
soundController.playBleepSound()

// Vibrate.
soundController.vibrate()
})


// Play a sound from a custom url and vibrate after 3 seconds.
guard let url = Bundle.main.url(forResource: "sound", withExtension: "m4a") else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {

// Play custom sound.
soundController.playCustomSound(from: url)

// Vibrate.
soundController.vibrate()
})
}
}

Want to scan longer than one minute?

Generate your free "no-strings-attached" Trial License and properly test the Scanbot SDK.

Get your free Trial License

What do you think of this documentation?