Kofax Capture erweiterte Scan Api: Eine erste Annäherung

Keine Kommentare

Kofax Capture erweiterte Scan Api: Eine erste Annäherung

Der folgende Artikel zeigt einen möglichen Anwendungsfall für die Anwendung der neuen Api Erweiterung für das Scan-Modul, die mit SP1 von Kofax Capture 10 zur Verfügung steht. Eine Beispielanwendung des Herstellers liegt im Quellcode vor und ist im bekannten Verzeichnis zu finden unter …\Source\Sample Projects\StdCust\ScanApiSamplePanel. Der Anwednungsfall, den wir lösen wollten ist wie folgt beschrieben:
Wir wollen duplex scannen und trennen die Dokumente per Patchcode. Für ein folgendes CustomModule müssen wir alle Seiten mit einem CustomStorageString versehen, der aussagen soll, ob die Seite von der Vorder- oder Rückseitenkamera aufgenommen wurde. Falls das Tiff von der Vorderseitenkamera aufgenommen wurde, setzen wir den Wert auf „0“, andernfalls auf „1“.
Um diese Information aufbereiten zu können wird auf das neue Event KfxOcxEventScanPageEnd zurückgegriffen. Für jede gescannte Seite wird dieses Event gefeuert, bevor die Seite auf der Festplatte abgelegt wird. Das gilt beispielsweise auch für die Seiten, die einen Patchcode enthalten. Wie gesagt, zu dieser Zeit befindet sich die Seite noch im Speicher und ist nicht im Images Verzeichnis des Stapels abgelegt. Hier hat man nun die Möglichkeit über die Funktionen GetKImgp() und  GetKScan()des AscentCaptureModule.Application Objektes auf erweiterte Informationen zuzugreifen.

Sollten Sie mit c# arbeiten, dann muss ihr Projekt mindestens gegen das .NET Framework 4 gehen, auch wenn offiziell nur .NET 3.5 für Kofax  freigegeben ist.  Der Grund ist die DLR (Dynamic Language Runtime) und die Verwendung der Klasse DynamicObject.

dynamic kimgp = _kofaxApplication.GetKImgp();
kimgp.LifeIsGood();

kimgp ist als dynamisch gekennzeichnet und somit kennt der Compiler zur Entwurfszeit nicht den Typ der Deklaration. Der Compiler kennt nicht mal das Member LifeIsGood(). Erst zur Laufzeit wird die Methode aufgelöst, basierend auf dem aktuellen Objekt. Vergleichbar ist das mit dem LateBinding aus VB via CreateObject() . Falls man fälschlicherweise kimgp.LifeIsGoo();  kodiert, so wird das erst einen Fehler zur Laufzeit auslösen. Für uns bedeutet das aber auch, dass man die Eigenschaften und Methoden des Objektes nicht genau kennt, da man auch auf das Feature IntelliSense nicht zurückgreifen kann. Aber es gibt zwei Dokumentationen, die hier Abhilfe schaffen (Language Reference and Programmers Guide). Diese sind verfügbar unter: http://www.kofax.com/support/products/imagecontrols/3.1/

Kommen wir zurück zum Anwendungsfall. Zuerst haben wir eine einfache Behelfsklasse erstellt mit der wir die Informationen jeder gescannten Seite persistieren wollen:

class PageFlag
{
    public bool IsPatch { get; private set; }
    public bool IsFrontPage { get; private set; }
    public PageFlag(bool isPatch, bool isFrontPage)
    {
        IsPatch = isPatch;
        IsFrontPage = isFrontPage;
    }
}

Dann machen wir uns eine GenericList zu Nutze.

using System.Collections.Generic;
private List<PageFlag> _pageList = new List<PageFlag>();

Zu guter Letzt brauchen wir eine Art Zähler für die jeweilige Seite. Dieser muss auf Null zurückgesetzt werden, wenn ein neuer Stapel geöffnet wird.

private int _pageNumber;

Es folgt nun das zuvor erwähnte Event in der ActionEvent Funktion:

