Módulo de Python para convertir PDF a texto [cerrado]


385

¿Hay algún módulo de Python para convertir archivos PDF en texto? Probé una pieza de código que se encuentra en Activestate que utiliza pypdf pero el texto generado tenido ningún espacio entre y era de ninguna utilidad.


1
Estaba buscando una solución similar. Solo necesito leer el texto del archivo pdf. No necesito las imágenes pdfminer es una buena opción, pero no encontré un ejemplo simple sobre cómo extraer el texto. Finalmente obtuve esta respuesta SO ( stackoverflow.com/questions/5725278/… ) y ahora la uso.
Nayan

2
Como la pregunta se cerró, la volví a publicar en el Stack Exchange dedicado a las recomendaciones de software en caso de que alguien quiera escribir una nueva respuesta: Módulo de Python para convertir PDF a texto
Franck Dernoncourt

1
La única solución que me funcionó para el contenido UTF-8: Apache Tika
Shoham

Me gustaría actualizar la lista de opciones disponibles para la conversión de PDF a texto en Python, GroupDocs.Conversion Cloud SDK para Python convierte PDF a texto con precisión.
Tilal Ahmad

Respuestas:


142

Prueba PDFMiner . Puede extraer texto de archivos PDF en formato HTML, SGML o "PDF etiquetado".

El formato PDF etiquetado parece ser el más limpio, y quitar las etiquetas XML deja solo el texto desnudo.

Una versión de Python 3 está disponible en:


2
Acabo de agregar una respuesta que describe cómo usar pdfminer como biblioteca.
codeape

24
sin soporte para python 3 :(
Karl Adler

1
La respuesta que proporcioné en este hilo podría ser útil para las personas que miran esta respuesta y se preguntan cómo usar la biblioteca. Doy un ejemplo sobre cómo usar la biblioteca PDFMiner para extraer texto del PDF. Dado que la documentación es un poco escasa, pensé que podría ayudar a algunas personas.
DuckPuncher

17
con respecto a python 3, hay una bifurcación basada en seis pypi.python.org/pypi/pdfminer.six
Denis Cornehl

1
código de muestra en stackoverflow.com/a/26495057/125617
Renaud

136

El paquete PDFMiner ha cambiado desde que se publicó codeape .

EDITAR (nuevamente):

PDFMiner ha sido actualizado nuevamente en la versión 20100213

Puede verificar la versión que ha instalado con lo siguiente:

>>> import pdfminer
>>> pdfminer.__version__
'20100213'

Aquí está la versión actualizada (con comentarios sobre lo que cambié / agregué):

def pdf_to_csv(filename):
    from cStringIO import StringIO  #<-- added so you can copy/paste this to try it
    from pdfminer.converter import LTTextItem, TextConverter
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item.objs:
                if isinstance(child, LTTextItem):
                    (_,_,x,y) = child.bbox                   #<-- changed
                    line = lines[int(-y)]
                    line[x] = child.text.encode(self.codec)  #<-- changed

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, codec="utf-8")  #<-- changed 
        # becuase my test documents are utf-8 (note: utf-8 is the default codec)

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(fp)       #<-- changed
    parser.set_document(doc)     #<-- added
    doc.set_parser(parser)       #<-- added
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

Editar (una vez más):

Aquí es una actualización para la versión más reciente en PyPI , 20100619p1. En resumen He sustituido LTTextItemcon LTChary pasé una instancia de LAParams al constructor CsvConverter.

def pdf_to_csv(filename):
    from cStringIO import StringIO  
    from pdfminer.converter import LTChar, TextConverter    #<-- changed
    from pdfminer.layout import LAParams
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item.objs:
                if isinstance(child, LTChar):               #<-- changed
                    (_,_,x,y) = child.bbox                   
                    line = lines[int(-y)]
                    line[x] = child.text.encode(self.codec)

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams())  #<-- changed
        # becuase my test documents are utf-8 (note: utf-8 is the default codec)

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(fp)       
    parser.set_document(doc)     
    doc.set_parser(parser)       
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        if page is not None:
            interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

EDITAR (una vez más):

