Overview

Kofax Capture Advanced Scan Api: A first approach

7 Comments

Kofax Capture Advanced Scan Api: A first approach

The following article shows a possible use case in using the new scan api, coming with SP1 of Kofax Capture 10.

A sample application can be found at the Source directory of the Kofax Capture installation under …\Source\Sample Projects\StdCust\ScanApiSamplePanel.

The business case we want to solve is described as follows:

We want to scan duplex and for document separation we use patch codes. For a following custom module we need to flag all pages with a CustomStorageString. If the tiff was grabbed by the front camera we want to set its value to “0” and to “1” if the tiff was grabbed by the rear camera.

To preprocess this information we need to use the new available event KfxOcxEventScanPageEnd.

For each scanned page this event is fired. This applies also to the pages containing the patch code. During that time the tiff file is not even placed in the image folder of the batch. When this event is fired you can use the functions GetKImgp() and GetKScan() of the AscentCaptureModule.Application object.

If you work with c# then your project has to be at least .NET 4. The reason is the dynamic language runtime and the use of the DynamicObject class.
dynamic kimgp = _kofaxApplication.GetKImgp();
kimgp.LifeIsGood();

kimgp is marked as dynamic and the compiler does not know the type of it. He also does not know of its member LifeIsGood(). Only at runtime this method is resolved based on the actual object! It is comparable to the late binding in VB with CreateObject() . If you coded wrongly kimgp.LifeIsGoo(); then this will only cause an error at runtime – the compiler does not mark this line as faulty!

Moreover that means that you don’t know about the properties or methods of these objects during design time. No IntelliSense can be used. But there are two documents (Language Reference and Programmers Guide) available that give detailed information at: http://www.kofax.com/support/products/imagecontrols/3.1/

Coming back to the use case. We first created a small helper class to persist the information of each scanned page:

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

Then we need a generic list containing instances of this class:

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

Additionally we need a counter for the current page that needs to be reset to zero if a new batch is opened.

private int _pageNumber;

Here follows the event in the ActionEvent function:

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;

After the last page was scanned we have all the information persisted for all scanned pages during the scan process.

In the KfxOcxEvent.KfxOcxEventScanStopped event we need to start a timer for the subsequent process. We have to do it with a Timer object, as we cannot do this work in the scope of the ActionEvent function.

     …
     …
     …
         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");
                }
            }
        }
    }

The function IsFrontPage helps to detect if the page was a front page or not. We need to deal with the circumstance, that we probably have more events than pages, as an event of a scanned page with a patchcode is used for document separation but then this page is not part of the batch. That is why we needed to detect if the page contained a patchcode.

    
    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;
    }

An example:

We scan two documents (duplex scanning) separated by patchcodes. The first document contains one sheet and the second document conatains two sheets:

 

 

 

 

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.

Another example:

We scan the same documents (duplex scanning) separated by patchcodes but let VRS delete blank pages.

If blank page deletion in duplex mode is used then there are no events fired for this deleted pages.

 

 

 

 

 

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.

Unfortunately we have limitations of this code so far. It will only work from scratch, which means only scanning in an empty batch. The code can easily be extended for using it with scanning more times in one single batch. But what becomes more difficult is how to deal with replacing an existing page. Imagine that the page to be replaced is a back page. Therefore the page is put into the scanner and is scanned as a front page with the front camera! You also get two events for the front and the rear camera but only the front page replaces the back page. To solve this issue you could make a snapshot of the batch, its documents and pages with information about file size and the last write time before the scanning of a new page or batch is about to start. Corresponding events are available to take this snapshot. Then you could detect a replaced page by comparing the snapshot before scanning and after scanning.

At least I would like to add that it is worth to take a look at the example source code. The CustomScanApi Xml interface allows you to to create documents or to specify the label of a document e.g. the content of a barcode in the event KfxOcxEventScanPageEnd . And it is possible to work with scanner profiles.

In the end I would like to mention that I tested the api with a high speed scanner (170 ppm, DIN A4, duplex) without running into an error.

BatchGenerator

Stefan Blank

I have 25 years of professional experience in fields of document and input management. As a Kofax Certified Capture Consultant I offer comprehensive knowledge of the Kofax products and technologies. The focus of my work is determination and evaluation of customer’s demands and responsibility for conception and realisation based on the possibilities the range of Kofax products offers (customising and software development).

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

