PDF Generation from ScanFlow

Available on: iOS Android React Native Flutter Cordova .NET MAUI

Overview

While ScanFlow can automatically generate a PDF during the scanning process, there are scenarios where you might want to:

  • Post-process scanned images before creating the PDF
  • Apply custom image filters or enhancements
  • Add metadata or annotations to the PDF
  • Create multiple PDFs from the same scan session
  • Regenerate PDFs with different settings

This guide shows how to use the SDK’s PDF generation capabilities with images captured from ScanFlow.

Workflow Overview

  1. Configure ScanFlow to return individual images without generating a PDF
  2. Process the images as needed (filters, cropping, etc.)
  3. Generate PDF using the processed images

Disabling Automatic PDF Generation

First, configure ScanFlow to return only the scanned images. We still use the multiPage option because we want to scan multiple pages, but we set the multiPageFormat to “none” so no multi-page document is generated.

// Configure ScanFlow without PDF generation
let configuration = GSKScanFlowConfiguration()
configuration.multiPage = true // Scan multiple pages
configuration.multiPageFormat = .none // Disable automatic PDF
configuration.jpegQuality = 0.9 // High quality for post-processing

// Start scanning
let scanFlow = GSKScanFlow(configuration: configuration)
scanFlow.start(from: self, onSuccess: { result in
    // Result contains scanned pages but no PDF
    let scannedPages = result.scannedPages
    self.processAndCreatePDF(from: scannedPages)
}, failure: { error in
    print("Scanning failed: \(error.localizedDescription)")
})
// Configure ScanFlow without PDF generation
val configuration = ScanConfiguration().apply {
    multiPage = true
    multiPageFormat = none // Disable automatic PDF
}

// Start scanning
ScanFlow.scanWithConfiguration(this, configuration)

// Handle result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    try {
        val result = ScanFlow.getScanResultFromActivityResult(data)
        // Result contains pages but no PDF
        val pages = result.pages
        processAndCreatePDF(pages)
    } catch (e: Exception) {
        Log.e("Scanner", "Scanning failed", e)
    }
}
// Configure without PDF generation
const configuration = {
  multiPage: true,
  multiPageFormat: 'none', // Don't generate PDF
};

try {
  const result = await RNGeniusScan.scanWithConfiguration(configuration);

  // Result contains individual scans but no PDF
  const { scans } = result;
  await processAndCreatePDF(scans);
} catch (error) {
  console.error("Scanning failed:", error);
}
// Configure without PDF generation
var configuration = {
  'multiPage': true,
  'multiPageFormat': 'none', // Don't generate PDF
};

try {
  var result = await FlutterGeniusScan.scanWithConfiguration(configuration);

  // Result contains individual scans but no PDF
  List<dynamic> scans = result['scans'];
  await processAndCreatePDF(scans);
} catch (error) {
  print("Scanning failed: $error");
}
// Configure without PDF generation
const configuration = {
  multiPage: true,
  multiPageFormat: 'none', // Don't generate PDF
};

cordova.plugins.GeniusScan.scanWithConfiguration(
  configuration,
  function(result) {
    // Result contains individual scans but no PDF
    const scans = result.scans;
    processAndCreatePDF(scans);
  },
  function(error) {
    console.error("Scanning failed:", error);
  }
);

Processing Scanned Images

After receiving the scanned images from ScanFlow, you can process them before PDF generation:

func processAndCreatePDF(from scannedPages: [GSKScanResult]) {
    let processedImages = scannedPages.compactMap { page -> (path: String, size: GSKPDFSize)? in
        let imagePath = page.enhancedImagePath

        guard let image = UIImage(contentsOfFile: imagePath) else {
            return nil
        }

        // Example: Apply custom filter
        let processedImage = applyCustomFilter(to: image)

        // Example: Add watermark
        let watermarkedImage = addWatermark(to: processedImage)

        // Save processed image
        let processedPath = saveProcessedImage(watermarkedImage)

        return (path: processedPath, size: .A4)
    }

    // Now generate PDF with processed images
    generatePDF(from: processedImages)
}

func applyCustomFilter(to image: UIImage) -> UIImage {
    // Your custom image processing logic
    // Example: Increase contrast, adjust brightness, etc.
    return image
}

func addWatermark(to image: UIImage) -> UIImage {
    // Add watermark, timestamp, or annotations
    return image
}
fun processAndCreatePDF(pages: List<Page>) {
    val processedImages = mutableListOf<Pair<String, PDFPage.Size>>()

    pages.forEach { page ->
        val imagePath = page.enhancedImage.path

        // Load and process the image
        val bitmap = BitmapFactory.decodeFile(imagePath)

        // Example: Apply custom filter
        val processedBitmap = applyCustomFilter(bitmap)

        // Example: Add watermark
        val watermarkedBitmap = addWatermark(processedBitmap)

        // Save processed image
        val processedPath = saveProcessedImage(watermarkedBitmap)

        // Add to PDF pages
        processedImages.add(
            processedPath to PDFPage.Size.A4
        )
    }

    // Now generate PDF with processed images
    generatePDF(processedImages)
}

fun applyCustomFilter(bitmap: Bitmap): Bitmap {
    // Your custom image processing logic
    // Example: Increase contrast, adjust brightness, etc.
    return bitmap
}

fun addWatermark(bitmap: Bitmap): Bitmap {
    // Add watermark, timestamp, or annotations
    return bitmap
}

Generating the PDF

After processing, use the SDK’s PDF generator to create the final document:

