python - example - pypdf2 extract text
Extrae imágenes de PDF sin remuestrear, en Python? (10)
A menudo, en un PDF, la imagen simplemente se almacena tal cual. Por ejemplo, un PDF con un jpg insertado tendrá un rango de bytes en algún lugar en el medio que cuando se extrae es un archivo jpg válido. Puede usar esto para extraer muy fácilmente los rangos de bytes del PDF. Escribí sobre esto hace algún tiempo, con un código de muestra: extracción de JPG de archivos PDF .
¿Cómo se pueden extraer todas las imágenes de un documento pdf, en resolución y formato nativos? (Significado extraer tiff como tiff, jpeg como jpeg, etc. y sin remuestrear). El diseño no es importante, no me importa si la imagen de origen se encuentra en la página.
Estoy usando Python 2.7 pero puedo usar 3.x si es necesario.
Agregué todos estos en PyPDFTK here .
Mi propia contribución es manejar los archivos /Indexed
como tales:
for obj in xObject:
if xObject[obj][''/Subtype''] == ''/Image'':
size = (xObject[obj][''/Width''], xObject[obj][''/Height''])
color_space = xObject[obj][''/ColorSpace'']
if isinstance(color_space, pdf.generic.ArrayObject) and color_space[0] == ''/Indexed'':
color_space, base, hival, lookup = [v.getObject() for v in color_space] # pg 262
mode = img_modes[color_space]
if xObject[obj][''/Filter''] == ''/FlateDecode'':
data = xObject[obj].getData()
img = Image.frombytes(mode, size, data)
if color_space == ''/Indexed'':
img.putpalette(lookup.getData())
img = img.convert(''RGB'')
img.save("{}{:04}.png".format(filename_prefix, i))
Tenga en cuenta que cuando se encuentran los archivos /Indexed
, no puede simplemente comparar /ColorSpace
con una cadena, ya que se trata de un ArrayObject
. Entonces, debemos verificar la matriz y recuperar la paleta indexada ( lookup
en el código) y configurarla en el objeto de la imagen PIL, de lo contrario permanece sin inicializar (cero) y toda la imagen se muestra en negro.
Mi primer instinto fue guardarlos como GIF (que es un formato indexado), pero mis pruebas resultaron que los PNG eran más pequeños y tenían el mismo aspecto.
Encontré esos tipos de imágenes al imprimir en PDF con la impresora PDF de Foxit Reader.
Después de buscar, encontré el siguiente script que funciona muy bien con mis PDF. Solo aborda JPG, pero funcionó perfectamente con mis archivos no protegidos. Además, no requiere ninguna biblioteca externa.
Para no tomar ningún crédito, el guión proviene de Ned Batchelder, y no yo. Código de Python3: extrae los jpg de los pdf. Rápido y sucio
import sys
with open(sys.argv[1],"rb") as file:
file.seek(0)
pdf = file.read()
startmark = b"/xff/xd8"
startfix = 0
endmark = b"/xff/xd9"
endfix = 2
i = 0
njpg = 0
while True:
istream = pdf.find(b"stream", i)
if istream < 0:
break
istart = pdf.find(startmark, istream, istream + 20)
if istart < 0:
i = istream + 20
continue
iend = pdf.find(b"endstream", istart)
if iend < 0:
raise Exception("Didn''t find end of stream!")
iend = pdf.find(endmark, iend - 20)
if iend < 0:
raise Exception("Didn''t find end of JPG!")
istart += startfix
iend += endfix
print("JPG %d from %d to %d" % (njpg, istart, iend))
jpg = pdf[istart:iend]
with open("jpg%d.jpg" % njpg, "wb") as jpgfile:
jpgfile.write(jpg)
njpg += 1
i = iend
Empecé con el código de @sylvain Hubo algunos errores, como la excepción NotImplementedError: unsupported filter /DCTDecode
de getData, o el hecho de que el código no pudo encontrar imágenes en algunas páginas porque estaban en un nivel más profundo que la página.
Ahí está mi código:
import PyPDF2
from PIL import Image
import sys
from os import path
import warnings
warnings.filterwarnings("ignore")
number = 0
def recurse(page, xObject):
global number
xObject = xObject[''/Resources''][''/XObject''].getObject()
for obj in xObject:
if xObject[obj][''/Subtype''] == ''/Image'':
size = (xObject[obj][''/Width''], xObject[obj][''/Height''])
data = xObject[obj]._data
if xObject[obj][''/ColorSpace''] == ''/DeviceRGB'':
mode = "RGB"
else:
mode = "P"
imagename = "%s - p. %s - %s"%(abspath[:-4], p, obj[1:])
if xObject[obj][''/Filter''] == ''/FlateDecode'':
img = Image.frombytes(mode, size, data)
img.save(imagename + ".png")
number += 1
elif xObject[obj][''/Filter''] == ''/DCTDecode'':
img = open(imagename + ".jpg", "wb")
img.write(data)
img.close()
number += 1
elif xObject[obj][''/Filter''] == ''/JPXDecode'':
img = open(imagename + ".jp2", "wb")
img.write(data)
img.close()
number += 1
else:
recurse(page, xObject[obj])
try:
_, filename, *pages = sys.argv
*pages, = map(int, pages)
abspath = path.abspath(filename)
except BaseException:
print(''Usage :/nPDF_extract_images file.pdf page1 page2 page3 …'')
sys.exit()
file = PyPDF2.PdfFileReader(open(filename, "rb"))
for p in pages:
page0 = file.getPage(p-1)
recurse(p, page0)
print(''%s extracted images''% number)
En Python con PyPDF2 para el filtro CCITTFaxDecode:
import PyPDF2
import struct
"""
Links:
PDF format: http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
CCITT Group 4: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.6-198811-I!!PDF-E&type=items
Extract images from pdf: http://.com/questions/2693820/extract-images-from-pdf-without-resampling-in-python
Extract images coded with CCITTFaxDecode in .net: http://.com/questions/2641770/extracting-image-from-pdf-with-ccittfaxdecode-filter
TIFF format and tags: http://www.awaresystems.be/imaging/tiff/faq.html
"""
def tiff_header_for_CCITT(width, height, img_size, CCITT_group=4):
tiff_header_struct = ''<'' + ''2s'' + ''h'' + ''l'' + ''h'' + ''hhll'' * 8 + ''h''
return struct.pack(tiff_header_struct,
b''II'', # Byte order indication: Little indian
42, # Version number (always 42)
8, # Offset to first IFD
8, # Number of tags in IFD
256, 4, 1, width, # ImageWidth, LONG, 1, width
257, 4, 1, height, # ImageLength, LONG, 1, lenght
258, 3, 1, 1, # BitsPerSample, SHORT, 1, 1
259, 3, 1, CCITT_group, # Compression, SHORT, 1, 4 = CCITT Group 4 fax encoding
262, 3, 1, 0, # Threshholding, SHORT, 1, 0 = WhiteIsZero
273, 4, 1, struct.calcsize(tiff_header_struct), # StripOffsets, LONG, 1, len of header
278, 4, 1, height, # RowsPerStrip, LONG, 1, lenght
279, 4, 1, img_size, # StripByteCounts, LONG, 1, size of image
0 # last IFD
)
pdf_filename = ''scan.pdf''
pdf_file = open(pdf_filename, ''rb'')
cond_scan_reader = PyPDF2.PdfFileReader(pdf_file)
for i in range(0, cond_scan_reader.getNumPages()):
page = cond_scan_reader.getPage(i)
xObject = page[''/Resources''][''/XObject''].getObject()
for obj in xObject:
if xObject[obj][''/Subtype''] == ''/Image'':
"""
The CCITTFaxDecode filter decodes image data that has been encoded using
either Group 3 or Group 4 CCITT facsimile (fax) encoding. CCITT encoding is
designed to achieve efficient compression of monochrome (1 bit per pixel) image
data at relatively low resolutions, and so is useful only for bitmap image data, not
for color images, grayscale images, or general data.
K < 0 --- Pure two-dimensional encoding (Group 4)
K = 0 --- Pure one-dimensional encoding (Group 3, 1-D)
K > 0 --- Mixed one- and two-dimensional encoding (Group 3, 2-D)
"""
if xObject[obj][''/Filter''] == ''/CCITTFaxDecode'':
if xObject[obj][''/DecodeParms''][''/K''] == -1:
CCITT_group = 4
else:
CCITT_group = 3
width = xObject[obj][''/Width'']
height = xObject[obj][''/Height'']
data = xObject[obj]._data # sorry, getData() does not work for CCITTFaxDecode
img_size = len(data)
tiff_header = tiff_header_for_CCITT(width, height, img_size, CCITT_group)
img_name = obj[1:] + ''.tiff''
with open(img_name, ''wb'') as img_file:
img_file.write(tiff_header + data)
#
# import io
# from PIL import Image
# im = Image.open(io.BytesIO(tiff_header + data))
pdf_file.close()
En Python con las bibliotecas PyPDF2 y Pillow es simple:
import PyPDF2
from PIL import Image
if __name__ == ''__main__'':
input1 = PyPDF2.PdfFileReader(open("input.pdf", "rb"))
page0 = input1.getPage(0)
xObject = page0[''/Resources''][''/XObject''].getObject()
for obj in xObject:
if xObject[obj][''/Subtype''] == ''/Image'':
size = (xObject[obj][''/Width''], xObject[obj][''/Height''])
data = xObject[obj].getData()
if xObject[obj][''/ColorSpace''] == ''/DeviceRGB'':
mode = "RGB"
else:
mode = "P"
if xObject[obj][''/Filter''] == ''/FlateDecode'':
img = Image.frombytes(mode, size, data)
img.save(obj[1:] + ".png")
elif xObject[obj][''/Filter''] == ''/DCTDecode'':
img = open(obj[1:] + ".jpg", "wb")
img.write(data)
img.close()
elif xObject[obj][''/Filter''] == ''/JPXDecode'':
img = open(obj[1:] + ".jp2", "wb")
img.write(data)
img.close()
Instalé ImageMagick en mi servidor y luego Popen
llamadas de línea de comando a través de Popen
:
#!/usr/bin/python
import sys
import os
import subprocess
import settings
IMAGE_PATH = os.path.join(settings.MEDIA_ROOT , ''pdf_input'' )
def extract_images(pdf):
output = ''temp.png''
cmd = ''convert '' + os.path.join(IMAGE_PATH, pdf) + '' '' + os.path.join(IMAGE_PATH, output)
subprocess.Popen(cmd.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
Esto creará una imagen para cada página y las almacenará como temp-0.png, temp-1.png ... Esto es solo ''extracción'' si tienes un pdf con solo imágenes y sin texto.
Libpoppler viene con una herramienta llamada "pdfimages" que hace exactamente esto.
(En los sistemas ubuntu está en el paquete poppler-utils)
http://poppler.freedesktop.org/
http://en.wikipedia.org/wiki/Pdfimages
Binarios de Windows: http://blog.alivate.com.au/poppler-windows/
Puedes usar el módulo PyMuPDF. Esto produce todas las imágenes como archivos .png, pero se resuelven de inmediato y es rápido.
import fitz
doc = fitz.open("file.pdf")
for i in range(len(doc)):
for img in doc.getPageImageList(i):
xref = img[0]
pix = fitz.Pixmap(doc, xref)
if pix.n < 5: # this is GRAY or RGB
pix.writePNG("p%s-%s.png" % (i, xref))
else: # CMYK: convert to RGB first
pix1 = fitz.Pixmap(fitz.csRGB, pix)
pix1.writePNG("p%s-%s.png" % (i, xref))
pix1 = None
pix = None
Solución mucho más fácil:
Use el paquete poppler-utils. Para instalarlo, use homebrew (homebrew es específico de MacOS, pero puede encontrar el paquete poppler-utils para Widows o Linux aquí: https://poppler.freedesktop.org/ ). La primera línea de código a continuación instala poppler-utils usando homebrew. Después de la instalación, la segunda línea (ejecutar desde la línea de comandos) luego extrae imágenes de un archivo PDF y las denomina "imagen *". Para ejecutar este programa desde dentro de Python use el sistema operativo o el módulo de subproceso. La tercera línea es el código que usa el módulo os, debajo se encuentra un ejemplo con un subproceso (Python 3.5 o posterior para la función run ()). Más información aquí: https://www.cyberciti.biz/faq/easily-extract-images-from-pdf-file/
brew install poppler
pdfimages file.pdf image
import os
os.system(''pdfimages file.pdf image'')
o
import subprocess
subprocess.run(''pdfimages file.pdf image'', shell=True)