Kommentare

  • Hayden

    4. March 2014 von Hayden

    Thanks for the info, Stefan.
    Have you played with 10.1 now that the above code has effectively been made available in there?
    Thanks for the hint to look at CustomScanApi in v10, too, as that now has me coming up with all sorts of new ways to mimick functionality of other scan apps that Kofax couldn’t do – like put documents into folders based on a patch sheet or barcode.
    Thanks
    – Hayden.

    • Stefan Blank

      Hello Hayden,

      thank you for your reply. I have played with 10.1’s api to build a dummy workflow agent, but not in conjunction with the CustomScanApi. With 10.1 the api is full .NET (v4.0), but you can use the old one as well. If there is a bit more time I will give it a try. Definitely interesting aspects for customising/scripting, e.g. SBL is marked as deprecated.

      Kind regards from Germany

      Stefan

      • Hayden

        6. March 2014 von Hayden

        Hi Stefan. I was thinking about 10.1 more with the information about front & back pages being held in there. We don’t want to put a patch sheet between single sheets of paper, but if they are double-sided, we currently have no choice. Knowing front & back will allow us to code to a sheet rather than a page. Wondering if it replaces your hard work above?

        And with your second example – “let VRS delete blank pages”, is the back page of all your examples blank? Or would your code handle some blank, some not? Like: (see event 3)

        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=0 FrontCamera=False -> Document 1, page 2.
        Event 4 PatchType=2 FrontCamera=True -> Deleted in case of document separation.
        Event 5 PatchType=0 FrontCamera=True -> Document 2, page 1.
        Event 6 PatchType=0 FrontCamera=True -> Document 2, page 2.

        • Stefan Blank

          6. March 2014 von Stefan Blank

          Hello Hayden,

          well, my example comes with patchcodes, but you can use the information provided by the api for everything you want. If you have a batch containing documents with the same number of physical sheets, then your separation method will be “fixed pages”. We had a business case with patchcode separation. I haven’t already understood your business case. Tell me more about what you are trying to do.

          Coming to your VRS question. If there are backpages with content, then VRS would not delete them and the code would still work correctly. I am just logging the events that are fired by Kofax! The difference between deletion of empty backpages by VRS or Kofax using a threshold is that VRS deletes them earlier and so NO events are fired for deleted pages whereas Kofax deletes the pages later and you receive an event for it. That makes it harder after the KfxOcxEventScanStopped event. In that case you have more events than pages in your batch and that makes it tricky to assign the information of a scanned page to the physical pages in your batch.

          Event 1 FrontCamera=True -> Document 1, page 1.
          Event 2 FrontCamera=False Bytesize > Threshold -> Document 1, page 2.
          Event 3 FrontCamera=True-> Document 2, page 1.
          Event 4 FrontCamera=False Byte Size < Threshold -> Will be deleted later by Kofax! You don’t know this right at the moment the event is fired!
          Event 5 FrontCamera=True -> Document 3, page 1.
          Event 6 FrontCamera=False Byte Size < Threshold -> Will be deleted later by Kofax! You don’t know this right at the moment the event is fired!

          At the end of the scanning process you have:
          Batch
          Document 1
          Page 1
          Page 2
          Document 2
          Page1
          Document 3
          Page1

          You could make a matching, but that would only work with fixed pages separation and the demand that one document takes one sheet (maximum two pages per document)! If a document would consist of two sheets and up to four pages you would not be able to match the information of the events to the physical pages of a doument. Imagine that one document would have three ohysical pages after scanning. One backpage was empty and deleted but four events fired!

          Page 1 is definetly coming from the front camera, but page 2 could be the backpage of the first sheet or the first page of the second sheet.

          If you would tell me more about your technical background and business case I could try to give you an advice what to do and what is possible. You can contact me directly: stefan.blank@codecentric.de.

          Kind regards

          Stefan

          Therefore it is better to use VRS for blank page deletion.

          I will give it a try later today, if they probably changed this unlucky behaviour.

  • Sundar

    8. April 2014 von Sundar

    Hi,

    thanks in advance,

    I want to set the default value(20) pages per batch in kofax scan module.I want to customize this part.I dont know where i have to start. Please help me on this.

    somehow i can able to write one function but i am not sure the way i wrote is correct or not

    Public Sub setScanDefaultValue()
    Dim _pagesperBatch As Integer = 20
    Dim _objBatch As Kofax.AscentCaptureModule.Batch
    _objBatch = New Kofax.AscentCaptureModule.Batch
    _objBatch.PagesPerBatch = _pagesperBatch
    End Sub

    thanks
    sundar

    • Stefan Blank

      8. April 2014 von Stefan Blank

      Hi Sundar,

      I haven’t already understood what you are planning to do. PagesPerBatch is for page separation and you want to make a new document after 20 pages, right? Therefore you have to use PagesPerDoc.

      Please have a look in the Programmers Guide and the Kofax Api Reference Guide. You first need to implement a property Application. You get the KofaxApplication object at runtime when your panel is loaded.
      The you do something like this:

      Dim myBatch As Kofax.AscentCaptureModule.Batch

      myBatch = myKofaxApplication.CreateBatch(batchClassName, batchName)
      myBatch.PagesPerDoc = 20

      Is that useful? If not, please describe what exactly you are trying to do.

      Kind regards

      Stefan

      • sundar

        14. April 2014 von sundar

        Hi,

        I want to set 20 pages per batch by default. When user clicking the scan module by default pages per batch textbox show as 20. this is my requirement.

        thanks
        sundar

Comment

Your email address will not be published. Required fields are marked *