Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8be9a68c5e | ||
|
|
6c34d59836 | ||
|
|
386453d178 | ||
|
|
615a7561b5 | ||
|
|
c4c64c3ea0 | ||
|
|
21279f5784 | ||
|
|
a63a21a7fc | ||
|
|
1c4d5d79f7 |
@@ -19,16 +19,16 @@ repos:
|
|||||||
language_version: python
|
language_version: python
|
||||||
exclude: ^src/ocrmypdf/lib/_leptonica.py
|
exclude: ^src/ocrmypdf/lib/_leptonica.py
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v1.17.0
|
rev: v1.19.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v2.26.0
|
rev: v2.29.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: ["--py36-plus"]
|
args: ["--py36-plus"]
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v0.910
|
rev: v0.910-1
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
|
|||||||
@@ -18,6 +18,16 @@ wish to use some of its features for working with PDFs.
|
|||||||
for Python 3.6 around that time. The change will be marked with a major
|
for Python 3.6 around that time. The change will be marked with a major
|
||||||
release.
|
release.
|
||||||
|
|
||||||
|
v12.7.2
|
||||||
|
=======
|
||||||
|
|
||||||
|
- Fixed "invalid version number" error for Tesseract packaging with nonstandard
|
||||||
|
version "5.0.0-rc1.20211030".
|
||||||
|
- Fixed use of deprecated ``importlib.resources.read_binary``.
|
||||||
|
- Replace some uses of string paths with ``pathlib.Path``.
|
||||||
|
- Fixed a leaked file handle when using ``--output-type none``.
|
||||||
|
- Removed shims to support versions of pikepdf that are no longer supported.
|
||||||
|
|
||||||
v12.7.1
|
v12.7.1
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ classifiers =
|
|||||||
Programming Language :: Python :: 3.7
|
Programming Language :: Python :: 3.7
|
||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
Topic :: Scientific/Engineering :: Image Recognition
|
Topic :: Scientific/Engineering :: Image Recognition
|
||||||
Topic :: Text Processing :: Indexing
|
Topic :: Text Processing :: Indexing
|
||||||
Topic :: Text Processing :: Linguistic
|
Topic :: Text Processing :: Linguistic
|
||||||
@@ -49,14 +50,14 @@ install_requires =
|
|||||||
cffi>=1.9.1 # must be a setup and install requirement
|
cffi>=1.9.1 # must be a setup and install requirement
|
||||||
coloredlogs>=14.0 # strictly optional
|
coloredlogs>=14.0 # strictly optional
|
||||||
img2pdf>=0.3.0,<0.5 # pure Python
|
img2pdf>=0.3.0,<0.5 # pure Python
|
||||||
importlib-metadata>=4;python_version<'3.8' # until Python 3.8
|
|
||||||
importlib-resources>=5;python_version<'3.9' # until Python 3.9
|
|
||||||
pdfminer.six!=20200720,>=20191110,<=20211012
|
pdfminer.six!=20200720,>=20191110,<=20211012
|
||||||
pikepdf>=2.10.0
|
pikepdf>=2.10.0
|
||||||
pikepdf<3;implementation_name=="pypy" and python_version=='3.6'
|
|
||||||
pluggy>=0.13.0,<2
|
pluggy>=0.13.0,<2
|
||||||
reportlab>=3.5.66
|
reportlab>=3.5.66
|
||||||
tqdm>=4
|
tqdm>=4
|
||||||
|
importlib-metadata>=4;python_version<'3.8' # until Python 3.8
|
||||||
|
importlib-resources>=5;python_version<'3.9' # until Python 3.9
|
||||||
|
pikepdf<3;implementation_name=="pypy" and python_version=='3.6'
|
||||||
python_requires = >=3.6
|
python_requires = >=3.6
|
||||||
include_package_data = True
|
include_package_data = True
|
||||||
package_dir =
|
package_dir =
|
||||||
@@ -64,8 +65,8 @@ package_dir =
|
|||||||
platforms = any
|
platforms = any
|
||||||
setup_requires =
|
setup_requires =
|
||||||
cffi>=1.9.1 # to build the leptonica module
|
cffi>=1.9.1 # to build the leptonica module
|
||||||
setuptools_scm
|
setuptools-scm
|
||||||
setuptools_scm_git_archive
|
setuptools-scm-git-archive
|
||||||
zip_safe = False
|
zip_safe = False
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
"""Interface to Tesseract executable"""
|
"""Interface to Tesseract executable"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import shutil
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from os import fspath
|
from os import fspath
|
||||||
@@ -61,8 +59,8 @@ class TesseractVersion(StrictVersion):
|
|||||||
r'''
|
r'''
|
||||||
^(\d+) \. (\d+) (\. (\d+))? # groups: 1/major, 2/minor, 3/[skip], 4/patch
|
^(\d+) \. (\d+) (\. (\d+))? # groups: 1/major, 2/minor, 3/[skip], 4/patch
|
||||||
[-]? # optional hyphen separator
|
[-]? # optional hyphen separator
|
||||||
(?:(alpha|beta|rc|dev)?[.\-\ ]?(\d+)?)? # 5/prerelease, 6/prerelease_num
|
(?: ((?:alpha|beta|rc|dev)\d*)? [.\-\ ]? (\d+)? )? # 5/prerelease, 6/prerelease_num
|
||||||
(?:-(\d+)-g[0-9a-f]+)? # untagged git version
|
(?:(?:-\d+)?-g[0-9a-f]+)? # untagged git version
|
||||||
$
|
$
|
||||||
''',
|
''',
|
||||||
re.VERBOSE | re.ASCII,
|
re.VERBOSE | re.ASCII,
|
||||||
@@ -73,7 +71,7 @@ class TesseractVersion(StrictVersion):
|
|||||||
super().parse(vstring)
|
super().parse(vstring)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
if 'int() argument must be a string' in str(e):
|
if 'int() argument must be a string' in str(e):
|
||||||
super().parse(vstring + '0')
|
super().parse(vstring + '-0')
|
||||||
|
|
||||||
|
|
||||||
def version():
|
def version():
|
||||||
@@ -250,7 +248,7 @@ def generate_hocr(
|
|||||||
|
|
||||||
# Reminder: test suite tesseract test plugins will break after any changes
|
# Reminder: test suite tesseract test plugins will break after any changes
|
||||||
# to the number of order parameters here
|
# to the number of order parameters here
|
||||||
args_tesseract.extend([os.fspath(input_file), os.fspath(prefix), 'hocr', 'txt'])
|
args_tesseract.extend([fspath(input_file), fspath(prefix), 'hocr', 'txt'])
|
||||||
args_tesseract.extend(tessconfig)
|
args_tesseract.extend(tessconfig)
|
||||||
try:
|
try:
|
||||||
p = run(args_tesseract, stdout=PIPE, stderr=STDOUT, timeout=timeout, check=True)
|
p = run(args_tesseract, stdout=PIPE, stderr=STDOUT, timeout=timeout, check=True)
|
||||||
@@ -273,7 +271,7 @@ def generate_hocr(
|
|||||||
# The sidecar text file will get the suffix .txt; rename it to
|
# The sidecar text file will get the suffix .txt; rename it to
|
||||||
# whatever caller wants it named
|
# whatever caller wants it named
|
||||||
if prefix.with_suffix('.txt').exists():
|
if prefix.with_suffix('.txt').exists():
|
||||||
shutil.move(prefix.with_suffix('.txt'), output_text)
|
prefix.with_suffix('.txt').replace(output_text)
|
||||||
|
|
||||||
|
|
||||||
def use_skip_page(output_pdf, output_text):
|
def use_skip_page(output_pdf, output_text):
|
||||||
@@ -320,18 +318,18 @@ def generate_pdf(
|
|||||||
if user_patterns:
|
if user_patterns:
|
||||||
args_tesseract.extend(['--user-patterns', user_patterns])
|
args_tesseract.extend(['--user-patterns', user_patterns])
|
||||||
|
|
||||||
prefix = os.path.splitext(output_pdf)[0] # Tesseract appends suffixes
|
prefix = output_pdf.parent / Path(output_pdf.stem)
|
||||||
|
|
||||||
# Reminder: test suite tesseract test plugins might break after any changes
|
# Reminder: test suite tesseract test plugins might break after any changes
|
||||||
# to the number of order parameters here
|
# to the number of order parameters here
|
||||||
|
|
||||||
args_tesseract.extend([os.fspath(input_file), os.fspath(prefix), 'pdf', 'txt'])
|
args_tesseract.extend([fspath(input_file), fspath(prefix), 'pdf', 'txt'])
|
||||||
args_tesseract.extend(tessconfig)
|
args_tesseract.extend(tessconfig)
|
||||||
try:
|
try:
|
||||||
p = run(args_tesseract, stdout=PIPE, stderr=STDOUT, timeout=timeout, check=True)
|
p = run(args_tesseract, stdout=PIPE, stderr=STDOUT, timeout=timeout, check=True)
|
||||||
stdout = p.stdout
|
stdout = p.stdout
|
||||||
if os.path.exists(prefix + '.txt'):
|
if prefix.with_suffix('.txt').exists():
|
||||||
shutil.move(prefix + '.txt', output_text)
|
prefix.with_suffix('.txt').replace(output_text)
|
||||||
except TimeoutExpired:
|
except TimeoutExpired:
|
||||||
page_timedout(timeout)
|
page_timedout(timeout)
|
||||||
use_skip_page(output_pdf, output_text)
|
use_skip_page(output_pdf, output_text)
|
||||||
|
|||||||
@@ -290,10 +290,10 @@ def exec_concurrent(context: PdfContext, executor: Executor):
|
|||||||
# Copy text file to destination
|
# Copy text file to destination
|
||||||
copy_final(text, options.sidecar, context)
|
copy_final(text, options.sidecar, context)
|
||||||
|
|
||||||
if options.output_type != 'none':
|
# Merge layers to one single pdf
|
||||||
# Merge layers to one single pdf
|
pdf = ocrgraft.finalize()
|
||||||
pdf = ocrgraft.finalize()
|
|
||||||
|
|
||||||
|
if options.output_type != 'none':
|
||||||
# PDF/A and metadata
|
# PDF/A and metadata
|
||||||
log.info("Postprocessing...")
|
log.info("Postprocessing...")
|
||||||
pdf = post_process(pdf, context, executor)
|
pdf = post_process(pdf, context, executor)
|
||||||
|
|||||||
@@ -160,10 +160,12 @@ def _pages_from_ranges(ranges: str) -> Set[int]:
|
|||||||
try:
|
try:
|
||||||
new_pages = list(range(int(start) - 1, int(end)))
|
new_pages = list(range(int(start) - 1, int(end)))
|
||||||
if not new_pages:
|
if not new_pages:
|
||||||
raise BadArgsError(f"invalid page subrange '{start}-{end}'")
|
raise BadArgsError(
|
||||||
|
f"invalid page subrange '{start}-{end}'"
|
||||||
|
) from None
|
||||||
pages.extend(new_pages)
|
pages.extend(new_pages)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise BadArgsError("invalid page range") from None
|
raise BadArgsError(f"invalid page subrange '{g}'") from None
|
||||||
|
|
||||||
if not pages:
|
if not pages:
|
||||||
raise BadArgsError(
|
raise BadArgsError(
|
||||||
|
|||||||
@@ -221,15 +221,7 @@ def check_pdf(input_file: Path) -> bool:
|
|||||||
# If linearization is missing entirely, we do not complain. We do
|
# If linearization is missing entirely, we do not complain. We do
|
||||||
# complain if linearization is present but incorrect.
|
# complain if linearization is present but incorrect.
|
||||||
pdf.check_linearization(sio)
|
pdf.check_linearization(sio)
|
||||||
except RuntimeError:
|
except (RuntimeError, pikepdf.ForeignObjectError):
|
||||||
pass
|
|
||||||
except (
|
|
||||||
# Workaround for a problematic pikepdf version
|
|
||||||
# pragma: no cover
|
|
||||||
pikepdf.ForeignObjectError
|
|
||||||
if pikepdf.__version__ == '2.1.0'
|
|
||||||
else NeverRaise
|
|
||||||
):
|
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
linearize_msgs = sio.getvalue()
|
linearize_msgs = sio.getvalue()
|
||||||
|
|||||||
@@ -585,7 +585,10 @@ def optimize(
|
|||||||
log.info(f"Optimize ratio: {ratio:.2f} savings: {(savings):.1%}")
|
log.info(f"Optimize ratio: {ratio:.2f} savings: {(savings):.1%}")
|
||||||
|
|
||||||
if savings < 0:
|
if savings < 0:
|
||||||
log.info("Image optimization did not improve the file - discarded")
|
log.info(
|
||||||
|
"Image optimization did not improve the file - "
|
||||||
|
"optimizations will not be used"
|
||||||
|
)
|
||||||
# We still need to save the file
|
# We still need to save the file
|
||||||
with Pdf.open(input_file) as pike:
|
with Pdf.open(input_file) as pike:
|
||||||
pike.remove_unreferenced_resources()
|
pike.remove_unreferenced_resources()
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ from pathlib import Path
|
|||||||
from typing import Dict, Iterator, Union
|
from typing import Dict, Iterator, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from importlib_resources import read_binary
|
from importlib_resources import files as package_files
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from importlib.resources import read_binary
|
from importlib.resources import files as package_files
|
||||||
|
|
||||||
import pikepdf
|
import pikepdf
|
||||||
import pkg_resources # deprecated
|
import pkg_resources # deprecated
|
||||||
|
|
||||||
@@ -107,9 +108,7 @@ def generate_pdfa_ps(target_filename: Path, icc: str = 'sRGB'):
|
|||||||
if icc != 'sRGB':
|
if icc != 'sRGB':
|
||||||
raise NotImplementedError("Only supporting sRGB")
|
raise NotImplementedError("Only supporting sRGB")
|
||||||
|
|
||||||
bytes_icc_profile = read_binary(
|
bytes_icc_profile = (package_files('ocrmypdf.data') / SRGB_ICC_PROFILE).read_bytes()
|
||||||
'ocrmypdf.data', SRGB_ICC_PROFILE_NAME
|
|
||||||
)
|
|
||||||
ps = '\n'.join(_make_postscript(icc, bytes_icc_profile, 3))
|
ps = '\n'.join(_make_postscript(icc, bytes_icc_profile, 3))
|
||||||
|
|
||||||
# We should have encoded everything to pure ASCII by this point, and
|
# We should have encoded everything to pure ASCII by this point, and
|
||||||
|
|||||||
@@ -238,6 +238,13 @@ def test_version_comparison():
|
|||||||
need_version='4.0.0',
|
need_version='4.0.0',
|
||||||
version_parser=TesseractVersion,
|
version_parser=TesseractVersion,
|
||||||
)
|
)
|
||||||
|
vd.check_external_program(
|
||||||
|
program="tesseract",
|
||||||
|
package="tesseract",
|
||||||
|
version_checker=lambda: '5.0.0-rc1.20211030',
|
||||||
|
need_version='4.0.0',
|
||||||
|
version_parser=TesseractVersion,
|
||||||
|
)
|
||||||
vd.check_external_program(
|
vd.check_external_program(
|
||||||
program="tesseract",
|
program="tesseract",
|
||||||
package="tesseract",
|
package="tesseract",
|
||||||
|
|||||||
Reference in New Issue
Block a user