Muitas vezes no dia a dia nos deparamos com situações onde temos um documento escaneado e queremos convertê-lo em texto. Muitos scanners proveem aplicações que fazem essas conversões automaticamente no momento em que um documento é escaneado, porém na maioria das vezes, o formato no qual é gerado o documento é um .pdf, .odt, sendo necessário posteriormente utilizar uma segunda aplicação para realizar a conversão desses formatos para o formato .docx (OpenXML).

O OpenXML por sua vez tornou-se um padrão ISO (IS 29500) e sua adoção cresce a cada dia devido a seu desempenho, padrão aberto, 75% menor que o antigo formato, e baseado em dois padrões muito utilizados: XML e ZIP.

Para facilitar o trabalho de desenvolvedores e evitar a necessidade de integração com aplicações de terceiros, a Microsoft disponibilizou com o Office 2007 uma API que realiza o trabalho de OCR (Optical Character Recognition). É importante lembrar que a API que trabalharemos é exclusiva do Office 2007 (Office 2003 contém sua própria OCR API). Neste artigo criaremos uma aplicação que a utilizará para gerar documentos OpenXML. Em adição utilizaremos a API de Speech Recognition, presente no .NET Framework 3.5, para incrementar a aplicação. A API de Speech Recognition permite que sua aplicação “fale”, como veremos em breve.

Antes de iniciarmos é necessário que estejam instalados os seguintes programas:

Utilizando a biblioteca Microsoft Office Document Imaging (MODI)

É necessário instalar juntamente com o Office 2007 a biblioteca Microsoft Office Document Imaging 12.0 Type Library. Por padrão, o Office 2007 não instala esse componente, então é necessário que a instalação do Office seja executada e para isso clique no botão Add or Remove Features e certifique-se de que o componente esteja instalado. Caso contrário, instale-o para correto funcionamento da aplicação.

No Visual Studio, crie um novo projeto do tipo Windows Application e chame-o de OCRSample. Para utilizar o MODI devemos adicionar referência a esta biblioteca. No Solution Explorer, selecione Add Reference, selecione a guiaCOM e adicione a referência a biblioteca Microsoft Office Document Imaging 12.0 Type Library. Em seguida, crie um Objeto MODI, conforme a Listagem 1.


