Resolved conflits with jbarlow83 pull request
This commit is contained in:
+104
-20
@@ -6,12 +6,95 @@
|
||||
# Initial version by Jonathan Brinley, jonathanbrinley@gmail.com
|
||||
##############################################################################
|
||||
from reportlab.pdfgen.canvas import Canvas
|
||||
from reportlab.pdfgen.pdfimages import PDFImage
|
||||
from reportlab.lib.units import inch
|
||||
from lxml import etree as ElementTree
|
||||
from PIL import Image
|
||||
import re, sys
|
||||
import argparse
|
||||
|
||||
|
||||
def monkeypatch_method(cls):
|
||||
'''
|
||||
Override a class method at runtime.
|
||||
|
||||
Rationale:
|
||||
https://mail.python.org/pipermail/python-dev/2008-January/076194.html
|
||||
'''
|
||||
def decorator(func):
|
||||
setattr(cls, func.__name__, func)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
||||
@monkeypatch_method(PDFImage)
|
||||
def PIL_imagedata(self):
|
||||
'''
|
||||
Add ability to output greyscale and 1-bit PIL images without conversion to RGB.
|
||||
|
||||
The upstream Python 2.7 version of reportlab converts 1-bit PIL images to RGB
|
||||
instead of saving them in a lower BPP format. They have since added the following
|
||||
fix to their Python 3.3 branch, but it has not been back-ported.
|
||||
|
||||
https://bitbucket.org/rptlab/reportlab/commits/177ddcbe4df6f9b461dac62612df9b8da3966a5d
|
||||
'''
|
||||
image = self.image
|
||||
if image.format == 'JPEG':
|
||||
fp = image.fp
|
||||
fp.seek(0)
|
||||
return self._jpg_imagedata(fp)
|
||||
|
||||
from reportlab.lib.utils import import_zlib
|
||||
from reportlab import rl_config
|
||||
from reportlab.pdfbase.pdfutils import _AsciiBase85Encode, _chunker
|
||||
|
||||
self.source = 'PIL'
|
||||
zlib = import_zlib()
|
||||
if not zlib:
|
||||
return
|
||||
|
||||
bpc = 8
|
||||
# Use the colorSpace in the image
|
||||
if image.mode == 'CMYK':
|
||||
myimage = image
|
||||
colorSpace = 'DeviceCMYK'
|
||||
bpp = 4
|
||||
elif image.mode == '1':
|
||||
myimage = image
|
||||
colorSpace = 'DeviceGray'
|
||||
bpp = 1
|
||||
bpc = 1
|
||||
elif image.mode == 'L':
|
||||
myimage = image
|
||||
colorSpace = 'DeviceGray'
|
||||
bpp = 1
|
||||
else:
|
||||
myimage = image.convert('RGB')
|
||||
colorSpace = 'RGB'
|
||||
bpp = 3
|
||||
imgwidth, imgheight = myimage.size
|
||||
|
||||
# this describes what is in the image itself
|
||||
# *NB* according to the spec you can only use the short form in inline images
|
||||
|
||||
imagedata = ['BI /W %d /H %d /BPC %d /CS /%s /F [%s/Fl] ID' %
|
||||
(imgwidth, imgheight, bpc, colorSpace, rl_config.useA85 and '/A85 ' or '')]
|
||||
|
||||
# use a flate filter and, optionally, Ascii Base 85 to compress
|
||||
raw = myimage.tostring()
|
||||
rowstride = (imgwidth * bpc * bpp + 7) / 8
|
||||
assert len(raw) == rowstride * imgheight, "Wrong amount of data for image"
|
||||
data = zlib.compress(raw) # this bit is very fast...
|
||||
|
||||
if rl_config.useA85:
|
||||
# ...sadly this may not be
|
||||
data = _AsciiBase85Encode(data)
|
||||
# append in blocks of 60 characters
|
||||
_chunker(data, imagedata)
|
||||
imagedata.append('EI')
|
||||
return (imagedata, imgwidth, imgheight)
|
||||
|
||||
|
||||
class hocrTransform():
|
||||
"""
|
||||
A class for converting documents from the hOCR format.
|
||||
@@ -24,20 +107,20 @@ class hocrTransform():
|
||||
|
||||
self.hocr = ElementTree.ElementTree()
|
||||
self.hocr.parse(hocrFileName)
|
||||
|
||||
|
||||
# if the hOCR file has a namespace, ElementTree requires its use to find elements
|
||||
matches = re.match('({.*})html', self.hocr.getroot().tag)
|
||||
self.xmlns = ''
|
||||
if matches:
|
||||
self.xmlns = matches.group(1)
|
||||
|
||||
|
||||
# get dimension in pt (not pixel!!!!) of the OCRed image
|
||||
for div in self.hocr.findall(".//%sdiv[@class='ocr_page']"%(self.xmlns)):
|
||||
coords = self.element_coordinates(div)
|
||||
self.width = self.px2pt(coords[2]-coords[0])
|
||||
self.height = self.px2pt(coords[3]-coords[1])
|
||||
break # there shouldn't be more than one, and if there is, we don't want it
|
||||
|
||||
|
||||
# no width and heigh definition in the ocr_image element of the hocr file
|
||||
if self.width is None:
|
||||
print("No page dimension found in the hocr file")
|
||||
@@ -54,7 +137,7 @@ class hocrTransform():
|
||||
return self._get_element_text(body).encode('utf-8') # XML gives unicode
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
def _get_element_text(self, element):
|
||||
"""
|
||||
Return the textual content of the element and its children
|
||||
@@ -67,7 +150,7 @@ class hocrTransform():
|
||||
if element.tail is not None:
|
||||
text = text + element.tail
|
||||
return text
|
||||
|
||||
|
||||
def element_coordinates(self, element):
|
||||
"""
|
||||
Returns a tuple containing the coordinates of the bounding box around
|
||||
@@ -80,13 +163,13 @@ class hocrTransform():
|
||||
coords = matches.group(1).split()
|
||||
out = (int(coords[0]),int(coords[1]),int(coords[2]),int(coords[3]))
|
||||
return out
|
||||
|
||||
|
||||
def px2pt(self, pxl):
|
||||
"""
|
||||
Returns the length in pt given length in pxl
|
||||
"""
|
||||
return float(pxl)/self.dpi*inch
|
||||
|
||||
|
||||
def to_pdf(self, outFileName, imageFileName, showBoundingboxes, fontname="Helvetica"):
|
||||
"""
|
||||
Creates a PDF file with an image superimposed on top of the text.
|
||||
@@ -97,13 +180,13 @@ class hocrTransform():
|
||||
"""
|
||||
# create the PDF file
|
||||
pdf = Canvas(outFileName, pagesize=(self.width, self.height), pageCompression=1) # page size in points (1/72 in.)
|
||||
|
||||
|
||||
# draw bounding box for each paragraph
|
||||
pdf.setStrokeColorRGB(0,1,1) # light blue for bounding box of paragraph
|
||||
pdf.setFillColorRGB(0,1,1) # light blue for bounding box of paragraph
|
||||
pdf.setLineWidth(0) # no line for bounding box
|
||||
for elem in self.hocr.findall(".//%sp[@class='%s']" % (self.xmlns, "ocr_par")):
|
||||
|
||||
|
||||
elemtxt=self._get_element_text(elem).rstrip()
|
||||
if len(elemtxt) == 0:
|
||||
continue
|
||||
@@ -113,12 +196,12 @@ class hocrTransform():
|
||||
y1=self.px2pt(coords[1])
|
||||
x2=self.px2pt(coords[2])
|
||||
y2=self.px2pt(coords[3])
|
||||
|
||||
|
||||
# draw the bbox border
|
||||
if showBoundingboxes == True:
|
||||
pdf.rect(x1, self.height-y2, x2-x1, y2-y1, fill=1)
|
||||
|
||||
|
||||
pdf.rect(x1, self.height-y2, x2-x1, y2-y1, fill=1)
|
||||
|
||||
|
||||
# check if element with class 'ocrx_word' are available
|
||||
# otherwise use 'ocr_line' as fallback
|
||||
elemclass="ocr_line"
|
||||
@@ -141,7 +224,7 @@ class hocrTransform():
|
||||
y1=self.px2pt(coords[1])
|
||||
x2=self.px2pt(coords[2])
|
||||
y2=self.px2pt(coords[3])
|
||||
|
||||
|
||||
# draw the bbox border
|
||||
if showBoundingboxes == True:
|
||||
pdf.rect(x1, self.height-y2, x2-x1, y2-y1, fill=0)
|
||||
@@ -152,7 +235,7 @@ class hocrTransform():
|
||||
|
||||
# set cursor to bottom left corner of bbox (adjust for dpi)
|
||||
text.setTextOrigin(x1, self.height-y2)
|
||||
|
||||
|
||||
# scale the width of the text to fill the width of the bbox
|
||||
text.setHorizScale(100*(x2-x1)/pdf.stringWidth(elemtxt, fontname, fontsize))
|
||||
|
||||
@@ -162,13 +245,14 @@ class hocrTransform():
|
||||
|
||||
# put the image on the page, scaled to fill the page
|
||||
if imageFileName != None:
|
||||
im = Image.open(imageFileName)
|
||||
im = Image.open(imageFileName)
|
||||
pdf.drawInlineImage(im, 0, 0, width=self.width, height=self.height)
|
||||
|
||||
|
||||
# finish up the page and save it
|
||||
pdf.showPage()
|
||||
pdf.save()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Convert hocr file to PDF')
|
||||
parser.add_argument('-b', '--boundingboxes', action="store_true", default=False, help='Show bounding boxes borders')
|
||||
@@ -181,5 +265,5 @@ if __name__ == "__main__":
|
||||
hocr = hocrTransform(args.hocrfile, args.resolution)
|
||||
hocr.to_pdf(args.outputfile, args.image, args.boundingboxes)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Executable → Regular
+11
-4
@@ -43,7 +43,7 @@ FORCE_OCR="${14}" # Force to OCR, even if the page already contains fonts
|
||||
# - 2: in case the page contains more than one image
|
||||
##################################
|
||||
getImgInfo() {
|
||||
local page widthPDF heightPDF curImgInfo nbImg curImg propCurImg widthCurImg heightCurImg colorspaceCurImg dpi
|
||||
local page widthPDF heightPDF curImgInfo nbImg curImg propCurImg widthCurImg heightCurImg colorspaceCurImg depthCurImg dpi
|
||||
|
||||
# page number
|
||||
page="$1"
|
||||
@@ -73,10 +73,11 @@ getImgInfo() {
|
||||
fi
|
||||
# Get characteristics of the extracted image
|
||||
curImg=`ls -1 "$curOrigImg"* 2>/dev/null`
|
||||
propCurImg=`identify -format "%w %h %[colorspace]" "$curImg"`
|
||||
propCurImg=`identify -format "%w %h %[colorspace] %[depth]" "$curImg"`
|
||||
widthCurImg=`echo "$propCurImg" | cut -f1 -d" "`
|
||||
heightCurImg=`echo "$propCurImg" | cut -f2 -d" "`
|
||||
colorspaceCurImg=`echo "$propCurImg" | cut -f3 -d" "`
|
||||
depthCurImg=`echo "$propCurImg" | cut -f4 -d" "`
|
||||
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Size ${heightCurImg}x${widthCurImg} (in pixel)"
|
||||
|
||||
# compute the resolution of the image (making the assumption that x & y resolution are equal)
|
||||
@@ -87,6 +88,7 @@ getImgInfo() {
|
||||
# save the image characteristics
|
||||
echo "DPI=$dpi" > "$curImgInfo"
|
||||
echo "COLOR_SPACE=$colorspaceCurImg" >> "$curImgInfo"
|
||||
echo "DEPTH=$depthCurImg" >> "$curImgInfo"
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -109,6 +111,7 @@ curImgInfo="$TMP_FLD/${page}.orig-img-info.txt" # Detected characteristics of
|
||||
|
||||
|
||||
# auto-detect the characteristics of the embedded image
|
||||
depthCurImg="8"
|
||||
getImgInfo "$page" "$widthPDF" "$heightPDF" "$curImgInfo"
|
||||
ret_code="$?"
|
||||
# in case the page contains text do not OCR, unless the FORCE_OCR flag is set
|
||||
@@ -127,6 +130,7 @@ else
|
||||
# read the image characteristics from the file
|
||||
dpi=`cat "$curImgInfo" | grep "^DPI=" | cut -f2 -d"="`
|
||||
colorspaceCurImg=`cat "$curImgInfo" | grep "^COLOR_SPACE=" | cut -f2 -d"="`
|
||||
depthCurImg=`cat "$curImgInfo" | grep "^DEPTH=" | cut -f2 -d"="`
|
||||
fi
|
||||
|
||||
# perform oversampling if the resolution is not sufficient to get good OCR results
|
||||
@@ -137,10 +141,13 @@ elif [ "$dpi" -lt "200" ]; then
|
||||
[ $VERBOSITY -ge $LOG_WARN ] && echo "Page $page: Low image resolution detected ($dpi dpi). If needed, please use the \"-o\" to try to get better OCR results."
|
||||
fi
|
||||
|
||||
# Identify if page image should be saved as ppm (color) or pgm (gray)
|
||||
# Identify if page image should be saved as ppm (color), pgm (gray) or pbm (b&w)
|
||||
ext="ppm" # by default (color image) the extension of the extracted image is ppm
|
||||
opt="" # by default (color image) no option as to be passed to pdftoppm
|
||||
if [ "$colorspaceCurImg" = "Gray" ]; then
|
||||
if [ "$colorspaceCurImg" = "Gray" ] && [ "$depthCurImg" = "1" ]; then
|
||||
ext="pbm"
|
||||
opt="-mono"
|
||||
elif [ "$colorspaceCurImg" = "Gray" ]; then
|
||||
ext="pgm"
|
||||
opt="-gray"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user