Actualizado para la versión 20110515(¡gracias a Oeufcoque Penteano!):

def pdf_to_csv(filename):
    from cStringIO import StringIO  
    from pdfminer.converter import LTChar, TextConverter
    from pdfminer.layout import LAParams
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item._objs:                #<-- changed
                if isinstance(child, LTChar):
                    (_,_,x,y) = child.bbox                   
                    line = lines[int(-y)]
                    line[x] = child._text.encode(self.codec) #<-- changed

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams())
        # becuase my test documents are utf-8 (note: utf-8 is the default codec)

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(fp)       
    parser.set_document(doc)     
    doc.set_parser(parser)       
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        if page is not None:
            interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

1
In [6]: import pdfminer In [7]: pdfminer .__ version__ Out [7]: '20100424' In [8]: from pdfminer.converter import LTTextItem ImportError: no se puede importar el nombre LTTextItem .... LITERALS_DCT_DECODE LTChar LTImage LTPolygon LTTextBoxVITG_LITER__LITER_ LTContainer LTLine LTRect LTTextGroup LITERAL_DEVICE_RGB LTFigure LTPage LTText LTTextLine
Skylar Saveland

@skyl, el código anterior es para la versión anterior '20100213'. De la lista de cambios en su sitio web, parece que cambiaron LTTextItema LTChar. unixuser.org/~euske/python/pdfminer/index.html#changes
tgray

2
@Oeufcoque Penteano, gracias! Agregué otra sección a la respuesta para la versión 20110515según su comentario.
tgray

1
La respuesta dada por @ user3272884 funciona a partir del 5-1-2014
jmunsch

1
Tenía que resolver este mismo problema hoy en día, modificado el código del tgray un poco para extraer información acerca de los espacios en blanco, lo publicó aquí
tarikki

67

Como ninguna de estas soluciones admite la última versión de PDFMiner, escribí una solución simple que devolverá el texto de un PDF usando PDFMiner. Esto funcionará para aquellos que reciben errores de importación conprocess_pdf

import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
from cStringIO import StringIO

def pdfparser(data):

    fp = file(data, 'rb')
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    # Create a PDF interpreter object.
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    # Process each page contained in the document.

    for page in PDFPage.get_pages(fp):
        interpreter.process_page(page)
        data =  retstr.getvalue()

    print data

if __name__ == '__main__':
    pdfparser(sys.argv[1])  

Vea a continuación el código que funciona para Python 3:

import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
import io

def pdfparser(data):

    fp = open(data, 'rb')
    rsrcmgr = PDFResourceManager()
    retstr = io.StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    # Create a PDF interpreter object.
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    # Process each page contained in the document.

    for page in PDFPage.get_pages(fp):
        interpreter.process_page(page)
        data =  retstr.getvalue()

    print(data)

if __name__ == '__main__':
    pdfparser(sys.argv[1])  

2
Este es el primer fragmento que he encontrado que realmente funciona con archivos PDF extraños (especialmente los libros electrónicos gratuitos que se pueden obtener de packtpub). Cualquier otra pieza de código solo devuelve las cosas crudas extrañamente codificadas, pero la suya en realidad devuelve texto. ¡Gracias!
somada141

Probablemente desee hacer retstr.seek (0) después de obtener datos, o acumulará texto de todas las páginas.
Tshirtman

2
Para usar con python3, además de los paréntesis obvios después del printcomando, uno tiene que reemplazar el filecomando opene importarlo StringIOdesde el paqueteio
McLawrence

1
Guau. Este bloque funcionó perfectamente la primera vez que lo copié. ¡Increíble! Para analizar y corregir los datos y no tener que preocuparse por ingresarlos.
SecsAndCyber

1
pdfminer no funciona para python3. este código no funciona para pdfminer3k
thang

47

Pdftotext Un programa de código abierto (parte de Xpdf) al que puede llamar desde python (no es lo que solicitó pero podría ser útil). Lo he usado sin problemas. Creo que Google lo usa en Google Desktop.


66
Esta parece ser la más útil de las herramientas enumeradas aquí, con la -layoutopción de mantener el texto en la misma posición que está en el PDF. Ahora, si solo pudiera descubrir cómo canalizar el contenido de un PDF.
Matthew Schinckel