case (int)KfxOcxEvent.KfxOcxEventScanPageEnd:
    _pageNumber += 1;
    bool frontPage = false;
    try
    {
        dynamic kscan = _kofaxApplication.GetKScan();
        frontPage = kscan.PEFront;       
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        Marshal.ReleaseComObject(kscan);
    }
 
    try
    {
        dynamic kimgp = _kofaxApplication.GetKImgp();
        int patchCodeType = kimgp.PEPatchCode;
    }  
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        Marshal.ReleaseComObject(kimgp);
    }
    try
    {
        // we know that we will only get the PatchCode Type 2 (set in the batchclass)
        _pageList.Add(new PageFlag(patchCodeType==2, frontPage));
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    break;

Nachdem die letzte Seite gescannt wurde, haben wir alle Informationen für alle gescannten Seiten während des Scan-Vorgangs persistiert. Im Event KfxOcxEvent.KfxOcxEventScanStopped müssen wir nun mittels eines Timer Objektes eine Folgeverarbeitung anstoßen. Leider müssen wir das über diese Art und Weise regeln und können das nicht direkt in der ActionEvent Funktion bewerkstelligen.

     …
     …
     …
         case (int)KfxOcxEvent.KfxOcxEventScanStopped:
             timerScanStopped.Enabled = true;
             break;
     …
     …
     …
    private void timerScanStopped_Tick(object sender, EventArgs e)
    {
        timerScanStopped.Enabled = false;
        foreach (Kofax.AscentCaptureModule.Document document
                  in _kofaxApplication.ActiveBatch.Documents)
        {
            int index = 0;
            foreach (Kofax.AscentCaptureModule.Page page in document.Pages)
            {
                index += 1;
                if (IsFrontPage(index))
                {
                    page.set_CustomStorageString("Paradatec-Backside", "0");
                }
                else
                {
                    page.set_CustomStorageString("Paradatec-Backside", "1");
                }
            }
        }
    }

Die Funktion IsFrontPage hilft zu erkennen, ob die Seite von der Vorderseitenkamera stammt oder nicht. Hier müssen wir dem Umstand Genüge tragen, dass wir wahrscheinlich mehr Events bekommen, als Seiten in dem Stapel sind, da auch die Events für Seiten mit Patchcode gefeuert wurden, diese Seiten aber nicht im Stapel enthalten sind. Deshalb müssen wir die Information, ob die Seite einen Patchcode enthielt, berücksichtigen.

    
    private bool IsFrontPage(int pageIdx)
    {
        bool previousPatch = false;
        int idx = 0;
        for (int i = 0; i < _pageList.Count; i++)
        {
            if (previousPatch)
            {
                if (_pageList[i].IsPatch==false)
                {
                    if (_pageList[i].IsFrontPage)
                    {
                        idx += 1;
                        if (idx==pageIdx)
                        {
                            return _pageList[i].IsFrontPage;
                        }
                    }
                    previousPatch = false;
                }
            }
            else
            {
                if (_pageList[i].IsPatch)
                {
                    previousPatch = true;
                }
                else
                {
                    idx += 1;
                    if (idx == pageIdx)
                    {
                        return _pageList[i].IsFrontPage;
                    } 
                }        
            }        
        }
        return false;
    }

Ein Beispiel: Wir scannen zwei Dokumente (duplex) getrennt über Patchcodes. Das erste Dokument besteht aus einem Blatt und das zweite Dokument besteht aus zwei Blättern.:

 Events fired:

Event 1 PatchType=2 FrontCamera=True    -> Deleted in case of document separation.
Event 2 PatchType=0 FrontCamera=False   -> Deleted in case of document separation.
Event 3 PatchType=0 FrontCamera=True    -> Document 1, page 1.
Event 4 PatchType=0 FrontCamera=False   -> Document 1, page 2.
Event 5 PatchType=0 FrontCamera=True    -> Deleted in case of document separation.
Event 6 PatchType=0 FrontCamera=False   -> Deleted in case of document separation.
Event 7 PatchType=2 FrontCamera=True    -> Document 2, page 1.
Event 8 PatchType=0 FrontCamera=False   -> Document 2, page 2.
Event 9 PatchType=0 FrontCamera=True    -> Document 2, page 3.
Event 10 PatchType=0 FrontCamera=False  -> Document 2, page 4.

Ein weiteres Beispiel: Wir scannen dieselben Dokumente (duplex) getrennt über Patchcodes aber lassen VRS leere Rückseiten enternen. Werden leere Seiten über VRS gelöscht, werden dafür keine Events gefeuert.

 Events fired:

Event 1 PatchType=2 FrontCamera=True    -> Deleted in case of document separation.
Event 2 PatchType=0 FrontCamera=True    -> Document 1, page 1.
Event 3 PatchType=2 FrontCamera=True    -> Deleted in case of document separation.
Event 4 PatchType=0 FrontCamera=True    -> Document 2, page 1.
Event 5 PatchType=0 FrontCamera=True    -> Document 2, page 2.

Leider gibt es Einschränkungen mit dem dargestellten Code. Er funktioniert perfekt, wenn man in einen leeren Stapel scannt, allerdings kann der Code einfach erweitert werden, um auch das mehrfache Scannen hintereinander in einen Stapel zu unterstützen. Schwieriger wird es aber, die Funktion des Ersetzens einer einzelnen vorhandenen Seite zu unterstützen oder gar das Einfügen von Seiten. Stellen wir uns vor, eine Rückseite muss neu gescannt werden. Dazu legt man die Rückseite auf den Scanner und scannt diese mit der Vorderseitenkamera. Man erhält zudem zwei Events, für die Vorder- und Rückseite, aber nur die Vorderseite ersetzt die Rückseite. Es ist nachher unsere Aufgabe, die ersetzte Seite ausfindig zumachen und zu flaggen (CustomStorageString). Hier könnte man dann vor dem Scannen einer Seite oder eines Stapels einen Snapshot erstellen, der mir Informationen bereitstellt für jedes Dokument und jede Seite im Stapel mit Dateigröße und des Zeitstempels der letzten Änderung. Entsprechende Events sind hierfür vorhanden. Dasselbe würde man dann nach dem Scannen machen und könnte dann relativ sicher die ersetzte Seite ausmachen.

Im Übrigen lohnt es sich, einen Blick in die Beispielanwendung des Herstellers Kofax zu werfen. Die neuen Möglichkeiten sind größer, als hier beschrieben. So kann man über ein CustomScanApi Xml Interface Dokument erstellen oder die Labels für die Dokumente in der Stapelansicht verändern, zum Beispiel auf den Inhalt eines Barcodes. Außerdem kann man mit Scannerprofilen arbeiten und vieles mehr.

Zu guter Letzt möchte ich noch erwähnen, dass der oben geschilderte Anwendungsfall von uns mit einem High-Speed-Scanner (170 ppm, DIN A4, duplex) getestet wurde, ohne dass es hier zu Fehlern kam.

BatchGenerator

Stefan Blank

Ich blicke auf eine über 25-jährige Erfahrung im Bereich Dokumenten- und Input-Management zurück. Als Kofax Certified Capture Consultant biete ich eine umfassende Kenntnis der Kofax-Produktpalette und -Technologien. Schwerpunkt meiner Tätigkeit ist dabei die Ermittlung und Bewertung von Kundenanforderungen und die Verantwortung für Konzeption und Realisierung basierend auf den Möglichkeiten des Standard-Software-Produkts (Customising und Software-Entwicklung).

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.