Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
019513696b | ||
|
|
ad7a4476db | ||
|
|
4812b20fb2 | ||
|
|
f5e07c9427 | ||
|
|
75dcb90621 | ||
|
|
dfc0434cc2 | ||
|
|
882fc2257c | ||
|
|
41e83b52fc | ||
|
|
47758b4d8f | ||
|
|
6bf1f970a0 | ||
|
|
7d451f101f | ||
|
|
7edbfe0e40 | ||
|
|
91b42cbfa8 | ||
|
|
6907df41b4 | ||
|
|
2cebd90cbd |
+55
-29
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# © 2016 James R. Barlow: github.com/jbarlow83
|
# © 2016-7 James R. Barlow: github.com/jbarlow83
|
||||||
|
|
||||||
from string import Template
|
from string import Template
|
||||||
from subprocess import run, PIPE
|
from subprocess import run, PIPE
|
||||||
@@ -14,43 +14,69 @@ class Ocrmypdf < Formula
|
|||||||
${ocrmypdf_url}
|
${ocrmypdf_url}
|
||||||
${ocrmypdf_sha256}
|
${ocrmypdf_sha256}
|
||||||
|
|
||||||
depends_on :python3
|
|
||||||
depends_on :x11 # Pillow needs XQuartz
|
|
||||||
depends_on "pkg-config" => :build
|
|
||||||
depends_on "libffi"
|
|
||||||
depends_on "tesseract"
|
|
||||||
depends_on "ghostscript"
|
|
||||||
depends_on "unpaper"
|
|
||||||
depends_on "qpdf"
|
|
||||||
|
|
||||||
# mactex installs its own ghostscript by default which causes problems
|
|
||||||
# mactex users should use caskroom/cask/mactex-no-ghostscript instead
|
|
||||||
conflicts_with cask: "caskroom/cask/mactex"
|
|
||||||
|
|
||||||
# For Pillow source install
|
|
||||||
depends_on "openjpeg"
|
|
||||||
depends_on "freetype"
|
depends_on "freetype"
|
||||||
|
depends_on "ghostscript"
|
||||||
|
depends_on "jpeg"
|
||||||
depends_on "libpng"
|
depends_on "libpng"
|
||||||
depends_on "libjpeg"
|
depends_on "pkg-config" => :build
|
||||||
depends_on "webp"
|
depends_on "python3"
|
||||||
depends_on "little-cms2"
|
depends_on "qpdf"
|
||||||
depends_on "zlib"
|
depends_on "tesseract"
|
||||||
|
depends_on "unpaper"
|
||||||
|
|
||||||
${resources}
|
${resources}
|
||||||
def install
|
def install
|
||||||
ENV.append ["SETUPTOOLS_SCM_PRETEND_VERSION"], "v${ocrmypdf_version}"
|
venv = virtualenv_create(libexec, "python3")
|
||||||
ENV.each do |key, value|
|
|
||||||
puts "#{key}:#{value}"
|
resource("Pillow").stage do
|
||||||
|
inreplace "setup.py" do |s|
|
||||||
|
sdkprefix = MacOS::CLT.installed? ? "" : MacOS.sdk_path
|
||||||
|
s.gsub! "openjpeg.h", "probably_not_a_header_called_this_eh.h"
|
||||||
|
s.gsub! "ZLIB_ROOT = None", "ZLIB_ROOT = ('#{sdkprefix}/usr/lib', '#{sdkprefix}/usr/include')"
|
||||||
|
s.gsub! "JPEG_ROOT = None", "JPEG_ROOT = ('#{Formula["jpeg"].opt_prefix}/lib', '#{Formula["jpeg"].opt_prefix}/include')"
|
||||||
|
s.gsub! "FREETYPE_ROOT = None", "FREETYPE_ROOT = ('#{Formula["freetype"].opt_prefix}/lib', '#{Formula["freetype"].opt_prefix}/include')"
|
||||||
|
end
|
||||||
|
|
||||||
|
# avoid triggering "helpful" distutils code that doesn't recognize Xcode 7 .tbd stubs
|
||||||
|
ENV.append "CFLAGS", "-I#{MacOS.sdk_path}/System/Library/Frameworks/Tk.framework/Versions/8.5/Headers" unless MacOS::CLT.installed?
|
||||||
|
venv.pip_install Pathname.pwd
|
||||||
end
|
end
|
||||||
virtualenv_install_with_resources
|
|
||||||
|
res = resources.map(&:name).to_set - ["Pillow"]
|
||||||
|
|
||||||
|
res.each do |r|
|
||||||
|
venv.pip_install resource(r)
|
||||||
|
end
|
||||||
|
|
||||||
|
venv.pip_install_and_link buildpath
|
||||||
end
|
end
|
||||||
|
|
||||||
test do
|
test do
|
||||||
# `test do` will create, run in and delete a temporary directory.
|
# Since we use Python 3, we require a UTF-8 locale
|
||||||
#
|
ENV["LC_ALL"] = "en_US.UTF-8"
|
||||||
# The installed folder is not in the path, so use the entire path to any
|
|
||||||
# executables being tested: `system "#{bin}/program", "do", "something"`.
|
# One page Postscript with the wording "Testing" on the page
|
||||||
system "#{bin}/ocrmypdf", "--version"
|
# This is more compact than including a test PDF
|
||||||
|
(testpath/"test.ps").write(
|
||||||
|
<<~EOS
|
||||||
|
%!PS
|
||||||
|
/Times-Roman findfont
|
||||||
|
20 scalefont
|
||||||
|
setfont
|
||||||
|
gsave
|
||||||
|
newpath
|
||||||
|
200 400 moveto
|
||||||
|
(Testing) show
|
||||||
|
closepath
|
||||||
|
stroke
|
||||||
|
showpage
|
||||||
|
EOS
|
||||||
|
)
|
||||||
|
|
||||||
|
system "#{Formula["ghostscript"].opt_bin}/ps2pdf", testpath/"test.ps", testpath/"test.pdf"
|
||||||
|
|
||||||
|
# Use ocrmypdf -f to rasterize the PDF to image before doing OCR
|
||||||
|
system "#{bin}/ocrmypdf", "-f", "-q", "--deskew", testpath/"test.pdf", testpath/"ocr.pdf"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
""")
|
""")
|
||||||
|
|||||||
@@ -157,6 +157,29 @@ If you set ``--tesseract-timeout 0`` OCRmyPDF will apply its image processing wi
|
|||||||
ocrmypdf --tesseract-timeout=0 --remove-background input.pdf output.pdf
|
ocrmypdf --tesseract-timeout=0 --remove-background input.pdf output.pdf
|
||||||
|
|
||||||
|
|
||||||
|
Redo OCR
|
||||||
|
""""""""
|
||||||
|
|
||||||
|
To redo OCR on a file OCRed with other OCR software or a previous version of OCRmyPDF and/or Tesseract, you may use the ``--force-ocr`` argument. Normally, OCRmyPDF does not modify files that already appear to contain OCR text.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ocrmypdf --force-ocr input.pdf output.pdf
|
||||||
|
|
||||||
|
Note that the method above will force rasterization of all pages, potentially reducing quality or losing vector content.
|
||||||
|
|
||||||
|
To ensure quality is preserved, one could extract all of the images and rebuild the PDF for a lossless transformation. This recipe does not work when PDFs contain multiple images per page, as many do in practice. It will also lose any page rotation information.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
pdfimages -all old-ocr.pdf prefix # extract all images
|
||||||
|
img2pdf -o temp.pdf prefix* # construct new PDF from the images
|
||||||
|
# review the new PDF to ensure it visually matches the old one
|
||||||
|
ocrmypdf --output-type pdf temp.pdf new-ocr.pdf
|
||||||
|
|
||||||
|
``--output-type pdf`` is used here to avoid using Ghostscript which will also rasterize images.
|
||||||
|
|
||||||
|
|
||||||
Improving OCR quality
|
Improving OCR quality
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ OCRmyPDF uses `semantic versioning <http://semver.org/>`_ for its command line i
|
|||||||
|
|
||||||
The OCRmyPDF package itself does not contain a public API, although it is fairly stable and breaking changes are usually timed with a major release. A future release will clearly define the stable public API.
|
The OCRmyPDF package itself does not contain a public API, although it is fairly stable and breaking changes are usually timed with a major release. A future release will clearly define the stable public API.
|
||||||
|
|
||||||
|
v5.5
|
||||||
|
----
|
||||||
|
|
||||||
|
- Add new argument ``--max-image-mpixels``. Pillow 5.0 now raises an exception when images may be decompression bombs. This argument can be used to override the limit Pillow sets.
|
||||||
|
- Fix output page cropped when using the sandwich renderer and OCR is skipped on a rotated and image-processed page
|
||||||
|
- A warning is now issued when old versions of Ghostscript are used in cases known to cause issues with non-Latin characters
|
||||||
|
- Fix a few parameter validation checks for ``-output-type pdfa-1`` and ``pdfa-2``
|
||||||
|
|
||||||
|
|
||||||
v5.4.4
|
v5.4.4
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
+35
-8
@@ -14,6 +14,7 @@ import logging
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
import PyPDF2 as pypdf
|
import PyPDF2 as pypdf
|
||||||
|
import PIL
|
||||||
|
|
||||||
import ruffus.ruffus_exceptions as ruffus_exceptions
|
import ruffus.ruffus_exceptions as ruffus_exceptions
|
||||||
import ruffus.cmdline as cmdline
|
import ruffus.cmdline as cmdline
|
||||||
@@ -23,7 +24,7 @@ from .pipeline import JobContext, JobContextManager, \
|
|||||||
cleanup_working_files, build_pipeline
|
cleanup_working_files, build_pipeline
|
||||||
from .pdfa import file_claims_pdfa
|
from .pdfa import file_claims_pdfa
|
||||||
from .helpers import is_iterable_notstr, re_symlink, is_file_writable
|
from .helpers import is_iterable_notstr, re_symlink, is_file_writable
|
||||||
from .exec import tesseract, qpdf
|
from .exec import tesseract, qpdf, ghostscript
|
||||||
from . import PROGRAM_NAME, VERSION
|
from . import PROGRAM_NAME, VERSION
|
||||||
|
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
@@ -241,6 +242,11 @@ ocrsettings.add_argument(
|
|||||||
advanced = parser.add_argument_group(
|
advanced = parser.add_argument_group(
|
||||||
"Advanced",
|
"Advanced",
|
||||||
"Advanced options to control Tesseract's OCR behavior")
|
"Advanced options to control Tesseract's OCR behavior")
|
||||||
|
advanced.add_argument(
|
||||||
|
'--max-image-mpixels', action='store', type=float, metavar='MPixels',
|
||||||
|
help="Set maximum number of pixels to unpack before treating an image as a "
|
||||||
|
"decompression bomb",
|
||||||
|
default=128.0)
|
||||||
advanced.add_argument(
|
advanced.add_argument(
|
||||||
'--tesseract-config', action='append', metavar='CFG', default=[],
|
'--tesseract-config', action='append', metavar='CFG', default=[],
|
||||||
help="Additional Tesseract configuration files -- see documentation")
|
help="Additional Tesseract configuration files -- see documentation")
|
||||||
@@ -321,11 +327,12 @@ def check_options_languages(options, _log):
|
|||||||
if '+' in options.language[0]:
|
if '+' in options.language[0]:
|
||||||
options.language = options.language[0].split('+')
|
options.language = options.language[0].split('+')
|
||||||
|
|
||||||
if not set(options.language).issubset(tesseract.languages()):
|
languages = set(options.language)
|
||||||
|
if not languages.issubset(tesseract.languages()):
|
||||||
msg = (
|
msg = (
|
||||||
"The installed version of tesseract does not have language "
|
"The installed version of tesseract does not have language "
|
||||||
"data for the following requested languages: \n")
|
"data for the following requested languages: \n")
|
||||||
for lang in (set(options.language) - tesseract.languages()):
|
for lang in (languages - tesseract.languages()):
|
||||||
msg += lang + '\n'
|
msg += lang + '\n'
|
||||||
raise MissingDependencyError(msg)
|
raise MissingDependencyError(msg)
|
||||||
|
|
||||||
@@ -348,7 +355,8 @@ def check_options_output(options, log):
|
|||||||
options.pdf_renderer = 'sandwich'
|
options.pdf_renderer = 'sandwich'
|
||||||
|
|
||||||
if options.pdf_renderer == 'tesseract':
|
if options.pdf_renderer == 'tesseract':
|
||||||
if tesseract.version() < '3.05' and options.output_type == 'pdfa':
|
if tesseract.version() < '3.05' and \
|
||||||
|
options.output_type.startswith('pdfa'):
|
||||||
log.warning(
|
log.warning(
|
||||||
"For best results use --pdf-renderer=tesseract "
|
"For best results use --pdf-renderer=tesseract "
|
||||||
"--output-type=pdf to disable PDF/A generation via "
|
"--output-type=pdf to disable PDF/A generation via "
|
||||||
@@ -412,9 +420,9 @@ def check_options_ocr_behavior(options, log):
|
|||||||
# if options.redo_ocr and (options.skip_text or options.force_ocr):
|
# if options.redo_ocr and (options.skip_text or options.force_ocr):
|
||||||
# raise argparse.ArgumentError(
|
# raise argparse.ArgumentError(
|
||||||
# "Error: --redo-ocr and other OCR options are incompatible.")
|
# "Error: --redo-ocr and other OCR options are incompatible.")
|
||||||
|
languages = set(options.language)
|
||||||
if options.pdf_renderer == 'hocr' and \
|
if options.pdf_renderer == 'hocr' and \
|
||||||
not set(options.language).issubset(HOCR_OK_LANGS):
|
not languages.issubset(HOCR_OK_LANGS):
|
||||||
msg = (
|
msg = (
|
||||||
"The 'hocr' PDF renderer is known to cause problems with one "
|
"The 'hocr' PDF renderer is known to cause problems with one "
|
||||||
"or more of the languages in your document. ")
|
"or more of the languages in your document. ")
|
||||||
@@ -427,6 +435,15 @@ def check_options_ocr_behavior(options, log):
|
|||||||
"Use --pdf-renderer tesseract --output-type pdf to avoid "
|
"Use --pdf-renderer tesseract --output-type pdf to avoid "
|
||||||
"this issue")
|
"this issue")
|
||||||
log.warning(msg)
|
log.warning(msg)
|
||||||
|
elif ghostscript.version() < '9.20' and \
|
||||||
|
not languages.issubset(HOCR_OK_LANGS) \
|
||||||
|
and options.output_type != 'pdf':
|
||||||
|
msg = (
|
||||||
|
"The installed version of Ghostscript does not work correctly "
|
||||||
|
"with the OCR languages you specified. Use --output-type pdf or "
|
||||||
|
"upgrade to Ghostscript 9.20 or later to avoid this issue.")
|
||||||
|
msg += "Found Ghostscript {}".format(ghostscript.version())
|
||||||
|
log.warning(msg)
|
||||||
|
|
||||||
|
|
||||||
def check_options_advanced(options, log):
|
def check_options_advanced(options, log):
|
||||||
@@ -441,10 +458,10 @@ def check_options_advanced(options, log):
|
|||||||
"--pdf-renderer tess4 requires Tesseract 4.x "
|
"--pdf-renderer tess4 requires Tesseract 4.x "
|
||||||
"commit 3d9fb3b or later")
|
"commit 3d9fb3b or later")
|
||||||
if options.pdfa_image_compression != 'auto' and \
|
if options.pdfa_image_compression != 'auto' and \
|
||||||
options.output_type != 'pdfa':
|
options.output_type.startswith('pdfa'):
|
||||||
log.warning(
|
log.warning(
|
||||||
"--pdfa-image-compression argument has no effect when "
|
"--pdfa-image-compression argument has no effect when "
|
||||||
"--output-type is not 'pdfa'"
|
"--output-type is not 'pdfa', 'pdfa-1', or 'pdfa-2'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -587,6 +604,12 @@ def do_ruffus_exception(ruffus_five_tuple, options, log):
|
|||||||
|
|
||||||
"""))
|
"""))
|
||||||
exit_code = ExitCode.encrypted_pdf
|
exit_code = ExitCode.encrypted_pdf
|
||||||
|
elif exc_name == 'PIL.Image.DecompressionBombError':
|
||||||
|
msg = cleanup_ruffus_error_message(exc_value)
|
||||||
|
msg += ("\nUse the --max-image-mpixels argument to set increase the "
|
||||||
|
"maximum number of megapixels to accept.")
|
||||||
|
log.error(msg)
|
||||||
|
exit_code = ExitCode.input_file
|
||||||
|
|
||||||
if exit_code is not None:
|
if exit_code is not None:
|
||||||
return exit_code
|
return exit_code
|
||||||
@@ -686,6 +709,10 @@ def run_pipeline():
|
|||||||
|
|
||||||
check_options(options, _log)
|
check_options(options, _log)
|
||||||
|
|
||||||
|
PIL.Image.MAX_IMAGE_PIXELS = int(options.max_image_mpixels * 1000000)
|
||||||
|
if PIL.Image.MAX_IMAGE_PIXELS == 0:
|
||||||
|
PIL.Image.MAX_IMAGE_PIXELS = None
|
||||||
|
|
||||||
# Complain about qpdf version < 7.0.0
|
# Complain about qpdf version < 7.0.0
|
||||||
# Suppress the warning if in the test suite, since there are no PPAs
|
# Suppress the warning if in the test suite, since there are no PPAs
|
||||||
# for qpdf 7.0.0 for Ubuntu trusty (i.e. Travis)
|
# for qpdf 7.0.0 for Ubuntu trusty (i.e. Travis)
|
||||||
|
|||||||
@@ -29,8 +29,14 @@ def get_version(program, *,
|
|||||||
stdout=PIPE, stderr=STDOUT, check=True)
|
stdout=PIPE, stderr=STDOUT, check=True)
|
||||||
output = proc.stdout
|
output = proc.stdout
|
||||||
except CalledProcessError as e:
|
except CalledProcessError as e:
|
||||||
raise MissingDependencyError(
|
if get_program(program) == program:
|
||||||
"Could not find program '{}' on the PATH".format(program)) from e
|
raise MissingDependencyError(
|
||||||
|
"Could not find program '{}' on the PATH".format(
|
||||||
|
program)) from e
|
||||||
|
else:
|
||||||
|
raise MissingDependencyError(
|
||||||
|
"Could not find program '{}'".format(
|
||||||
|
get_program(program))) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
version = re.match(regex, output.strip()).group(1)
|
version = re.match(regex, output.strip()).group(1)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log,
|
|||||||
:param yres:
|
:param yres:
|
||||||
:param raster_device:
|
:param raster_device:
|
||||||
:param log:
|
:param log:
|
||||||
:param pageno: page number to rasterize
|
:param pageno: page number to rasterize (beginning at page 1)
|
||||||
:param page_dpi: resolution tuple (x, y) overriding output image DPI
|
:param page_dpi: resolution tuple (x, y) overriding output image DPI
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -55,8 +55,8 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log,
|
|||||||
'-sDEVICE=%s' % raster_device,
|
'-sDEVICE=%s' % raster_device,
|
||||||
'-dFirstPage=%i' % pageno,
|
'-dFirstPage=%i' % pageno,
|
||||||
'-dLastPage=%i' % pageno,
|
'-dLastPage=%i' % pageno,
|
||||||
'-o', tmp.name,
|
|
||||||
'-r{0}x{1}'.format(str(int_res[0]), str(int_res[1])),
|
'-r{0}x{1}'.format(str(int_res[0]), str(int_res[1])),
|
||||||
|
'-o', tmp.name,
|
||||||
fspath(input_file)
|
fspath(input_file)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -75,6 +75,7 @@ def rasterize_pdf(input_file, output_file, xres, yres, raster_device, log,
|
|||||||
# if the resolution happens to be fractional, then the discrepancy
|
# if the resolution happens to be fractional, then the discrepancy
|
||||||
# would change the size of the output page, especially if the DPI
|
# would change the size of the output page, especially if the DPI
|
||||||
# is quite low. Resize the image to the expected size
|
# is quite low. Resize the image to the expected size
|
||||||
|
|
||||||
tmp.seek(0)
|
tmp.seek(0)
|
||||||
with Image.open(tmp) as im:
|
with Image.open(tmp) as im:
|
||||||
expected_size = round(im.size[0] / int_res[0] * res[0]), \
|
expected_size = round(im.size[0] / int_res[0] * res[0]), \
|
||||||
|
|||||||
@@ -279,6 +279,10 @@ def use_skip_page(text_only, skip_pdf, output_pdf, output_text):
|
|||||||
with open(output_pdf, 'wb') as out:
|
with open(output_pdf, 'wb') as out:
|
||||||
pdf_out = pypdf.PdfFileWriter()
|
pdf_out = pypdf.PdfFileWriter()
|
||||||
w, h = page0.mediaBox.getWidth(), page0.mediaBox.getHeight()
|
w, h = page0.mediaBox.getWidth(), page0.mediaBox.getHeight()
|
||||||
|
# If skip page has a /Rotate key, replicate the rotation
|
||||||
|
rotation = int(page0.get('/Rotate', 0))
|
||||||
|
if rotation % 180 == 90:
|
||||||
|
w, h = h, w
|
||||||
pdf_out.addBlankPage(w, h)
|
pdf_out.addBlankPage(w, h)
|
||||||
pdf_out.write(out)
|
pdf_out.write(out)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# setup.py lists a separate set of requirements that are looser to simplify
|
# setup.py lists a separate set of requirements that are looser to simplify
|
||||||
# installation
|
# installation
|
||||||
ruffus == 2.6.3
|
ruffus == 2.6.3
|
||||||
Pillow == 4.3.0
|
Pillow == 5.0.0
|
||||||
reportlab == 3.4.0
|
reportlab == 3.4.0
|
||||||
PyPDF2 == 1.26.0
|
PyPDF2 == 1.26.0
|
||||||
img2pdf == 0.2.4
|
img2pdf == 0.2.4
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ not permitted in PDF/A-2, overprint mode not set"""
|
|||||||
def main():
|
def main():
|
||||||
if '--version' in sys.argv:
|
if '--version' in sys.argv:
|
||||||
print('9.20')
|
print('9.20')
|
||||||
print('SPOOFED: ' + os.path.basename(__filename__))
|
print('SPOOFED: ' + os.path.basename(__file__))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
gs_args = ['gs'] + sys.argv[1:]
|
gs_args = ['gs'] + sys.argv[1:]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def real_ghostscript(argv):
|
|||||||
def main():
|
def main():
|
||||||
if '--version' in sys.argv:
|
if '--version' in sys.argv:
|
||||||
print('9.20')
|
print('9.20')
|
||||||
print('SPOOFED: ' + os.path.basename(__filename__))
|
print('SPOOFED: ' + os.path.basename(__file__))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Unless some argument is calling for PDFA generation, forward to
|
# Unless some argument is calling for PDFA generation, forward to
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def real_ghostscript(argv):
|
|||||||
def main():
|
def main():
|
||||||
if '--version' in sys.argv:
|
if '--version' in sys.argv:
|
||||||
print('9.20')
|
print('9.20')
|
||||||
print('SPOOFED: ' + os.path.basename(__filename__))
|
print('SPOOFED: ' + os.path.basename(__file__))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# For any rendering calls (device == pdfwrite) call real ghostscript
|
# For any rendering calls (device == pdfwrite) call real ghostscript
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def real_ghostscript(argv):
|
|||||||
def main():
|
def main():
|
||||||
if '--version' in sys.argv:
|
if '--version' in sys.argv:
|
||||||
print('9.20')
|
print('9.20')
|
||||||
print('SPOOFED: ' + os.path.basename(__filename__))
|
print('SPOOFED: ' + os.path.basename(__file__))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# For any rasterize calls (device != pdfwrite) call real ghostscript
|
# For any rasterize calls (device != pdfwrite) call real ghostscript
|
||||||
|
|||||||
@@ -73,10 +73,11 @@ def main():
|
|||||||
output = sys.argv[-3]
|
output = sys.argv[-3]
|
||||||
with Image.open(inputf) as im:
|
with Image.open(inputf) as im:
|
||||||
dpi = im.info['dpi']
|
dpi = im.info['dpi']
|
||||||
imsize = im.size[0] * dpi[0] / 72, im.size[1] * dpi[1] / 72
|
pagesize = im.size[0] / dpi[0], im.size[1] / dpi[1]
|
||||||
|
ptsize = pagesize[0] * 72, pagesize[1] * 72
|
||||||
|
|
||||||
pdf_out = pypdf.PdfFileWriter()
|
pdf_out = pypdf.PdfFileWriter()
|
||||||
pdf_out.addBlankPage(imsize[0], imsize[1])
|
pdf_out.addBlankPage(ptsize[0], ptsize[1])
|
||||||
with open(output + '.pdf', 'wb') as f:
|
with open(output + '.pdf', 'wb') as f:
|
||||||
pdf_out.write(f)
|
pdf_out.write(f)
|
||||||
with open(output + '.txt', 'w') as f:
|
with open(output + '.txt', 'w') as f:
|
||||||
|
|||||||
+51
-6
@@ -14,6 +14,8 @@ from ocrmypdf.exec import ghostscript, tesseract, qpdf
|
|||||||
import logging
|
import logging
|
||||||
from math import isclose
|
from math import isclose
|
||||||
|
|
||||||
|
import PIL
|
||||||
|
|
||||||
|
|
||||||
check_ocrmypdf = pytest.helpers.check_ocrmypdf
|
check_ocrmypdf = pytest.helpers.check_ocrmypdf
|
||||||
run_ocrmypdf = pytest.helpers.run_ocrmypdf
|
run_ocrmypdf = pytest.helpers.run_ocrmypdf
|
||||||
@@ -69,11 +71,10 @@ def test_quick(spoof_tesseract_cache, resources, outpdf):
|
|||||||
check_ocrmypdf(resources / 'ccitt.pdf', outpdf, env=spoof_tesseract_cache)
|
check_ocrmypdf(resources / 'ccitt.pdf', outpdf, env=spoof_tesseract_cache)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings('ignore:Image size')
|
|
||||||
def test_deskew(spoof_tesseract_noop, resources, outdir):
|
def test_deskew(spoof_tesseract_noop, resources, outdir):
|
||||||
# Run with deskew
|
# Run with deskew
|
||||||
deskewed_pdf = check_ocrmypdf(
|
deskewed_pdf = check_ocrmypdf(
|
||||||
resources / 'skew.pdf', outdir / 'skew.pdf', '-d', '-v', '1',
|
resources / 'skew.pdf', outdir / 'skew.pdf', '-d',
|
||||||
env=spoof_tesseract_noop)
|
env=spoof_tesseract_noop)
|
||||||
|
|
||||||
# Now render as an image again and use Leptonica to find the skew angle
|
# Now render as an image again and use Leptonica to find the skew angle
|
||||||
@@ -88,7 +89,8 @@ def test_deskew(spoof_tesseract_noop, resources, outdir):
|
|||||||
xres=150,
|
xres=150,
|
||||||
yres=150,
|
yres=150,
|
||||||
raster_device='pngmono',
|
raster_device='pngmono',
|
||||||
log=log)
|
log=log,
|
||||||
|
pageno=1)
|
||||||
|
|
||||||
from ocrmypdf.leptonica import Pix
|
from ocrmypdf.leptonica import Pix
|
||||||
pix = Pix.read(str(deskewed_png))
|
pix = Pix.read(str(deskewed_png))
|
||||||
@@ -127,8 +129,8 @@ def test_remove_background(spoof_tesseract_noop, resources, outdir):
|
|||||||
xres=100,
|
xres=100,
|
||||||
yres=100,
|
yres=100,
|
||||||
raster_device='png16m',
|
raster_device='png16m',
|
||||||
log=log)
|
log=log,
|
||||||
|
pageno=1)
|
||||||
|
|
||||||
# The output image should contain pure white and black
|
# The output image should contain pure white and black
|
||||||
im = Image.open(output_png)
|
im = Image.open(output_png)
|
||||||
@@ -521,7 +523,9 @@ def test_tesseract_image_too_big(renderer, spoof_tesseract_big_image_error,
|
|||||||
resources, outpdf):
|
resources, outpdf):
|
||||||
check_ocrmypdf(
|
check_ocrmypdf(
|
||||||
resources / 'hugemono.pdf', outpdf, '-r',
|
resources / 'hugemono.pdf', outpdf, '-r',
|
||||||
'--pdf-renderer', renderer, env=spoof_tesseract_big_image_error)
|
'--pdf-renderer', renderer,
|
||||||
|
'--max-image-mpixels', '0',
|
||||||
|
env=spoof_tesseract_big_image_error)
|
||||||
|
|
||||||
|
|
||||||
def test_no_unpaper(resources, no_outpdf):
|
def test_no_unpaper(resources, no_outpdf):
|
||||||
@@ -1037,3 +1041,44 @@ def test_bad_utf8(spoof_tess_bad_utf8, renderer, resources, no_outpdf):
|
|||||||
assert p.returncode != 0
|
assert p.returncode != 0
|
||||||
assert 'not utf-8' in err, "should whine about utf-8"
|
assert 'not utf-8' in err, "should whine about utf-8"
|
||||||
assert '\\x96' in err, 'should repeat backslash encoded output'
|
assert '\\x96' in err, 'should repeat backslash encoded output'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not tesseract.has_textonly_pdf(),
|
||||||
|
reason="issue only affects sandwich")
|
||||||
|
def test_rotate_deskew_timeout(resources, outdir):
|
||||||
|
check_ocrmypdf(
|
||||||
|
resources / 'rotated_skew.pdf',
|
||||||
|
outdir / 'deskewed.pdf',
|
||||||
|
'--deskew',
|
||||||
|
'--tesseract-timeout', '0',
|
||||||
|
'--pdf-renderer', 'sandwich'
|
||||||
|
)
|
||||||
|
|
||||||
|
correlation = check_monochrome_correlation(
|
||||||
|
outdir,
|
||||||
|
reference_pdf=resources / 'ccitt.pdf',
|
||||||
|
reference_pageno=1,
|
||||||
|
test_pdf=outdir / 'deskewed.pdf',
|
||||||
|
test_pageno=1)
|
||||||
|
|
||||||
|
# Confirm that the page still got deskewed
|
||||||
|
assert correlation > 0.50
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
PIL.PILLOW_VERSION < '5.0.0',
|
||||||
|
reason="Pillow < 5.0.0 doesn't raise the exception")
|
||||||
|
def test_decompression_bomb(resources, outpdf):
|
||||||
|
p, out, err = run_ocrmypdf(
|
||||||
|
resources / 'hugemono.pdf',
|
||||||
|
outpdf
|
||||||
|
)
|
||||||
|
assert 'decompression bomb' in err
|
||||||
|
|
||||||
|
p, out, err = run_ocrmypdf(
|
||||||
|
resources / 'hugemono.pdf',
|
||||||
|
outpdf,
|
||||||
|
'--max-image-mpixels', '2000'
|
||||||
|
)
|
||||||
|
assert p.returncode == 0
|
||||||
Reference in New Issue
Block a user