SRC: QA Извлечение текста документов (IFilter)
От: Kaa Украина http://blog.meta.ua/users/kaa/
Дата: 08.08.02 10:39
Оценка: 135 (14)
Часто спрашивают, как получить текст из Ворда, Екселя, PDF.

Часто отвечаю, что сложно, но есть способ простой, и зовут его IFilter. Давно думаю, что надо QA написать, но постоянно руки не доходят. Посему, пока руки не доходят и в этот раз, публикую текст, который даст представление о том, как этот механизм можно использовать. Сразу скажу, что использовать его можно только на Win2000 или WinNT, но для последней нужны некие приблуды, коими она по умолчанию не обладает (Option Pack).

Данная утилита вынимает текст в UNICODE из файла, переданного в качестве параметра (если для класса файлов, к которому документ принадлежит, установлен фильтр), и выдает его либо в файл, либо в стандартный вывод.


////////////////////////////////////////////////////////////////////////
//[] Filename   : filterDoc.h
////////////////////////////////////////////////////////////////////////

# ifndef _filterDoc_h_
#   define _filterDoc_h_

# include <windows.h>
# include <ntquery.h>
# include <filter.h>
# include <filterr.h>
# include <atlbase.h>
# include <atlconv.h>

bool ReallocBuffer( LPVOID& lpDst, ULONG ulDst, LPVOID lpSrc, ULONG ulSrc )
{
  if ( 0 == ulDst )
    lpDst = NULL;
  else
  {
    lpDst = CoTaskMemAlloc( ulDst );
    if ( NULL == lpDst )
      return false;
  }
  
  if ( NULL != lpSrc ) 
  {
    if ( 0 != ulSrc )
      CopyMemory( lpDst, lpSrc, (ulDst < ulSrc) ? ulDst : ulSrc );
    CoTaskMemFree( lpSrc );
  }
  
  return true;
}

HRESULT FilterDocument( const WCHAR* lpwSrc, LPVOID& lpvData, ULONG& ccvData )
{
  lpvData = NULL;
  ccvData = 0;

  if ( NULL == lpwSrc || 0 == *lpwSrc )
    return E_INVALIDARG;

  CComPtr<IUnknown> pUnk    = NULL;
  CComPtr<IFilter>  pFilter = NULL;
  HRESULT           hr      = LoadIFilter( lpwSrc, NULL, (void**)&pUnk );
  LPVOID            lpv     = NULL;
  ULONG             ccv     = 0x10000;

  if ( S_OK != hr || NULL == (IUnknown*)pUnk )
    return E_FAIL;

  hr = pUnk->QueryInterface( IID_IFilter, (void**)&pFilter );
  if ( S_OK != hr || NULL == (IFilter*)pFilter )
    return hr;

  ULONG lFlags = 0;
  hr = pFilter->Init(
    IFILTER_INIT_CANON_PARAGRAPHS       |
    IFILTER_INIT_CANON_HYPHENS          | 
    IFILTER_INIT_CANON_SPACES           |
    IFILTER_INIT_APPLY_INDEX_ATTRIBUTES |
    IFILTER_INIT_INDEXING_ONLY, 
    0, 
    NULL,
    &lFlags );
  if ( S_OK != hr )
    return hr;

  if ( ! ReallocBuffer( lpv, ccv, NULL, 0 ) )
    return E_OUTOFMEMORY;

  STAT_CHUNK chunk = { 0 };
  chunk.breakType = CHUNK_EOP;

  WCHAR  wbBuf[0x1000]  = { 0 };
  ULONG  lSize          = sizeof(wbBuf)/sizeof(wbBuf[0]);
  ULONG  lRes           = 0;
  DWORD  dwWritten      = 0;

  USES_CONVERSION;

  for ( ; ; )
  {
    hr = pFilter->GetChunk( &chunk );

    if ( FILTER_E_END_OF_CHUNKS == hr )
      break;
    switch ( hr )
    { 
    case FILTER_E_EMBEDDING_UNAVAILABLE :
    case FILTER_E_LINK_UNAVAILABLE      : continue;
    case FILTER_E_PASSWORD              :
    case FILTER_E_ACCESS                : return E_FAIL;
    default                             : break;
    }

    for ( ; ; )
    {
      lRes = lSize;
      hr = pFilter->GetText( &lRes, wbBuf );

      if ( FILTER_E_NO_TEXT == hr         // the chunk contains no text
        || FILTER_E_NO_MORE_TEXT  == hr ) // the chunk contains no more text
        break;

      wbBuf[lRes] = 0;
      if ( ccv < ( lRes + dwWritten ) * 2 )
      {
        LPVOID  lpvT  = NULL;
        ULONG   ccvT  = ( lRes + dwWritten ) * 2 + 0x10000;
        if ( ! ReallocBuffer( lpvT, ccvT, lpv, dwWritten * 2 ) )
        {
          ReallocBuffer( lpv, 0, lpv, ccv );
          return E_FAIL;
        }
        lpv = lpvT;
        ccv = ccvT;
      }

      CopyMemory( (WCHAR*)lpv + dwWritten, wbBuf, lRes * 2 );
      dwWritten += lRes;
      ((WCHAR*)lpv)[dwWritten] = 0;

      if ( FILTER_S_LAST_TEXT == hr ) /// This was last text fragment in chunk
        break;
    }

  }

  lpvData = lpv;
  ccvData = dwWritten * 2;

  return S_OK;
}

# endif // _filterDoc_h_


////////////////////////////////////////////////////////////////////////
//[] Filename   : flttest.cpp
//[] Description: Contains an entry point of IFilter tester.
////////////////////////////////////////////////////////////////////////

# ifdef _MSC_VER
#   pragma comment ( lib, "ntquery.lib" )
# endif

# include <atlbase.h>
# include <atlconv.h>
# include <io.h>
# include <fcntl.h>
# include <stdio.h>
# include "filterDoc.h"

static char gszAbout[] = 
  "RSDN Group (R) QnA on IFilter Code Sample\n";
static char gszUsage[] = 
  "Usage: flttest [srcfile [dstfile]]\n";

int main( int argc, char* argv[] )
{
  FILE* os  = stdout;
  int   err = 0;

  fprintf( stderr, gszAbout );

  if ( argc == 1 || ( argc > 1 && 0 == *argv[1] ) )
  {
    fprintf( stderr, gszUsage );
    return 0;
  }
  if ( argc >= 3 && 0 != *argv[2] )
  {
    os = fopen( argv[2], "wb" );
    if ( NULL == os )
      os = stdout;
  }

  if ( os == stdout )
    setmode( fileno( stdout ), O_BINARY );

  USES_CONVERSION;

  WCHAR*  lpwName = A2W( argv[1] );
  LPVOID  lpvData = NULL;
  ULONG   ccvData = NULL;
  HRESULT hr      = S_OK;

  hr = FilterDocument( lpwName, lpvData, ccvData );
  if ( S_OK == hr )
  {
    fwrite( lpvData, sizeof(char), ccvData, os );
    
    ReallocBuffer( lpvData, 0, lpvData, 0 );
  } else {
    fprintf( stderr, "Filtering failed!\n" );
    err = 1;
  }

  if ( os != stdout )
    fclose( os );

  return err;
}


PS: для извлечения текста из PDF-файлов нужно скачать и установить Adobe PDF IFilter v5.0 (находится здесь: http://www.adobe.com/support/downloads/detail.jsp?ftpID=1276)
Алексей Кирдин
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.