Compare commits

...
19 Commits
Author SHA1 Message Date
James R. Barlow 7691ba8535 v9.1.1 release notes 2019-11-18 15:17:00 -08:00
James R. Barlow b787a369ee Fix reference to Alpine apk add 2019-11-18 15:13:42 -08:00
James R. Barlow 9fb8b267af docker: use get-pip to install pip
Smaller download, needed for manylinux2010.
2019-11-16 15:21:45 -08:00
James R. Barlow 0a08d6ce1f Update version of pdfminer.six supported 2019-11-13 01:45:06 -08:00
James R. Barlow f517efe819 docs: wsl - get-pip.py 2019-11-12 15:01:15 -08:00
James R. Barlow 5f5421f23d test: further fixes to test_report_file_size 2019-11-12 01:14:21 -08:00
James R. Barlow 703b6db95c test: fix test_report_file_size 2019-11-11 23:00:08 -08:00
James R. Barlow 000040d497 v9.1.0 release notes 2019-11-11 22:39:33 -08:00
James R. Barlow 5bd6665b49 Use pikepdf 1.7.0 to improve Python 3.8 support 2019-11-11 22:36:38 -08:00
James R. Barlow 1c303afe21 docs: fix installation instructions for pikepdf manylinux2010 wheels 2019-11-11 22:22:30 -08:00
James R. Barlow 11a5c80917 travis: enable Py 3.8 2019-11-11 22:19:15 -08:00
James R. Barlow 9b2ab92913 tesseract: fix exception when logger is RootLogger 2019-11-11 22:19:01 -08:00
James R. Barlow 0c4b69ec5a Fix lint warning about missing cur_item 2019-11-09 14:56:43 -08:00
James R. Barlow 45bea1c0e0 Import and docstring cleanup 2019-11-09 14:56:30 -08:00
James R. Barlow db914d4cd1 Report missing optional dependencies as possible cause of file size increase 2019-11-09 14:21:57 -08:00
James R. Barlow df4a8faecd docs: mention systemd for batches 2019-11-08 03:24:54 -08:00
James R. Barlow 1273e7aeda docs: document optimization 2019-11-08 03:22:28 -08:00
James R. Barlow e13a673b1a docs: mention how to suppress progbar 2019-11-08 02:59:02 -08:00
James R. Barlow 979b0bcaed tesseract: refactor logging 2019-11-05 15:38:09 -08:00
22 changed files with 208 additions and 57 deletions
+9 -4
View File
@@ -10,16 +10,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential autoconf automake libtool \
libleptonica-dev \
zlib1g-dev \
python3-setuptools \
python3-pip \
wget \
python3 \
python3-distutils \
ca-certificates \
curl \
git
# Get the latest pip (Ubuntu version doesn't support manylinux2010)
RUN \
curl https://bootstrap.pypa.io/get-pip.py | python3
# Compile and install jbig2
# Needs libleptonica-dev, zlib1g-dev
RUN \
mkdir jbig2 \
&& wget -q https://github.com/agl/jbig2enc/archive/0.29.tar.gz -O - | \
&& curl -L https://github.com/agl/jbig2enc/archive/0.29.tar.gz | \
tar xz -C jbig2 --strip-components=1 \
&& cd jbig2 \
&& ./autogen.sh && ./configure && make && make install \
+24 -24
View File
@@ -86,30 +86,30 @@ matrix:
- tesseract-ocr-eng
- tesseract-ocr-fra
- unpaper
# - os: linux
# dist: bionic
# sudo: required
# language: python
# python: "3.8"
# env:
# - DIST=bionic
# addons:
# apt:
# update: true
# sources:
# - sourceline: "ppa:alex-p/tesseract-ocr"
# packages:
# - ghostscript
# - libexempi3
# - libffi-dev
# - pngquant
# - poppler-utils
# - qpdf
# - tesseract-ocr
# - tesseract-ocr-deu
# - tesseract-ocr-eng
# - tesseract-ocr-fra
# - unpaper
- os: linux
dist: bionic
sudo: required
language: python
python: "3.8"
env:
- DIST=bionic
addons:
apt:
update: true
sources:
- sourceline: "ppa:alex-p/tesseract-ocr"
packages:
- ghostscript
- libexempi3
- libffi-dev
- pngquant
- poppler-utils
- qpdf
- tesseract-ocr
- tesseract-ocr-deu
- tesseract-ocr-eng
- tesseract-ocr-fra
- unpaper
- os: osx
language: generic
addons:
+2 -1
View File
@@ -71,7 +71,8 @@ Progress monitoring
OCRmyPDF uses the ``tqdm`` package to implement its progress bars.
:func:`ocrmypdf.configure_logging` will set up logging output to
``sys.stderr`` in a way that is compatible with the display of the
progress bar.
progress bar. Use ``ocrmypdf.ocr(...progress_bar=False)`` to disable
the progress bar.
Exceptions
----------
+3
View File
@@ -244,6 +244,9 @@ Caveats
Alternatives
------------
- `systemd user services <https://wiki.archlinux.org/index.php/Systemd/User>`__
can be configured to automatically perform OCR on a collection of files.
- `Watchman <https://facebook.github.io/watchman/>`__ is a more
powerful alternative to ``watchmedo``.
+2 -2
View File
@@ -108,9 +108,9 @@ on the public one:
FROM jbarlow83/ocrmypdf
# Add French
RUN apk add tesseract-ocr-data-fra
RUN apt install tesseract-ocr-fra
You can also copy training data to ``/usr/share/tessdata``.
You can also copy training data to ``/usr/share/tesseract-ocr/<tesseract version>/tessdata``.
Executing the test suite
========================
+1
View File
@@ -12,6 +12,7 @@ PDF is the best format for storing and exchanging scanned documents. Unfortunat
introduction
release_notes
installation
optimizer
languages
jbig2
+8 -3
View File
@@ -134,8 +134,7 @@ first install the system version to get most of the dependencies:
sudo apt-get update
sudo apt-get install \
ocrmypdf \
python3-pip
ocrmypdf
There are a few system dependency changes since ocrmypdf 6.1.2. Let's
get these, too.
@@ -146,13 +145,19 @@ get these, too.
libxml2 \
pngquant
We will need a newer version of ``pip`` then was available for Ubuntu 18.04:
.. code-block:: bash
wget https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py
Then install the most recent ocrmypdf for the local user and set the
user's ``PATH`` to check for the user's Python packages.
.. code-block:: bash
export PATH=$HOME/.local/bin:$PATH
pip3 install --user ocrmypdf
python3 -m pip install --user ocrmypdf
To add JBIG2 encoding, see :ref:`jbig2`.
+71
View File
@@ -0,0 +1,71 @@
================
PDF optimization
================
OCRmyPDF includes an image-oriented PDF optimizer. By default, the optimizer
runs with safe settings with the goal of improving compression at no loss of
quality. At higher optimization levels, lossy optimizations may be applied and
tuned. Optimization occurs after OCR, and only if OCR succeeded. It does not
perform other possible optimizations such as deduplicating resources,
consolidating fonts, simplifying vector drawings, or anything of that nature.
Optimization ranges from ``-O0`` through ``-O3``, where ``0`` disables
optimization and ``3`` implements all options. ``1``, the default, performs only
safe and lossless optimizations. (This is similar to GCC's optimization
parameter.) The exact type of optimizations performed will vary over time.
Optimizations that always occurs
================================
OCRmyPDF will automatically replace obsolete or inferior compression schemes
such as RLE or LZW with superior schemes such as Deflate and converting
monochrome images to CCITT G4. Since this is harmless it always occurs and there
is no way to disable it. Other non-image compressed objects are compressed as
well.
Fast web view
=============
OCRmyPDF automatically optimizes PDFs for "fast web view" in Adobe Acrobat's
parlance, or equivalently, linearizes PDFs so that the resources they reference
are presented in the order a viewer needs them for sequential display. This
reduces the latency of viewing a PDF both online and from local storage. This
actually slightly increases the file size.
To disable this optimization and all others, use ``ocrmypdf --optimize 0 ...``
or the shorthand ``-O0``.
Lossless optimizations
======================
At optimization level ``-O1`` (the default), OCRmyPDF will also attempt lossless
image optimization.
If a JBIG2 encoder is available, then monochrome images will be converted to
JBIG2, with the potential for huge savings on large black and white images,
since JBIG2 is far more efficient than any other monochrome (bi-level)
compression. (All known US patents related to JBIG2 have probably expired, but
it remains the responsibility of the user to supply a JBIG2 encoder such as
`jbig2enc <https://github.com/agl/jbig2enc>`__. OCRmyPDF does not implement
JBIG2 encoding on its own.)
OCRmyPDF currently does not attempt to recompress losslessly compressed objects
more aggressively.
Lossy optimizations
===================
At optimization level ``-O2`` and ``-O3``, OCRmyPDF will some attempt lossy
image optimization.
If ``pngquant`` is installed, OCRmyPDF will use it to perform quantize paletted
images to reduce their size.
The quality of JPEGs may be lowered, on the assumption that a lower quality
image may be suitable for storage after OCR.
It is not possible to optimize all image types. Uncommon image types may be
skipped by the optimizer.
OCRmyPDF provides :ref:`lossy mode JBIG2 <jbig2-lossy>` as an advanced feature
that additional requires the argument ``--jbig2-lossy``.
+15
View File
@@ -13,6 +13,21 @@ Note that it is licensed under GPLv3, so scripts that
``import ocrmypdf`` and are released publicly should probably also be
licensed under GPLv3.
v9.1.1
======
- Expand the range of pdfminer.six versions that are supported.
- Fixed Docker build when using pikepdf 1.7.0.
- Fixed documentation to recommend using pip from get-pip.py.
v9.1.0
======
- Improved diagnostics when file size increases at output. Now warns if JBIG2
or pngquant were not available.
- pikepdf 1.7.0 is now required, to pick up changes that remove the need for
a source install on Linux systems running Python 3.8.
v9.0.5
======
-2
View File
@@ -1,4 +1,2 @@
check-manifest >= 0.35
twine >= 1.8.1
coverage >= 4.5
GitPython == 2.1.3
+2 -2
View File
@@ -3,8 +3,8 @@
# installation
cffi == 1.13.2
img2pdf == 0.3.3
pdfminer.six == 20191020
pikepdf == 1.6.5
pdfminer.six == 20191110
pikepdf == 1.7.0
Pillow >= 6.2.0
reportlab == 3.5.32
tqdm == 4.37.0
+2 -2
View File
@@ -96,8 +96,8 @@ setup(
'chardet >= 3.0.4, < 4', # unlisted requirement of pdfminer.six 20181108
'cffi >= 1.9.1', # must be a setup and install requirement
'img2pdf >= 0.3.0, < 0.4', # pure Python, so track HEAD closely
'pdfminer.six >= 20181108, <= 20191020',
'pikepdf >= 1.6.5, < 2',
'pdfminer.six >= 20181108, <= 20191110',
'pikepdf >= 1.7.0, < 2',
'Pillow >= 6.2.0',
'reportlab >= 3.3.0', # oldest released version with sane image handling
'tqdm >= 4',
+14
View File
@@ -418,6 +418,20 @@ def report_output_file_size(options, input_file, output_file):
f"The argument --{arg.replace('_', '-')} was issued, causing transcoding."
)
if options.optimize == 0:
reasons.append("Optimization was disabled.")
else:
image_optimizers = {
'jbig2': jbig2enc.available(),
'pngquant': pngquant.available(),
}
for name, available in image_optimizers.items():
if not available:
reasons.append(
f"The optional dependency '{name}' was not found, so some image "
f"optimizations could not be attempted."
)
if reasons:
explanation = "Possible reasons for this include:\n" + '\n'.join(reasons) + "\n"
else:
+8 -1
View File
@@ -15,8 +15,11 @@
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
"""Interface to Ghostscript executable"""
import logging
import re
import warnings
from functools import lru_cache
from os import fspath
from shutil import copy
@@ -193,7 +196,7 @@ def generate_pdfa(
output_file,
compression,
log,
threads=1,
threads=None, # deprecated parameter
pdf_version='1.5',
pdfa_part='2',
):
@@ -216,6 +219,10 @@ def generate_pdfa(
"""
if not log:
log = gslog
if threads is not None:
warnings.warn(
"use of deprecated parameter 'threads'", category=DeprecationWarning
)
compression_args = []
if compression == 'jpeg':
+2
View File
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
"""Interface to jbig2 executable"""
from functools import lru_cache
from subprocess import PIPE, run
+2
View File
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
"""Interface to pngquant executable"""
from functools import lru_cache
from subprocess import run
from tempfile import NamedTemporaryFile
+2
View File
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
"""Interface to qpdf executable"""
from functools import lru_cache
from os import fspath
from subprocess import PIPE, STDOUT, CalledProcessError, run
+20 -11
View File
@@ -15,10 +15,13 @@
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
"""Interface to Tesseract executable"""
import os
import shutil
from collections import namedtuple
from contextlib import suppress
import logging
from os import fspath
from subprocess import PIPE, STDOUT, CalledProcessError, TimeoutExpired, run
@@ -50,6 +53,11 @@ HOCR_TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
"""
class TesseractLoggerAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
return '[tesseract] %s' % (msg), kwargs
def version(tesseract_env=None):
return get_version('tesseract', regex=r'tesseract\s(.+)', env=tesseract_env)
@@ -177,15 +185,16 @@ def get_orientation(input_file, engine_mode, timeout: float, log, tesseract_env=
return oc
def tesseract_log_output(log, stdout, input_file):
prefix = "[tesseract] "
def tesseract_log_output(mainlog, stdout, input_file):
log = TesseractLoggerAdapter(
mainlog, extra=mainlog.extra if hasattr(mainlog, 'extra') else None
)
try:
text = stdout.decode()
except UnicodeDecodeError:
log.error(
prefix
+ "command line output was not utf-8. "
"command line output was not utf-8. "
+ "This usually means Tesseract's language packs do not match "
"the installed version of Tesseract."
)
@@ -198,25 +207,25 @@ def tesseract_log_output(log, stdout, input_file):
elif line.startswith("Warning in pixReadMem"):
continue
elif 'diacritics' in line:
log.warning(prefix + "lots of diacritics - possibly poor OCR")
log.warning("lots of diacritics - possibly poor OCR")
elif line.startswith('OSD: Weak margin'):
log.warning(prefix + "unsure about page orientation")
log.warning("unsure about page orientation")
elif 'Error in pixScanForForeground' in line:
pass # Appears to be spurious/problem with nonwhite borders
elif 'Error in boxClipToRectangle' in line:
pass # Always appears with pixScanForForeground message
elif 'parameter not found: ' in line.lower():
log.error(prefix + line.strip())
log.error(line.strip())
problem = line.split('found: ')[1]
raise TesseractConfigError(problem)
elif 'error' in line.lower() or 'exception' in line.lower():
log.error(prefix + line.strip())
log.error(line.strip())
elif 'warning' in line.lower():
log.warning(prefix + line.strip())
log.warning(line.strip())
elif 'read_params_file' in line.lower():
log.error(prefix + line.strip())
log.error(line.strip())
else:
log.info(prefix + line.strip())
log.info(line.strip())
def page_timedout(log, input_file, timeout):
+2 -1
View File
@@ -18,10 +18,11 @@
# unpaper documentation:
# https://github.com/Flameeyes/unpaper/blob/master/doc/basic-concepts.md
"""Interface to unpaper executable"""
import os
import shlex
import subprocess
import sys
from functools import lru_cache
from subprocess import PIPE, STDOUT, CalledProcessError
from tempfile import TemporaryDirectory
-2
View File
@@ -32,8 +32,6 @@ Ghostscript's handling of pdfmark.
"""
import base64
import os
from binascii import hexlify
from pathlib import Path
from string import Template
+1
View File
@@ -212,6 +212,7 @@ class TextPositionTracker(PDFLayoutAnalyzer):
super().__init__(rsrcmgr, pageno, laparams)
self.textstate = None
self.result = None
self.cur_item = None # not defined in pdfminer code as it should be
def begin_page(self, page, ctm):
super().begin_page(page, ctm)
+18 -2
View File
@@ -15,7 +15,6 @@
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
import locale
import logging
import os
from unittest.mock import patch
@@ -117,11 +116,28 @@ def test_report_file_size(tmp_path, caplog):
opts = make_opts()
vd.report_output_file_size(opts, in_, out)
assert caplog.text == ''
caplog.clear()
os.truncate(in_, 25001)
os.truncate(out, 50000)
with patch('ocrmypdf._validation.jbig2enc.available', return_value=True), patch(
'ocrmypdf._validation.pngquant.available', return_value=True
):
vd.report_output_file_size(opts, in_, out)
assert 'No reason' in caplog.text
caplog.clear()
with patch('ocrmypdf._validation.jbig2enc.available', return_value=False), patch(
'ocrmypdf._validation.pngquant.available', return_value=True
):
vd.report_output_file_size(opts, in_, out)
assert 'optional dependency' in caplog.text
caplog.clear()
opts = make_opts(in_, out, optimize=0)
vd.report_output_file_size(opts, in_, out)
assert 'No reason' in caplog.text
assert 'disabled' in caplog.text
caplog.clear()
def test_false_action_store_true():