Después de probar varias soluciones, esta parece ser la opción más simple y robusta. Python puede envolverlo fácilmente usando un archivo temporal para dictar dónde se escribe la salida.
Cerin

Cerin , use '-' como nombre de archivo para redirigir la salida a stdout. De esta manera, puede usar subprocess.check_output simple y esta llamada se sentiría como una función interna.
Ctrl-C

Solo para reforzar a cualquiera que lo esté usando. . . pdftotextparece funcionar muy bien, pero necesita un segundo argumento que sea un guión, si desea ver los resultados en stdout.
Gordon Linoff

1
Esto convertirá recursivamente todos los archivos PDF comenzando desde la carpeta actual: find . -iname "*.pdf" -exec pdftotext -enc UTF-8 -eol unix -raw {} \;por defecto, los archivos generados toman el nombre original con la .txtextensión.
ccpizza

43

pyPDF funciona bien (suponiendo que esté trabajando con archivos PDF bien formados). Si todo lo que desea es el texto (con espacios), puede hacer lo siguiente:

import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
    print page.extractText()

También puede acceder fácilmente a los metadatos, datos de imágenes, etc.

Un comentario en las notas del código extractText:

Localice todos los comandos de dibujo de texto, en el orden en que se proporcionan en la secuencia de contenido, y extraiga el texto. Esto funciona bien para algunos archivos PDF, pero deficiente para otros, dependiendo del generador utilizado. Esto será refinado en el futuro. No confíe en el orden del texto que sale de esta función, ya que cambiará si esta función se hace más sofisticada.

Si esto es un problema o no depende de lo que esté haciendo con el texto (por ejemplo, si el orden no importa, está bien, o si el generador agrega texto a la secuencia en el orden en que se mostrará, está bien) . Tengo código de extracción pyPdf en uso diario, sin ningún problema.