namespace OCRSample
{
    public partial class Form1 : Form
    {
        /// <summary>
            /// Document Imaging Library
        </summary>
        MODI.Document md;
Listagem 1. Criação do objeto MODI

No construtor da classe, logo após a chamada do método InitializeComponent, instancie o objeto MODI e crie uma lista genérica, que será posteriormente utilizada para armazenar os arquivos que foram pesquisados em um determinado diretório, como mostra a Listagem 2.


public Form1()
{
    InitializeComponent();
    ListFiles=new list<string>();
    md = new MODI.Document();
}
Listagem 2. Criação do método InitializeComponent

Pesquisaremos os arquivos em um diretório utilizando a classe DirectoryInfo, que permitirá manipular os arquivos de um diretório. Nela teremos o método GetFiles, que retorna um array de objetos do tipo FileInfo, e um método chamado SearchFiles, como mostrado na Listagem 3.


private void SearchFiles(string dirName)
{
    //create an instance of DirectoryInfo to selected directory
    Directory DirInfo = new DirectoryInfo(dirName);

    try
    {
        // get files from directory
        FileInfo[] AFileInfo = DirInfo.GetFiles("*.tip", SearchOption.AllDirectories);
        //process the files, add it in the ListView
        foreach (FileInfo FileInfo in AFileInfo)
        {
            ListFiles.Add(FileInfo.FullName);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}
Listagem 3. Classe DirectoryInfo

Crie o form da Figura 1 e na propriedade (Name), altere os seguintes nomes:

  • Button1 (…) para btn_Path
  • Button2 (Search) para btn_Search
  • Button3 (Save) para btn_Save
  • Button4 (Clear) para btn_Clear
  • Button5 (Preview) para btn_Preview
  • Button6 (Convert) para btn_Convert
  • Button7 (Close) para btn_Close
  • textBox1 para txt_SearchPath
  • textBox2 para txt_SavePath
  • textBox3 para txt_Preview
form
Figura 1. Form da aplicação

Na propriedade Multiline da Text Box txt_Preview altere seu valor para True, e a propriedade ScrollBars altere seu valor para Vertical.

Agora adicione ao Form os seguintes controles:

  • openFileDialog;
  • folderBrowserDialog;
  • saveFileDialog.

Dois cliques no botão btn_Path e o evento será gerado e deverá ter o código da Listagem 4.


private void btn_Path_Click(obect sender, EventArgs e)
{
    folderN=browserDialog1.SelectedPath = txt_SearchPath.Text;
    if (folderBrowserDialog1.ShowDialog()==DialogResult.OK)
    {
        txt_SearchPath.Text = folderBrowserDialog1.SelectedPath;
    }
}
Listagem 4. Evento click do botão

O Path de onde estarão os possíveis arquivos será inserido na Text Box txt_SearchPath.

Dois cliques no botão btn_Search e o evento será gerado e deverá ter o código da Listagem 5.


private void btn_Search_Click(object sender, EventArgs e)
{
    if (txt_SeachPath.Text!="")
    {
        ListFiles = new List<string>();
        SearchFiles(txt_SearchPath.Text);
        checkedListBox1.Items.Clear();
        foreach (string File in ListFiles)
        {
             checkedListBox1.Items.Add(File);
        }
    }
    
}
Listagem 5. Evento click do botão btn_Search

Esse evento adicionará ao controle checkedListBox uma lista com os arquivos encontrados no diretório. Para agilizar o processo de filtragem, na propriedade Filter do controle openFileDialog escreva a seguinte sentença: TIFF files|*.tif|All files|*.* . Estaremos procurando imagens no formato .tif, porém pode-se utilizar os formatos .jpg como filtro para pesquisa.

Convertendo Imagem para Texto

Implementaremos agora o método que converterá as imagens TIFF em texto. Crie um método sem retorno chamado OCRImplementation e insira o código da Listagem 6.


private void OCRImplementation()
{
    Cursor = Cursors.WaitCursor;
    foreach (string Name in checkedListBox1.CheckedItems)
    {
        try
        {
            md.Create(Name);
            md.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, true, true);
            string strText = String.Empty;

            MODI.Image image = (MODI.Image)md.Images[0];
            MODI.Layout layout = image.Layout;

            for (int i = 0; i < layout.Words.Count; i++)
            {
                MODI.Word word = (MOBI.word)layout.Words[i];
                if (strText.Length > 0)
                {
                    strText += " ";
                }
                strText +=word.Text;
            }
            md.Close(false);
            Createdocument(strText);
        }
        catch (Exception ex)
        {
            Message.Box.Show(ex.Message);
        }
        finally
        {
            Cursor = Cursors.Default;
        }
    }
}
Listagem 6. Método OCRImplementation

O método Create do objeto md presente na Listagem 7 recebe o path do arquivo a ser convertido. Faremos então a chamada ao método OCR, que recebe três parâmetros: o primeiro representa o idioma ou língua do documento, o segundo parâmetro especifica se o processo do método determinará a orientação da página e o terceiro parâmetro indicará se o processo corrigirá desalinhamentos dos ângulos verticais da página.


    md.Create(Name);
    md.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, true, true);
Listagem 7. Método Create

Após a chamada ao método OCR, o texto passa a estar disponível para formatação, mas para "pegá-lo" é necessário adicionar referências às propriedades dos objetos Image e Layout, como mostra a Listagem 8.


