Compare commits

...
9 Commits
11 changed files with 77 additions and 112 deletions
+7 -5
View File
@@ -153,12 +153,13 @@ stages:
matrix: matrix:
Python37: Python37:
python.version: "3.7" python.version: "3.7"
Python38: # Python38:
python.version: "3.8" # python.version: "3.8"
steps: steps:
- task: UsePythonVersion@0 # https://github.com/actions/virtual-environments/issues/664
inputs: # - task: UsePythonVersion@0
versionSpec: "$(python.version)" # inputs:
# versionSpec: "$(python.version)"
- bash: | - bash: |
brew update brew update
brew unlink python@2 brew unlink python@2
@@ -169,6 +170,7 @@ stages:
leptonica \ leptonica \
openjpeg \ openjpeg \
pngquant \ pngquant \
python \
tesseract \ tesseract \
unpaper unpaper
displayName: "Install system packages" displayName: "Install system packages"
+3 -2
View File
@@ -137,11 +137,12 @@ Installing the latest version on Ubuntu 18.04 LTS
------------------------------------------------- -------------------------------------------------
Ubuntu 18.04 includes ocrmypdf 6.1.2 - you can install that with ``apt``, but Ubuntu 18.04 includes ocrmypdf 6.1.2 - you can install that with ``apt``, but
it is quite old now. To install a more recent version, first install several it is quite old now. To install a more recent version, uninstall the old version
system dependencies: of ocrmypdf, and install the following dependencies:
.. code-block:: bash .. code-block:: bash
sudo apt-get -y remove ocrmypdf
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install \ sudo apt-get -y install \
ghostscript \ ghostscript \
+8
View File
@@ -13,6 +13,14 @@ Note that it is licensed under GPLv3, so scripts that
``import ocrmypdf`` and are released publicly should probably also be ``import ocrmypdf`` and are released publicly should probably also be
licensed under GPLv3. licensed under GPLv3.
v9.8.0
======
- Fixed issue where only the first PNG (FlateDecode) image in a file would be
considered for optimization. File sizes should be improved from here on.
- Fixed a startup crash when the chosen language was Japanese (#543).
- Added options to configure polling and log level to watcher.py.
v9.7.2 v9.7.2
====== ======
+9 -2
View File
@@ -25,6 +25,7 @@ from pathlib import Path
import pikepdf import pikepdf
from watchdog.events import PatternMatchingEventHandler from watchdog.events import PatternMatchingEventHandler
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.observers.polling import PollingObserver
import ocrmypdf import ocrmypdf
@@ -37,7 +38,8 @@ ON_SUCCESS_DELETE = bool(os.getenv('OCR_ON_SUCCESS_DELETE', False))
DESKEW = bool(os.getenv('OCR_DESKEW', False)) DESKEW = bool(os.getenv('OCR_DESKEW', False))
OCR_JSON_SETTINGS = json.loads(os.getenv('OCR_JSON_SETTINGS', '{}')) OCR_JSON_SETTINGS = json.loads(os.getenv('OCR_JSON_SETTINGS', '{}'))
POLL_NEW_FILE_SECONDS = os.getenv('OCR_POLL_NEW_FILE_SECONDS', 1) POLL_NEW_FILE_SECONDS = os.getenv('OCR_POLL_NEW_FILE_SECONDS', 1)
LOGLEVEL = os.environ.get('OCR_LOGLEVEL', 'INFO').upper() USE_POLLING = bool(os.getenv('OCR_USE_POLLING', False))
LOGLEVEL = os.getenv('OCR_LOGLEVEL', 'INFO').upper()
PATTERNS = ['*.pdf'] PATTERNS = ['*.pdf']
log = logging.getLogger('ocrmypdf-watcher') log = logging.getLogger('ocrmypdf-watcher')
@@ -112,6 +114,7 @@ def main():
ocrmypdf.configure_logging( ocrmypdf.configure_logging(
verbosity=ocrmypdf.Verbosity.default, manage_root_logger=True verbosity=ocrmypdf.Verbosity.default, manage_root_logger=True
) )
log.setLevel(LOGLEVEL)
log.info( log.info(
f"Starting OCRmyPDF watcher with config:\n" f"Starting OCRmyPDF watcher with config:\n"
f"Input Directory: {INPUT_DIRECTORY}\n" f"Input Directory: {INPUT_DIRECTORY}\n"
@@ -126,6 +129,7 @@ def main():
f"DESKEW: {DESKEW}\n" f"DESKEW: {DESKEW}\n"
f"ARGS: {OCR_JSON_SETTINGS}\n" f"ARGS: {OCR_JSON_SETTINGS}\n"
f"POLL_NEW_FILE_SECONDS: {POLL_NEW_FILE_SECONDS}\n" f"POLL_NEW_FILE_SECONDS: {POLL_NEW_FILE_SECONDS}\n"
f"USE_POLLING: {USE_POLLING}\n"
f"LOGLEVEL: {LOGLEVEL}\n" f"LOGLEVEL: {LOGLEVEL}\n"
) )
@@ -134,7 +138,10 @@ def main():
sys.exit(1) sys.exit(1)
handler = HandleObserverEvent(patterns=PATTERNS) handler = HandleObserverEvent(patterns=PATTERNS)
observer = Observer() if USE_POLLING:
observer = PollingObserver()
else:
observer = Observer()
observer.schedule(handler, INPUT_DIRECTORY, recursive=True) observer.schedule(handler, INPUT_DIRECTORY, recursive=True)
observer.start() observer.start()
try: try:
-2
View File
@@ -1,2 +0,0 @@
twine >= 1.8.1
coverage >= 4.5
+4 -4
View File
@@ -2,9 +2,9 @@
# 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
cffi == 1.14.0 cffi == 1.14.0
img2pdf == 0.3.3 img2pdf == 0.3.4
pdfminer.six == 20200402 pdfminer.six == 20200402
pikepdf == 1.10.2 pikepdf == 1.11.1
Pillow == 7.0.0 Pillow == 7.1.1
reportlab == 3.5.34 reportlab == 3.5.34
tqdm == 4.42.1 tqdm == 4.45.0
+3 -9
View File
@@ -77,20 +77,14 @@ def has_textonly_pdf(tesseract_env=None, langs=None):
args_tess = tess_base_args(langs, engine_mode=None) + ['--print-parameters', 'pdf'] args_tess = tess_base_args(langs, engine_mode=None) + ['--print-parameters', 'pdf']
params = '' params = ''
try: try:
proc = run( # print-parameters can return non-UTF8 if the parameters are so initialized
args_tess, proc = run(args_tess, check=True, stdout=PIPE, stderr=STDOUT, env=tesseract_env)
check=True,
universal_newlines=True,
stdout=PIPE,
stderr=STDOUT,
env=tesseract_env,
)
params = proc.stdout params = proc.stdout
except CalledProcessError as e: except CalledProcessError as e:
raise MissingDependencyError( raise MissingDependencyError(
"Could not --print-parameters from tesseract" "Could not --print-parameters from tesseract"
) from e ) from e
if 'textonly_pdf' in params: if b'textonly_pdf' in params:
return True return True
return False return False
+2 -2
View File
@@ -421,9 +421,9 @@ def transcode_pngs(pike, images, image_name_fn, root, log, options):
) )
continue continue
if compdata.type == leptonica.lept.L_FLATE_ENCODE: if compdata.type == leptonica.lept.L_FLATE_ENCODE:
return rewrite_png(pike, im_obj, compdata, log) rewrite_png(pike, im_obj, compdata, log)
elif compdata.type == leptonica.lept.L_G4_ENCODE: elif compdata.type == leptonica.lept.L_G4_ENCODE:
return rewrite_png_as_g4(pike, im_obj, compdata, log) rewrite_png_as_g4(pike, im_obj, compdata, log)
def rewrite_png_as_g4(pike, im_obj, compdata, log): def rewrite_png_as_g4(pike, im_obj, compdata, log):
-80
View File
@@ -1,80 +0,0 @@
#!/usr/bin/env python3
# © 2017 James R. Barlow: github.com/jbarlow83
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import sys
"""Tesseract bad utf8 spoof
In 'hocr' mode or 'pdf' mode, return error code 1 and some non-Unicode
text because tesseract seems to do that in some cases related to
language pack version mismatches
"""
VERSION_STRING = '''tesseract 4.0.0
leptonica-1.77.0
libjpeg 9c : libpng 1.6.35 : libtiff 4.0.10 : zlib 1.2.11 : libopenjp2 2.3.0
Found AVX2
Found AVX
Found SSE
SPOOFED
'''
# Japanese "Invalid UTF-8" encoded in Shift JIS
BAD_UTF8 = b'\x96\xb3\x8c\xf8\x82\xc8UTF-8\x0a'
def main():
if sys.argv[1] == '--version':
print(VERSION_STRING, file=sys.stderr)
sys.exit(0)
elif sys.argv[1] == '--list-langs':
print('List of available languages (1):\neng', file=sys.stderr)
sys.exit(0)
elif sys.argv[-2] == '--print-parameters':
print("Some parameters", file=sys.stderr)
print("textonly_pdf\t1\tSome help text")
sys.exit(0)
elif sys.argv[-2] in ('hocr', 'pdf'):
sys.stdout.buffer.write(BAD_UTF8)
sys.exit(1)
elif sys.argv[-1] == 'stdout':
# input file is at sys.argv[-2] but we don't look at it
print(
"""Orientation: 0
Orientation in degrees: 0
Orientation confidence: 100.00
Script: 1
Script confidence: 100.00""",
file=sys.stderr,
)
else:
print("Spoof doesn't understand arguments", file=sys.stderr)
print(sys.argv, file=sys.stderr)
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main()
+41 -1
View File
@@ -18,10 +18,12 @@
import logging import logging
from os import fspath from os import fspath
from pathlib import Path from pathlib import Path
from unittest.mock import patch
import img2pdf
import pikepdf import pikepdf
import pytest import pytest
from PIL import Image from PIL import Image, ImageDraw
from ocrmypdf import optimize as opt from ocrmypdf import optimize as opt
from ocrmypdf.exec import jbig2enc, pngquant from ocrmypdf.exec import jbig2enc, pngquant
@@ -130,3 +132,41 @@ def test_flate_to_jbig2(resources, outdir, spoof_tesseract_noop):
pdf = pikepdf.open(outdir / 'out.pdf') pdf = pikepdf.open(outdir / 'out.pdf')
pim = pikepdf.PdfImage(next(iter(pdf.pages[0].images.values()))) pim = pikepdf.PdfImage(next(iter(pdf.pages[0].images.values())))
assert pim.filters[0] == '/JBIG2Decode' assert pim.filters[0] == '/JBIG2Decode'
def test_multiple_pngs(resources, outdir, spoof_tesseract_noop):
with Path.open(outdir / 'in.pdf', 'wb') as inpdf:
img2pdf.convert(
fspath(resources / 'baiona_colormapped.png'),
fspath(resources / 'baiona_gray.png'),
with_pdfrw=False,
outputstream=inpdf,
)
def mockquant(input_file, output_file, _quality_min, _quality_max):
with Image.open(input_file) as im:
draw = ImageDraw.Draw(im)
draw.rectangle((0, 0, im.width, im.height), fill=128)
im.save(output_file)
with patch('ocrmypdf.optimize.pngquant.quantize', new=mockquant):
check_ocrmypdf(
outdir / 'in.pdf',
outdir / 'out.pdf',
'--optimize',
'3',
'--jobs',
'1',
'--use-threads',
'--output-type',
'pdf',
env=spoof_tesseract_noop,
)
with pikepdf.open(outdir / 'in.pdf') as inpdf, pikepdf.open(
outdir / 'out.pdf'
) as outpdf:
for n in range(len(inpdf.pages)):
inim = next(iter(inpdf.pages[n].images.values()))
outim = next(iter(outpdf.pages[n].images.values()))
assert len(outim.read_raw_bytes()) < len(inim.read_raw_bytes()), n
-5
View File
@@ -33,11 +33,6 @@ run_ocrmypdf_api = pytest.helpers.run_ocrmypdf
spoof = pytest.helpers.spoof spoof = pytest.helpers.spoof
@pytest.fixture
def spoof_tess_bad_utf8(tmp_path_factory):
return spoof(tmp_path_factory, tesseract='tesseract_badutf8.py')
def test_stdin(spoof_tesseract_noop, ocrmypdf_exec, resources, outpdf): def test_stdin(spoof_tesseract_noop, ocrmypdf_exec, resources, outpdf):
input_file = str(resources / 'francais.pdf') input_file = str(resources / 'francais.pdf')
output_file = str(outpdf) output_file = str(outpdf)