77
sin soporte Unicode :(
PanosJee

77
pyPdf es compatible con UTF ahora.
lbolla

2
Esta biblioteca parece basura. Las pruebas en un PDF aleatorio me dan el error "pyPdf.utils.PdfReadError: marcador EOF no encontrado"
Cerin

44
De la pregunta: el texto generado no tenía espacio entre ellos y era inútil . Utilicé pyPDF y obtuve el mismo resultado: el texto se extrae sin espacios entre las palabras.
Jordan Reiter

Cuando ejecuto la función page.extractText () obtengo el error 'TypeError: No se puede convertir el objeto' bytes 'en str implícitamente' ¿Cómo puedo lidiar con eso?
juankysmith

21

También puede usar pdfminer con bastante facilidad como biblioteca. Tiene acceso al modelo de contenido del pdf y puede crear su propia extracción de texto. Hice esto para convertir contenido PDF a texto separado por punto y coma, usando el código a continuación.

La función simplemente ordena los objetos de contenido TextItem de acuerdo con sus coordenadas y y x, y genera elementos con la misma coordenada y como una línea de texto, separando los objetos en la misma línea con ';' caracteres.

Utilizando este enfoque, pude extraer texto de un pdf del que ninguna otra herramienta pudo extraer contenido adecuado para un análisis posterior. Otras herramientas que probé incluyen pdftotext, ps2ascii y la herramienta en línea pdftextonline.com.

pdfminer es una herramienta invaluable para el raspado de pdf.


def pdf_to_csv(filename):
    from pdflib.page import TextItem, TextConverter
    from pdflib.pdfparser import PDFDocument, PDFParser
    from pdflib.pdfinterp import PDFResourceManager, PDFPageInterpreter

    class CsvConverter(TextConverter):
        def __init__(self, *args, **kwargs):
            TextConverter.__init__(self, *args, **kwargs)

        def end_page(self, i):
            from collections import defaultdict
            lines = defaultdict(lambda : {})
            for child in self.cur_item.objs:
                if isinstance(child, TextItem):
                    (_,_,x,y) = child.bbox
                    line = lines[int(-y)]
                    line[x] = child.text

            for y in sorted(lines.keys()):
                line = lines[y]
                self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
                self.outfp.write("\n")

    # ... the following part of the code is a remix of the 
    # convert() function in the pdfminer/tools/pdf2text module
    rsrc = PDFResourceManager()
    outfp = StringIO()
    device = CsvConverter(rsrc, outfp, "ascii")

    doc = PDFDocument()
    fp = open(filename, 'rb')
    parser = PDFParser(doc, fp)
    doc.initialize('')

    interpreter = PDFPageInterpreter(rsrc, device)

    for i, page in enumerate(doc.get_pages()):
        outfp.write("START PAGE %d\n" % i)
        interpreter.process_page(page)
        outfp.write("END PAGE %d\n" % i)

    device.close()
    fp.close()

    return outfp.getvalue()

ACTUALIZACIÓN :

El código anterior está escrito en una versión anterior de la API, vea mi comentario a continuación.


¿Qué tipo de complementos necesitas para que eso funcione compañero? He descargado e instalado pdfminer pero no es suficiente ...
kxk

1
El código anterior está escrito en una versión anterior de PDFminer. La API ha cambiado en versiones más recientes (por ejemplo, el paquete es ahora pdfminer, no pdflib). Sugiero que eche un vistazo a la fuente pdf2txt.pyen la fuente PDFminer, el código anterior se inspiró en la versión anterior de ese archivo.
codeape

17

slate es un proyecto que simplifica el uso de PDFMiner desde una biblioteca:

>>> with open('example.pdf') as f:
...    doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'   

1
Recibo un error de importación al ejecutar "import slate": {File "C: \ Python33 \ lib \ site-packages \ slate-0.3-py3.3.egg \ slate_ init_ .py", línea 48, en <module> ImportError: no se puede importar el nombre PDF} ¡Pero la clase PDF está ahí! ¿Sabes cómo resolver esto?
juankysmith

No, esto suena muy extraño. ¿Tienes las dependencias?
Tim McNamara

Normalmente recibo mensajes sobre dependencias perdidas, en este caso recibo el clásico mensaje "Importar archivo de pizarra" C: \ Python33 \ lib \ site-packages \ slate-0.3-py3.3.egg \ slate_ init_ .py ", línea 48 , en <module> ImportError: no se puede importar el nombre PDF "
juankysmith

Slate 0.3 requiere pdfminer 20110515, de acuerdo con este problema de GitHub
jabbett

66
Este paquete ya no se mantiene. Abstenerse de usarlo. Ni siquiera puedes usarlo en Python 3.5
Sivasubramaniam Arunachalam

9

Necesitaba convertir un PDF específico a texto plano dentro de un módulo de Python. Utilicé PDFMiner 20110515, después de leer su herramienta pdf2txt.py escribí este fragmento simple:

from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams

def to_txt(pdf_path):
    input_ = file(pdf_path, 'rb')
    output = StringIO()

    manager = PDFResourceManager()
    converter = TextConverter(manager, output, laparams=LAParams())
    process_pdf(manager, converter, input_)

    return output.getvalue() 

1
def to_txt (pdf_path):
Cătălin George Feștilă

si quisiera convertir solo un cierto número de páginas, ¿cómo lo haría con este código?
psychok7

@ psychok7 ¿Has intentado usar la herramienta pdf2txt? Parece admitir esa característica en la versión actual con el indicador -p, la implementación parece fácil de seguir y también debería ser fácil de personalizar: github.com/euske/pdfminer/blob/master/tools/pdf2txt.py ¡ Espero que ayude! :)
gonz

1
gracias @gonz, intenté con todo lo anterior, pero su solución resulta perfecta para mí, salida con espacios :)
lazarus

pdf2txt.py está instalado aquí para mí:C:\Python27\Scripts\pdfminer\tools\pdf2txt.py
The Red Pea

6

Reutilizando el código pdf2txt.py que viene con pdfminer; puede hacer una función que tome un camino hacia el pdf; opcionalmente, un tipo externo (txt | html | xml | tag) y opta por la línea de comandos pdf2txt {'-o': '/path/to/outfile.txt' ...}. Por defecto, puede llamar a:

