Club Builder - Foros de discusión Club Builder
Foro de C++Builder en español
 
 FAQFAQ   BuscarBuscar   MiembrosMiembros   Grupos de UsuariosGrupos de Usuarios   RegistrarseRegistrarse 
 PerfilPerfil   Entra para ver tus mensajes privadosEntra para ver tus mensajes privados   EntrarEntrar 

PDF to TXT

 
Publicar nuevo tema   Responder al tema    Foros de discusión -> Consultas
Ver tema anterior :: Ver tema siguiente  
Autor Mensaje
Mensaje    Asunto: PDF to TXT Responder citando

¿Cómo puedo leer sólo el texto de un archivo PDF? Convertir PDF a texto sin formato?
Gracias!
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Encontré un código en C:

http://www.codeproject.com/Articles/7056/Code-to-extract-plain-text-from-a-PDF-file

Aunque no se ha probado, pero parece que funciona bien.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Clipper to BCB Responder citando

Encontré una adaptación más completa de este código para clipper (proyecto harbour):

Código:

#include "FiveWin.ch"

function Main()

   local oVoice := TOleAuto():New( "Sapi.SPVoice" )
   local cText := GetText( "c:\curl.pdf" )
   
   oVoice:Speak( "c:\curl.txt", 4 )

return nil

