 |
Club Builder Foro de C++Builder en español
|
| Ver tema anterior :: Ver tema siguiente |
| Autor |
Mensaje |
Felip Master

Registrado: 09 Jun 2004Mensajes: 200
|
Publicado: Dom 28 Jul, 2013 11:21 am Asunto: PDF to TXT |
|
|
¿Cómo puedo leer sólo el texto de un archivo PDF? Convertir PDF a texto sin formato?
Gracias! |
|
| Volver arriba |
|
 |
Felip Master

Registrado: 09 Jun 2004Mensajes: 200
|
|
| Volver arriba |
|
 |
Felip Master

Registrado: 09 Jun 2004Mensajes: 200
|
Publicado: Lun 12 Ago, 2013 3:27 pm Asunto: Clipper to BCB |
|
|
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 |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Mie 14 Ago, 2013 10:59 pm Asunto: |
|
|
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 |
|
 |
Felip Master

Registrado: 09 Jun 2004Mensajes: 200
|
Publicado: Jue 15 Ago, 2013 7:33 pm Asunto: |
|
|
| Gracias. Voy probar el programa y el código fuente. |
|
| Volver arriba |
|
 |
Felip Master

Registrado: 09 Jun 2004Mensajes: 200
|
Publicado: Lun 19 Ago, 2013 4:51 pm Asunto: |
|
|
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 |
|
 |
_Leo Site Admin

Registrado: 11 Ago 2002Mensajes: 1875Ubicación: Lanzarote - Islas Canarias (España)
|
Publicado: Lun 19 Ago, 2013 10:49 pm Asunto: |
|
|
| 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 |
|
 |
Felip Master

Registrado: 09 Jun 2004Mensajes: 200
|
Publicado: Mie 21 Ago, 2013 4:45 am Asunto: |
|
|
| Estoy usando BCB 6 en Windows 7. Código excelente. Muchas gracias. |
|
| Volver arriba |
|
 |
|
|
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
|
© 2002–2026 Club Builder · Funciona con phpBB.
|