convert_pdf(path)

Se creará un archivo de texto, un hermano en el sistema de archivos para el pdf original.

def convert_pdf(path, outtype='txt', opts={}):
    import sys
    from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, process_pdf
    from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter, TagExtractor
    from pdfminer.layout import LAParams
    from pdfminer.pdfparser import PDFDocument, PDFParser
    from pdfminer.pdfdevice import PDFDevice
    from pdfminer.cmapdb import CMapDB

    outfile = path[:-3] + outtype
    outdir = '/'.join(path.split('/')[:-1])

    debug = 0
    # input option
    password = ''
    pagenos = set()
    maxpages = 0
    # output option
    codec = 'utf-8'
    pageno = 1
    scale = 1
    showpageno = True
    laparams = LAParams()
    for (k, v) in opts:
        if k == '-d': debug += 1
        elif k == '-p': pagenos.update( int(x)-1 for x in v.split(',') )
        elif k == '-m': maxpages = int(v)
        elif k == '-P': password = v
        elif k == '-o': outfile = v
        elif k == '-n': laparams = None
        elif k == '-A': laparams.all_texts = True
        elif k == '-D': laparams.writing_mode = v
        elif k == '-M': laparams.char_margin = float(v)
        elif k == '-L': laparams.line_margin = float(v)
        elif k == '-W': laparams.word_margin = float(v)
        elif k == '-O': outdir = v
        elif k == '-t': outtype = v
        elif k == '-c': codec = v
        elif k == '-s': scale = float(v)
    #
    CMapDB.debug = debug
    PDFResourceManager.debug = debug
    PDFDocument.debug = debug
    PDFParser.debug = debug
    PDFPageInterpreter.debug = debug
    PDFDevice.debug = debug
    #
    rsrcmgr = PDFResourceManager()
    if not outtype:
        outtype = 'txt'
        if outfile:
            if outfile.endswith('.htm') or outfile.endswith('.html'):
                outtype = 'html'
            elif outfile.endswith('.xml'):
                outtype = 'xml'
            elif outfile.endswith('.tag'):
                outtype = 'tag'
    if outfile:
        outfp = file(outfile, 'w')
    else:
        outfp = sys.stdout
    if outtype == 'txt':
        device = TextConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)
    elif outtype == 'xml':
        device = XMLConverter(rsrcmgr, outfp, codec=codec, laparams=laparams, outdir=outdir)
    elif outtype == 'html':
        device = HTMLConverter(rsrcmgr, outfp, codec=codec, scale=scale, laparams=laparams, outdir=outdir)
    elif outtype == 'tag':
        device = TagExtractor(rsrcmgr, outfp, codec=codec)
    else:
        return usage()

    fp = file(path, 'rb')
    process_pdf(rsrcmgr, device, fp, pagenos, maxpages=maxpages, password=password)
    fp.close()
    device.close()

    outfp.close()
    return

1

PDFminer me dio quizás una línea [página 1 de 7 ...] en cada página de un archivo pdf que probé con él.

La mejor respuesta que tengo hasta ahora es pdftoipe, o el código c ++ está basado en Xpdf.

vea mi pregunta sobre cómo se ve la salida de pdftoipe.



1

He utilizado pdftohtmlcon el -xmlargumento, lea el resultado con subprocess.Popen(), que le dará x coord, y coord, ancho, alto y fuente, de cada fragmento de texto en el pdf. Creo que esto es lo que 'evince' probablemente también usa porque se escuchan los mismos mensajes de error.

Si necesita procesar datos en columnas, se vuelve un poco más complicado ya que tiene que inventar un algoritmo que se adapte a su archivo pdf. El problema es que los programas que hacen archivos PDF realmente no necesariamente presentan el texto en ningún formato lógico. Puede probar algoritmos de clasificación simples y funciona a veces, pero puede haber pequeños 'rezagados' y 'extraviados', fragmentos de texto que no se colocan en el orden que pensaba. Entonces tienes que ser creativo.

Me llevó alrededor de 5 horas descubrir uno para los archivos PDF en los que estaba trabajando. Pero funciona bastante bien ahora. Buena suerte.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.