function GetText( cPdfFile )

   local cPDF := MemoRead( cPdfFile )
   local nStart := At( "stream", cPDF )
   local nEnd := At( "endstream", cPDF )
   local cBuf := Replicate( Chr( 0 ), ( nEnd - nStart ) * 10 )
   local cText, nResult, cTemp
   local hFile, cResult := ""
   local n := 1

   if ! File( hb_CurDrive() + ":\" + cFileNoExt( cPdfFile ) + ".out" )
      hFile = fcreate( hb_CurDrive() + ":\" + cFileNoExt( cPdfFile ) + ".out", "wb" )
   else
      hFile = fopen( hb_CurDrive() + ":\" + cFileNoExt( cPdfFile ) + ".out", "wb" )
   endif   

   while "stream" $ cPDF
      nStart = At( "stream", cPDF )
      nEnd = At( "endstream", cPDF )
     
      if nStart == 0
         cPDF = ""
         exit
      endif
         
      cBuf = Replicate( Chr( 0 ), ( nEnd - nStart ) * 10 )
      cText = SubStr( cPDF, nStart + 6, nEnd - nStart )

      if Left( cText, 1 ) == Chr( 0x0d ) .and. ;
         SubStr( cText, 2, 1 ) == Chr( 0x0a )
         nStart += 2
      elseif Left( cText, 1 ) == Chr( 0x0a )
         nStart++
      endif

      if SubStr( cText, nEnd - 2, 1 ) == Chr( 0x0d ) .and. ;
         SubStr( cText, nEnd - 1, 1 ) == Chr( 0x0a )
         nEnd -= 2
      elseif SubStr( cText, nEnd - 1, 1 ) == Chr( 0x0a )
         nEnd--
      endif

      HB_ZUNCOMPRESS( SubStr( cPDF, nStart + 6, nEnd - nStart ), @cBuf, @nResult )
      cTemp = cBuf
      while "[" $ cTemp
         // OutputDebugString( "dentro del bucle Translate" + CRLF )
         if ! "]" $ cTemp
            exit
         endif   
         cResult += Translate( SubStr( cTemp, At( "[", cTemp ), At( "]", cTemp ) - At( "[", cTemp ) + 1 ) )
         cTemp = SubStr( cTemp, At( "]", cTemp ) + 1 )
      end   

      // OutputDebugString( Str( Len( cPDF ) ) )
      // OutputDebugString( If( Empty( cPDF ), " * Empty", " * not empty" ) + CRLF )
      ProcessOutput( hFile, cBuf )
      cPDF = SubStr( cPDF, nEnd + Len( "endstream" ) + 1 )
   end

   // OutputDebugString( "done" )
   FClose( hFile )
   MemoWrit( hb_CurDrive() + ":\" + cFileNoExt( cPdfFile ) + ".txt", cResult )

return cResult

function Translate( cText )

   local cCode, cResult := ""
   local nStart, nEnd, n
   
   cText = SubStr( cText, 2, Len( cText ) - 2 )
   
   while "<" $ cText
      nStart = At( "<", cText )
      nEnd = At( ">", cText )
      cCode = SubStr( cText, nStart + 1, nEnd - nStart - 1 )
      for n = 1 to Len( cCode ) step 4
         cResult += Chr( hb_HextoNum( SubStr( cCode, n, 4 ) ) )
      next   
      if nEnd != 0
         cText = SubStr( cText, nEnd + 1 )
      else
         cText = ""
      endif     
   end   
   
return cResult + " "     

#pragma BEGINDUMP

#include <hbapi>
#include <wtypes>

#define oldchar 15

float ExtractNumber(const char* search, int lastcharoffset)
{
        float flt=-1.0;
        int i = lastcharoffset;
        char buffer[oldchar+5];

        while (i>0 && search[i]==' ') i--;
        while (i>0 && (isdigit(search[i]) || search[i]=='.')) i--;
        ZeroMemory(buffer,sizeof(buffer));
        strncpy(buffer, search+i+1, lastcharoffset-i);
        if (buffer[0] && sscanf(buffer, "%f", &flt))
        {
                return flt;
        }
        return -1.0;
}
BOOL seen2(const char* search, char* recent)
{
if (    recent[oldchar-3]==search[0]
     && recent[oldchar-2]==search[1]
         && (recent[oldchar-1]==' ' || recent[oldchar-1]==0x0d ||
recent[oldchar-1]==0x0a)
         && (recent[oldchar-4]==' ' || recent[oldchar-4]==0x0d ||
recent[oldchar-4]==0x0a)
         )
        {
                return TRUE;
        }
        return FALSE;
}

#include <hbapifs>

int iPos = 0;

static int xputc( unsigned char c, FILE * fo )
{
   return hb_fsWriteAt( ( HB_FHANDLE ) fo, &c, 1, iPos++ );
}

void ProcessOutput(FILE* file, char* output, size_t len)
{
        //Are we currently inside a text object?
        BOOL intextobject = FALSE;

        //Is the next character literal (e.g. \\ to get a \ character or \( to get ( ):
        BOOL nextliteral = FALSE;

        //() Bracket nesting level. Text appears inside ()
        int rbdepth = 0;

        //Keep previous chars to get extract numbers etc.:
        char oc[oldchar];
        int j;
        size_t i;

        for (j=0; j<oldchar; j++)
           oc[j]=' ';

        for( i=0; i<len>1.0)
                                {
                                        xputc(0x0d, file);
                                        xputc(0x0a, file);
                                }
                                if (num<1>1000) is called for by looking
                                //at the number in front of (
                                num = ExtractNumber(oc,oldchar-1);
                                if (num>0)
                                {
                                        if (num>1000.0)
                                        {
                                                xputc('\t', file);
                                        }
                                        else if (num>100.0)
                                        {
                                                xputc(' ', file);
                                        }
                                }
                        }
                        else if (c==')' && rbdepth==1 && !nextliteral)
                        {
                                //Stop outputting text
                                rbdepth=0;
                        }
                        else if (rbdepth==1)
                        {
                                //Just a normal text character:
                                if (c=='\\' && !nextliteral)
                                {
                                        //Only print out next character no matter what. Do not interpret.
                                        nextliteral = TRUE;
                                }
                                else
                                {
                                        nextliteral = FALSE;
                                        if ( ((c>=' ') && (c<c>=128) && (c<255)) )
                                        {
                                                xputc(c, file);
                                        }
                                }
                        }
                }
                //Store the recent characters for when we have to go back for a number:
                for (j=0; j<oldchar-1; j++) oc[j]=oc[j+1];
                oc[oldchar-1]=c;
                if (!intextobject)
                {
                        if (seen2("BT", oc))
                        {
                                //Start of a text object:
                                intextobject = TRUE;
                        }
                }
        }
}

HB_FUNC( PROCESSOUTPUT )
{
   ProcessOutput( ( FILE * ) hb_parnl( 1 ), ( char * ) hb_parc( 2 ), hb_parclen( 2 ) );
}

#pragma ENDDUMP



fuente: http://forums.fivetechsupport.com/viewtopic.php?f=6&t=26511&sid=029522dde6c6676624a75a20df4f22f8

He buscado en la Internet para traductores (clipper para CPP), pero ninguno ha producido código compilable.

¿Alguien puede traducir este código para utilizar en BCB?

Muchas gracias.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

El código compila en C++Builder con muy poquitos cambios, aunque necesitas descargarte la librería zlib (en las versiones de C++Builder nuevas ya viene preinstalada).

El código que has puesto en Clipper es una adaptación del primer enlace que pusiste en C, así que no entiendo porque lo ves más claro si es realmente lo mismo...

He probado el código y no es muy bueno, hay muchos PDF que no es capaz de convertir, y muchos de los que convierte parte las líneas por donde le parece. Lógico ya que no tiene en cuenta muchos detalles..

Si no necesitas complicarte mucho con el código y te vale un ejecutable de consola para hacer la conversión fácilmente, tienes Xpdf, que trae un ejecutable llamado "pdftotext.exe", me ha funcionado con todos los PDF que he probado. Y además si quieres complicarte tienes en la web también el código fuente en C++ de toda la librería completa:

http://www.foolabs.com/xpdf/
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Gracias. Voy probar el programa y el código fuente.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Estoy estudiando la estructura de los archivos PDF. En cuanto a una mejor comprensión de los detalles de este tipo de archivos, publicar aquí en este tema. Me di cuenta de que el problema con el primer código es asociado a métodos de (des)compresion e de (des)criptografia de los datos.

Enlace interesante:

http://www.printmyfolders.com/understanding-pdf

El codigo de la librería Xpdf es muy bueno, pero complejo. Creo que una adaptación del primer código si mostrará una menos laboriosa.

De todo modo, voy a publicar aquí el progreso de mis estudios, y las dudas que aparecen.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Felip escribió:
El codigo de la librería Xpdf es muy bueno, pero complejo. Creo que una adaptación del primer código si mostrará una menos laboriosa.

Bueno, aquí el código con algunos pequeños cambios del código de más arriba. Solo tienes que crear un proyecto nuevo vacío, añadir un TMemo y puedes sustituir todo el contenido de Unit1.cpp por el siguiente:

- Antes que nada, para que funcione necesitas añadir la librería ZLIB a tu proyecto. En el siguiente enlace puedes descargarla compilada para C++Builder y las dos cabeceras necesarias (zlibcb.lib, zlib.h, zconf.h), copia esos archivos en el directorio de tu proyecto: https://www.dropbox.com/s/5yqubqpfsi9znxw/zlib128cb.zip

Código:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

#pragma link "zlibcb.lib"
#include "zlib.h"
#include <stdio.h>
#include <memory>
#include <vector>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
//Find a string in a buffer:
size_t FindStringInBuffer (char* buffer, char* search, size_t buffersize)
{
   char* buffer0 = buffer;

   size_t len = strlen(search);
   bool fnd = false;
   while (!fnd)
   {
      fnd = true;
      for (size_t i=0; i<len; i++)
      {
         if (buffer[i]!=search[i])
         {
            fnd = false;
            break;
         }
      }
      if (fnd) return buffer - buffer0;
      buffer = buffer + 1;
      if (buffer - buffer0 + len >= buffersize) return -1;
   }
   return -1;
}
//---------------------------------------------------------------------------
//Keep this many previous recent characters for back reference:
#define oldchar 15
//---------------------------------------------------------------------------
//Convert a recent set of characters into a number if there is one.
//Otherwise return -1:
float ExtractNumber(const char* search, int lastcharoffset)
{
   int i = lastcharoffset;
   while (i>0 && search[i]==' ') i--;
   while (i>0 && (isdigit(search[i]) || search[i]=='.')) i--;
   float flt=-1.0;
   char buffer[oldchar+5]; ZeroMemory(buffer,sizeof(buffer));
   strncpy(buffer, search+i+1, lastcharoffset-i);
   if (buffer[0] && sscanf(buffer, "%f", &flt))
   {
      return flt;
   }
   return -1.0;
}
//---------------------------------------------------------------------------
//Check if a certain 2 character token just came along (e.g. BT):
bool seen2(const char* search, char* recent)
{
    if (recent[oldchar-3]==search[0]
        && recent[oldchar-2]==search[1]
        && (recent[oldchar-1]==' ' || recent[oldchar-1]==0x0d || recent[oldchar-1]==0x0a)
        && (recent[oldchar-4]==' ' || recent[oldchar-4]==0x0d || recent[oldchar-4]==0x0a) )
    {
      return true;
   }
   return false;
}
//---------------------------------------------------------------------------
//This method processes an uncompressed Adobe (text) object and extracts text.
String ProcessOutput(char* output, size_t len)
{
    String texto = "";

   //Are we currently inside a text object?
   bool intextobject = false;

   //Is the next character literal (e.g. \\ to get a \ character or \( to get ( ):
   bool nextliteral = false;

   //() Bracket nesting level. Text appears inside ()
   int rbdepth = 0;

   //Keep previous chars to get extract numbers etc.:
    char oc[oldchar];
   int j = 0;
   for (j = 0; j < oldchar; j++)
        oc[j]=' ';

   for (size_t i = 0; i < len; i++)
   {
        char c = output[i];
      if (intextobject)
      {
         if (rbdepth == 0 && seen2("TD", oc))
         {
            //Positioning. See if a new line has to start or just a tab:
            float num = ExtractNumber(oc, oldchar - 5);
            if (num > 1.0)
                    texto += "\r\n";
            if (num < 1.0)
                    texto += "\t";
         }
         if (rbdepth == 0 && seen2("ET", oc))
         {
            //End of a text object, also go to a new line.
            intextobject = false;
                texto += "\r\n";
         }
         else if (c=='(' && rbdepth==0 && !nextliteral)
         {
            //Start outputting text!
            rbdepth=1;
            //See if a space or tab (>1000) is called for by looking
            //at the number in front of (
            int num = ExtractNumber(oc, oldchar - 1);
            if (num > 0)
            {
               if (num > 1000.0)
                        texto += "\t";
               else if (num > 100.0)
                        texto += " ";
            }
         }
         else if (c==')' && rbdepth==1 && !nextliteral)
         {
            //Stop outputting text
            rbdepth=0;
         }
         else if (rbdepth == 1)
         {
            //Just a normal text character:
            if (c=='\\' && !nextliteral)
            {
               //Only print out next character no matter what. Do not interpret.
               nextliteral = true;
            }
            else
            {
               nextliteral = false;
               if ( ((c>=' ') && (c<='~')) || (((UCHAR)c>=128) && ((UCHAR)c<255)) )
                        texto += c;
            }
         }
      }
      //Store the recent characters for when we have to go back for a number:
      for (j = 0; j < oldchar-1; j++)
            oc[j] = oc[j+1];
      oc[oldchar-1] = c;
      if (!intextobject)
      {
         if (seen2("BT", oc))
         {
            //Start of a text object:
            intextobject = true;
         }
      }
   }
    return texto;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    //Read ethe ntire file into memory (!):
    std::auto_ptr<TMemoryStream> ms(new TMemoryStream);
    ms->LoadFromFile("X:\\miDir\\miPDF.pdf");
    char* buffer = static_cast<char*>(ms->Memory);
    long filelen = ms->Size;

    bool morestreams = true;

    //Now search the buffer repeated for streams of data:
    while (morestreams)
    {
        //Search for stream, endstream. We ought to first check the filter
        //of the object to make sure it if FlateDecode, but skip that for now!
        size_t streamstart = FindStringInBuffer(buffer, "stream", filelen);
        size_t streamend   = FindStringInBuffer(buffer, "endstream", filelen);
        if (streamstart>0 && streamend>streamstart)
        {
            //Skip to beginning and end of the data stream:
            streamstart += 6;

            if (buffer[streamstart]==0x0d && buffer[streamstart+1]==0x0a) streamstart+=2;
            else if (buffer[streamstart]==0x0a) streamstart++;

            if (buffer[streamend-2]==0x0d && buffer[streamend-1]==0x0a) streamend-=2;
            else if (buffer[streamend-1]==0x0a) streamend--;

            //Assume output will fit into 10 times input buffer:
            size_t outsize = (streamend - streamstart) * 10;
            std::vector<char> output(outsize);

            //Now use zlib to inflate:
            z_stream_s zstrm = {0};
            zstrm.avail_in = streamend - streamstart + 1;
            zstrm.avail_out = outsize;
            zstrm.next_in = (Bytef*)(buffer + streamstart);
            zstrm.next_out = (Bytef*)&output[0];

            int rsti = inflateInit(&zstrm);
            if (rsti == Z_OK)
            {
                int rst2 = inflate(&zstrm, Z_FINISH);
                if (rst2 >= 0)
                {
                    //Ok, got something, extract the text:
                    size_t totout = zstrm.total_out;
                    String texto = ProcessOutput(&output[0], totout);

                    // Texto extraido del stream pdf
                    //Memo2->Lines->Add(String::StringOfChar('-', 50));
                    Memo1->Lines->Add(texto);

                    //--Usa un segundo TMemo si quieres ver los streams sin procesar
                    //Tamaño y datos del stream descomprimido
                    //Memo2->Lines->Add(String::StringOfChar('-', 50));
                    //Memo2->Lines->Add("--> totout = " + String(totout));
                    //Memo2->Lines->Add(&output[0]);
                }
            }
            buffer+= streamend + 7;
            filelen = filelen - (streamend+7);
        }
        else
        {
            morestreams = false;
        }
    }
}
//---------------------------------------------------------------------------

Probado en el BCB6 que creo que es la que tienes. En las versiones más actuales no hace falta descargar ZLIB, ya que llevan una versión integrada, aunque habría que modificar el código para usarla, ya que las estructuras tienen nombres diferentes a las oficiales de la librería.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Estoy usando BCB 6 en Windows 7. Código excelente. Muchas gracias.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mostrar mensajes de anteriores:   
Cambiar a:  
No puedes publicar temas nuevos en este foro
No puedes responder a temas en este foro
No puedes editar tus mensajes en este foro
No puedes borrar tus mensajes en este foro
No puedes votar en las encuestas de este foro


Hosting WordPress Indedmedia desde 3,99 €
© 2002–2026 Club Builder · Funciona con phpBB.