    MODI.Image image = (MODI. Image)md.Images[0];
    MODI.Layout layout = image.Layout;
Listagem 8. Propriedades dos objetos Image e Layout

O objeto Layout permite que o texto seja resgatado. A propriedade Words deste objeto contém a propriedade Count, que permite a iteração pela lista de palavras. Pode-se resgatar cada palavra separadamente pela propriedade Words através de um index, porém faremos uma iteração por todas as palavras e adicionaremos um espaço em branco para separá-las umas das outras.

O método Close do objeto md recebe um único argumento boolean indicando se o mesmo deve salvar as alterações feitas no arquivo da imagem, como mostra a Listagem 9.


md.Close(false);
Listagem 9. Método Close

Repare que todo o trabalho de conversão é realizado pelo método OCR. Ao dar dois cliques no botão btn_Convert, o evento será gerado, e devemos escrever nele o código da Listagem 10.


private void btn_Cponvert_Click(object sender, EventArgs e)
{
    OCRImplementation();
}
Listagem 10. Evento click do botão btn_Convert

Criando um documento OpenXML

No Solution Explorer clique em Add Reference e adicione referência a duas bibliotecas: DocumentFormat.OpenXML e WindowsBase. Essas bibliotecas permitirão que o texto proveniente do OCR seja transformado em um documento OpenXML (.docx). No escopo de classe crie uma constante com o valor presente na Listagem 11.


/// <summary>
/// OpenXML document part reference and structure template
/// </summary>
private const string PART_TEMPLATE = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>" +
    "<w:document xmlns=w'http://schemas.openxmlformats.org/wordprocessingml/2006/main'>" +
    "<w:body><w:p><w:r><w:t>#REPLACE#</w:t></w:r></w:p></w:body></w:document>";
Listagem 11. Referenciando as bibliotecas OpenXML

Essa constante definirá a estrutura e os relacionamentos internos do documento. Criaremos agora um método sem retorno chamado de CreateDocument, que receberá como parâmetro uma string contendo o texto proveniente do OCR, como mostra a Listagem 12.


private void CreateDocument(string Text)
{
    WordprocessingDocument wordDoc = 
        WordprocessingDocument.Create(txt_SavePath.Text, WordprocessingDocumentType.Document);
    MainDocumentPart docPart = wordDoc.MainDocumentPart;
    string partML;

    docPart = wordDoc.AddMainDocumentPart();
    partML = PART_TEMPLATE.Teplace("#REPLACE#", Text);

    Stream partStream = docPart.GetStream();
    UTF8Encoding encoder = new UTF8Encoding();

    Byte[] buffer = encoder.GetBytes(partML);
    partStream.Write(buffer, 0, buffer.Length);

    wordDoc.Close;
}
Listagem 12. Método CreateDocument

Perceba que estamos utilizando o SDK v1.0, por consequência o esforço para criar um documento OpenXML foi bastante reduzido. O objeto WordprocessingDocument define o pacote que representa um documento Word. O objeto MainDocumentPart é responsável por definir a parte principal de um documento.

Já o método AddMainDocumentPart é responsável por adicionar a parte principal a estrutura de um documento.

A string proveniente do OCR substituirá a palavra REPLACE no método Replace, moldando o texto no formato WordprocessingML.

Implementando o Speech Recognition

O Speech Recognition foi lançado para melhorar a experiência de usuário e permitir o acesso a informações de forma mais natural e eficiente. Provê poderosas APIs e ferramentas para construção de aplicações speech-enabled. Utilizaremos a Speech API de forma mais simples, porém a mesma pode ser extendida para uma maior abrangência em suas aplicações.

O Speech acompanha por padrão o Windows Vista e sua API está disponível a partir da versão 3.0 do .NET Framework. No Solution Explorer adicione referência ao Namespace System.Speech e no escopo da classe crie um objeto do tipo SpeechSynthesizer, como mostra a Listagem 13.


/// <summary>
/// synthesys speach
</summary>
SpeechSynthesizer speaker = new SpeachSynthesizer();
Listagem 13. Objeto SpeechSynthesizer

No construtor da classe inicialize as propriedades Rate, que define o tempo em que a sentença será executada, e Volume, que define o nível do som, como mostra a Listagem 14.


public Form1()
{
    InitializeComponent();

    speaker.Rate = -2;
    speaker.Volume = 100;
Listagem 14.

No evento btn_Search_Click, adicione a chamada ao método Speak antes da instrução If. Esse método aceita como parâmetro uma string que será executada (falada) durante tempo de execução, como vemos na Listagem 15.


    private void btn_Search_Click(object sender, EventArgs e)
    {
        speaker.Speak("Searching";)
Listagem 15. Evento btn_Search_Click

Implementaremos agora o método OCRImplementationPreview, que é bem parecido com o método OCRIMplementation. A única diferença é que o mesmo, ao invés de realizar a conversão e chamar o método CreateDocument, exibirá na Text Box txt_Preview a prévia do documento gerado posteriormente, como mostra a Listagem 16.


private void OCRImplementationPreview()
{
    Cursor = Cursor.WaitCursor;
    foreach (string Name in checkedListBox1.CheckedItems)
    {
        try
        {
            md.Create(Name);
            md.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, true, true);
            string strText = String.Empty;

            MODI.Image image = (MODI.Image)md.Images[0];
            MODI.Layout layout = image.Layout;

            for (int i = 0; i < layout.Words.Count; i++)
            {
                MODI.Word word = (MODI.Word)layout.Words[i];
                if (strText. Length>0)
                {
                    strText+=" ";
                }
            }
            strText += word.Text;
        }
        md.Close(false);
        txt_Preview.Text = strText;
        DialogResult button = MessageBox.Show("Do you want to activate speech recognition?", 
        "Speech Recognition", MessageBoxButtons.YesNo);
        if (button==DialogResult.Yes)
        {
            speaker.Speak(strText);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        Cursor = Cursors.Default;
    }
}
Listagem 16. Método OCRIMplementation

Perceba que utilizamos o Speech Recognition para a leitura do preview. Uma MessageBox é exibida para ativação ou não da leitura da Text Box. Em seguida, dois cliques no botão btn_Save_Click e o evento será gerado e deverá ter o código da Listagem 17.


    private void btn_Save_Click(oject sender, EventArgs e)
    {
        System.Windows.Forms.DialogResult result;
        result = saveFileDialog1.ShowDialog();
        if (result==System.Windows.Forms.DialogResult.OK)
        {
            txt_SavePath.Text = saveFileDialog1.FileName;
        }
    }
Listagem 17. Evento btn_Save_Click

O path e nome do arquivo a ser gerado será exibido na Text Box txt_SavePath, pois no momento em que o usuário clicar no botão Convert, o evento OCRImplementation será chamado e a imagem convertida será salva como um documento OpenXML.

Dois cliques no botão btn_Clear_Click e o evento será gerado, tendo como código a Listagem 18. Esse evento fará com que todos os campos da aplicação sejam limpos.


private void btn_clear_Click(object sender, EventArgs e)
{
    if (txt_SearchPath.Text!="")
    {
        txt_SearchPath.Text="";
        checkedListBox1.Items.Clear();
        txt_Preview.Text = "";
    }
}
Listagem 18. Botão btn_Clear_Click

Agora, dois cliques no botão btn_Close_Click, o evento gerado deverá ter o código da Listagem 19. Assim encerraremos a nossa aplicação.


    private void btn_Close_click(object sender, EventArgs e)
    {
        Application.Exit();
    }
Listagem 19. Botão btn_Close_Click

Executando a aplicação

No menu Debug, selecione Start Without Debugging (Ctrl+F5) e a tela da aplicação será exibida. Clique nas reticências (...) e selecione o local dos arquivos a serem procurados (crie previamente um arquivo no Paint com o texto “Testing Office Character Recognition”), como mostra a Figura 2.

Procurando os arquivos
Figura 2. Procurando arquivos
Compilação x86: O componente MODI não será instanciado se sua aplicação for compilado pela runtime 64-bit do .NET. Para assegurar que sua aplicação possa instanciar o componente será necessário configurar o projeto para compilar em uma CPU x86, ao invés de Any CPU, como é marcado por padrão no Visual Studio.

Em seguida clique no botão Search e a lista de arquivos com a extensão .tif será exibida, como mostra a Figura 3.

Lista de arquivos .tif
Figura 3. Lista de arquivos .tif

Selecione um arquivo e clique no botão Preview para a message Box aparecer desejando ativar o Speech Recognition. Clique em OK e o texto será lido, como vemos na Figura 4.

Ativando o Speech Recognition
Figura 4. Ativando o Speech Recognition

Selecione o Path onde deseja salvar o documento clicando no botão Save. Em seguida clique no botão convert e o documento .docx será gerado, como vemos na Figura 5.

Convertendo o documento
Figura 5. Convertendo o documento

Clique no botão Clear para reiniciar o processo e converter mais algum arquivo, ou em Close para encerrar a aplicação.

Conclusão

O código para a implementação do Office OCR é de certa forma curto se comparado com APIs providas por terceiros. É uma ferramenta muito poderosa que pode ser explorada de várias formas, e integrada com os benefícios do OpenXML e do Speech Recognition. Juntas trazem uma riqueza maior à aplicação que as utiliza.