func generatePDF(from processedImages: [(path: String, size: GSKPDFSize)]) {
    // Create PDF pages
    let pdfPages = processedImages.map { (imagePath, pageSize) in
        GSKPDFPage(
            filePath: imagePath,
            inchesSize: pageSize
        )
    }

    // Configure PDF metadata
    let document = GSKPDFDocument(
        title: "Processed Document",
        password: nil, // Add password protection if needed
        keywords: ["scanned", "processed"],
        pages: pdfPages
    )

    // Set output path
    let outputPath = NSTemporaryDirectory()
        .appendingPathComponent("processed_document.pdf")

    // Configure generator
    let configuration = GSKDocumentGeneratorConfiguration(
        outputFilePath: outputPath
    )

    // Generate PDF
    do {
        try GSKDocumentGenerator().generate(
            document,
            configuration: configuration
        )

        print("PDF generated at: \(outputPath)")
        // Share or save the PDF
        sharePDF(at: outputPath)
    } catch {
        print("PDF generation failed: \(error)")
    }
}
fun generatePDF(processedImages: List<Pair<String, PDFPage.Size>>) {
    // Create PDF pages
    val pdfPages = processedImages.map { (imagePath, pageSize) ->
        PDFPage(imagePath, pageSize)
    }

    // Configure PDF metadata
    val document = PDFDocument(
        pdfPages,
        "Processed Document", // title
        null, // password - add if needed
        listOf("scanned", "processed") // keywords
    )

    // Set output file
    val outputFile = File(
        context.cacheDir,
        "processed_document.pdf"
    )

    // Configure generator
    val configuration = DocumentGenerator.Configuration(outputFile)

    // Generate PDF
    try {
        DocumentGenerator(context).generatePDFDocument(
            document,
            configuration
        )

        Log.d("PDF", "Generated at: ${outputFile.absolutePath}")
        // Share or save the PDF
        sharePDF(outputFile)
    } catch (e: Exception) {
        Log.e("PDF", "Generation failed", e)
    }
}

Advanced PDF Options

Make your PDF searchable by adding OCR results:

// Perform OCR on processed images
let ocrConfiguration = GSKOCRConfiguration(
    languages: ["eng", "fra"],
    tessdataPrefixPath: Bundle.main.bundlePath
)

let ocr = GSKOCR(configuration: ocrConfiguration)

// Create PDF pages with OCR
let pdfPages = try processedImages.map { (imagePath, pageSize) in
    // Run OCR
    let ocrResult = try ocr.recognize(fromImageAtPath: imagePath)

    // Create page with text layout
    return GSKPDFPage(
        filePath: imagePath,
        inchesSize: pageSize,
        textLayout: ocrResult?.textLayout
    )
}

// Generate searchable PDF
let document = GSKPDFDocument(
    title: "Searchable Document",
    password: nil,
    keywords: nil,
    pages: pdfPages
)
// Initialize OCR
val tessDataPath = File(context.filesDir, "tessdata").absolutePath
val ocr = OCREngine(tessDataPath, listOf("eng", "fra"))

// Create PDF pages with OCR
val pdfPages = processedImages.map { (imagePath, pageSize) ->
    // Run OCR
    val ocrResult = ocr.recognizeText(imagePath)

    // Create page with text layout
    PDFPage(
        imagePath = imagePath,
        pageSize = pageSize,
        textLayout = ocrResult.textLayout // Makes PDF searchable
    )
}

// Generate searchable PDF
val document = PDFDocument(
    pages = pdfPages,
    title = "Searchable Document",
    password = null,
    keywords = null
)

Setting PDF Page Sizes

Control the page size in your generated PDF:

// Predefined sizes
let a4Page = GSKPDFPage(
    filePath: imagePath,
    inchesSize: .A4
)

let letterPage = GSKPDFPage(
    filePath: imagePath,
    inchesSize: .letter
)

// Custom size in inches
let customPage = GSKPDFPage(
    filePath: imagePath,
    inchesSize: GSKPDFSize(width: 8.5, height: 14.0)
)

// Fit to image dimensions
let fitPage = GSKPDFPage(
    filePath: imagePath,
    inchesSize: .fit // Maintains original image aspect ratio
)
// Predefined sizes
val a4Page = PDFPage(
    imagePath,
    PDFPage.Size.A4
)

val letterPage = PDFPage(
    imagePath,
    PDFPage.Size.LETTER
)

// Custom size in inches
val customPage = PDFPage(
    imagePath,
    PDFPage.Size.Custom(8.5f, 14.0f)
)

// Fit to image dimensions
val fitPage = PDFPage(
    imagePath,
    PDFPage.Size.FIT // Maintains original image aspect ratio
)

Use Cases

Batch Processing Multiple Documents

Process multiple documents and create separate PDFs:

func processBatch(scanResults: [GSKScanResult]) {
    // Group pages by document
    let documents = groupByDocument(scanResults)

    for (index, document) in documents.enumerated() {
        // Process each document
        let processedImages = processDocument(document)

        // Generate individual PDF
        generatePDF(
            from: processedImages,
            filename: "document_\(index + 1).pdf"
        )
    }
}

Adding Security

Protect your PDFs with passwords:

let document = GSKPDFDocument(
    title: "Confidential Document",
    password: "securePassword123", // User will need this to open PDF
    keywords: nil,
    pages: pdfPages
)

Custom Metadata

Add searchable metadata to your PDFs:

let document = GSKPDFDocument(
    title: "Invoice #12345",
    password: nil,
    keywords: ["invoice", "2024", "accounting", customerName],
    pages: pdfPages
)

Products

Industries

Case Studies

Integration

Company

© 2025 The Grizzly Labs. All rights reserved.