Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c082526dea | ||
|
|
33cdabaf65 | ||
|
|
94f8e36601 | ||
|
|
865002c7be | ||
|
|
5d0cc0a092 | ||
|
|
6c427f82ea | ||
|
|
e7a44ba87a | ||
|
|
c311768452 | ||
|
|
f53fedee63 | ||
|
|
87838127b0 | ||
|
|
4db4df5c72 |
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: "3.9"
|
python: "3.9"
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: "pypy-3.7"
|
python: "pypy-3.8"
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python: "3.9"
|
python: "3.9"
|
||||||
tesseract5: true
|
tesseract5: true
|
||||||
@@ -196,7 +196,7 @@ jobs:
|
|||||||
- name: Install system packages
|
- name: Install system packages
|
||||||
run: |
|
run: |
|
||||||
choco install --yes --no-progress --pre tesseract
|
choco install --yes --no-progress --pre tesseract
|
||||||
choco install --yes --no-progress --ignore-checksums ghostscript pngquant
|
choco install --yes --no-progress --ignore-checksums ghostscript
|
||||||
|
|
||||||
- name: Install Python packages
|
- name: Install Python packages
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.1.0
|
rev: v4.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
@@ -22,12 +22,12 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v2.31.1
|
rev: v2.32.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: ["--py37-plus"]
|
args: ["--py37-plus"]
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v0.942
|
rev: v0.950
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ tagged yet.
|
|||||||
|
|
||||||
.. |OCRmyPDF PyPI| image:: https://img.shields.io/pypi/v/ocrmypdf.svg
|
.. |OCRmyPDF PyPI| image:: https://img.shields.io/pypi/v/ocrmypdf.svg
|
||||||
|
|
||||||
|
v13.4.6
|
||||||
|
=======
|
||||||
|
|
||||||
|
- Convert error on corrupt ICC profiles into a warning. Thanks to @oscherler.
|
||||||
|
|
||||||
v13.4.5
|
v13.4.5
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from pikepdf import (
|
|||||||
PdfImage,
|
PdfImage,
|
||||||
PdfInlineImage,
|
PdfInlineImage,
|
||||||
PdfMatrix,
|
PdfMatrix,
|
||||||
|
UnsupportedImageTypeError,
|
||||||
parse_content_stream,
|
parse_content_stream,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -350,13 +351,20 @@ class ImageInfo:
|
|||||||
|
|
||||||
if self._color == Colorspace.icc:
|
if self._color == Colorspace.icc:
|
||||||
# Check the ICC profile to determine actual colorspace
|
# Check the ICC profile to determine actual colorspace
|
||||||
pim_icc = pim.icc
|
try:
|
||||||
if pim_icc.profile.xcolor_space == 'GRAY':
|
pim_icc = pim.icc
|
||||||
self._comp = 1
|
if pim_icc.profile.xcolor_space == 'GRAY':
|
||||||
elif pim_icc.profile.xcolor_space == 'CMYK':
|
self._comp = 1
|
||||||
self._comp = 4
|
elif pim_icc.profile.xcolor_space == 'CMYK':
|
||||||
else:
|
self._comp = 4
|
||||||
self._comp = 3
|
else:
|
||||||
|
self._comp = 3
|
||||||
|
except UnsupportedImageTypeError as ex:
|
||||||
|
self._comp = None
|
||||||
|
logger.warning(
|
||||||
|
f"An image with a corrupt or unreadable ICC profile was found. "
|
||||||
|
f"The output PDF may not match the input PDF visually: {ex}. {self}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if isinstance(self._color, Colorspace):
|
if isinstance(self._color, Colorspace):
|
||||||
self._comp = FRIENDLY_COMP.get(self._color)
|
self._comp = FRIENDLY_COMP.get(self._color)
|
||||||
@@ -409,15 +417,10 @@ class ImageInfo:
|
|||||||
return _get_dpi(self._shorthand, (self._width, self._height))
|
return _get_dpi(self._shorthand, (self._width, self._height))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
class_locals = {
|
|
||||||
attr: getattr(self, attr, None)
|
|
||||||
for attr in dir(self)
|
|
||||||
if not attr.startswith('_')
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
"<ImageInfo '{name}' {type_} {width}x{height} {color} "
|
f"<ImageInfo '{self.name}' {self.type_} {self.width}x{self.height} "
|
||||||
"{comp} {bpc} {enc} {dpi}>"
|
f"{self.color} {self.comp} {self.bpc} {self.enc} {self.dpi}>"
|
||||||
).format(**class_locals)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _find_inline_images(contentsinfo: ContentsInfo) -> Iterator[ImageInfo]:
|
def _find_inline_images(contentsinfo: ContentsInfo) -> Iterator[ImageInfo]:
|
||||||
|
|||||||
+50
-11
@@ -465,12 +465,18 @@ def test_overlay(resources, outpdf):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_destination_not_writable(resources, outdir):
|
@pytest.fixture
|
||||||
if os.name != 'nt' and (os.getuid() == 0 or os.geteuid() == 0):
|
def protected_file(outdir):
|
||||||
pytest.xfail(reason="root can write to anything")
|
|
||||||
protected_file = outdir / 'protected.pdf'
|
protected_file = outdir / 'protected.pdf'
|
||||||
protected_file.touch()
|
protected_file.touch()
|
||||||
protected_file.chmod(0o400) # Read-only
|
protected_file.chmod(0o400) # Read-only
|
||||||
|
yield protected_file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
os.name == 'nt' or os.geteuid() == 0, reason="root can write to anything"
|
||||||
|
)
|
||||||
|
def test_destination_not_writable(resources, protected_file):
|
||||||
p = run_ocrmypdf(
|
p = run_ocrmypdf(
|
||||||
resources / 'jbig2.pdf',
|
resources / 'jbig2.pdf',
|
||||||
protected_file,
|
protected_file,
|
||||||
@@ -480,7 +486,8 @@ def test_destination_not_writable(resources, outdir):
|
|||||||
assert p.returncode == ExitCode.file_access_error, "Expected error"
|
assert p.returncode == ExitCode.file_access_error, "Expected error"
|
||||||
|
|
||||||
|
|
||||||
def test_tesseract_config_valid(resources, outdir):
|
@pytest.fixture
|
||||||
|
def valid_tess_config(outdir):
|
||||||
cfg_file = outdir / 'test.cfg'
|
cfg_file = outdir / 'test.cfg'
|
||||||
with cfg_file.open('w') as f:
|
with cfg_file.open('w') as f:
|
||||||
f.write(
|
f.write(
|
||||||
@@ -490,20 +497,22 @@ language_model_penalty_non_dict_word 0
|
|||||||
language_model_penalty_non_freq_dict_word 0
|
language_model_penalty_non_freq_dict_word 0
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
yield cfg_file
|
||||||
|
|
||||||
|
|
||||||
|
def test_tesseract_config_valid(resources, valid_tess_config, outpdf):
|
||||||
check_ocrmypdf(
|
check_ocrmypdf(
|
||||||
resources / '3small.pdf',
|
resources / '3small.pdf',
|
||||||
outdir / 'out.pdf',
|
outpdf,
|
||||||
'--tesseract-config',
|
'--tesseract-config',
|
||||||
cfg_file,
|
valid_tess_config,
|
||||||
'--pages',
|
'--pages',
|
||||||
'1',
|
'1',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.slow # This test sometimes times out in CI
|
@pytest.fixture
|
||||||
@pytest.mark.parametrize('renderer', RENDERERS)
|
def invalid_tess_config(outdir):
|
||||||
def test_tesseract_config_invalid(renderer, resources, outdir):
|
|
||||||
cfg_file = outdir / 'test.cfg'
|
cfg_file = outdir / 'test.cfg'
|
||||||
with cfg_file.open('w') as f:
|
with cfg_file.open('w') as f:
|
||||||
f.write(
|
f.write(
|
||||||
@@ -511,14 +520,19 @@ def test_tesseract_config_invalid(renderer, resources, outdir):
|
|||||||
THIS FILE IS INVALID
|
THIS FILE IS INVALID
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
yield cfg_file
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.slow # This test sometimes times out in CI
|
||||||
|
@pytest.mark.parametrize('renderer', RENDERERS)
|
||||||
|
def test_tesseract_config_invalid(renderer, resources, invalid_tess_config, outpdf):
|
||||||
p = run_ocrmypdf(
|
p = run_ocrmypdf(
|
||||||
resources / 'ccitt.pdf',
|
resources / 'ccitt.pdf',
|
||||||
outdir / 'out.pdf',
|
outpdf,
|
||||||
'--pdf-renderer',
|
'--pdf-renderer',
|
||||||
renderer,
|
renderer,
|
||||||
'--tesseract-config',
|
'--tesseract-config',
|
||||||
cfg_file,
|
invalid_tess_config,
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
"parameter not found" in p.stderr.lower()
|
"parameter not found" in p.stderr.lower()
|
||||||
@@ -801,6 +815,9 @@ def test_text_curves(resources, outpdf):
|
|||||||
info = PdfInfo(outpdf)
|
info = PdfInfo(outpdf)
|
||||||
assert len(info.pages[0].images) == 0, "added images to the vector PDF"
|
assert len(info.pages[0].images) == 0, "added images to the vector PDF"
|
||||||
|
|
||||||
|
|
||||||
|
def test_text_curves_force(resources, outpdf):
|
||||||
|
with patch('ocrmypdf._pipeline.VECTOR_PAGE_DPI', 100):
|
||||||
check_ocrmypdf(
|
check_ocrmypdf(
|
||||||
resources / 'vector.pdf',
|
resources / 'vector.pdf',
|
||||||
outpdf,
|
outpdf,
|
||||||
@@ -922,3 +939,25 @@ def test_outputtype_none(resources, outtxt):
|
|||||||
'tests/plugins/tesseract_noop.py',
|
'tests/plugins/tesseract_noop.py',
|
||||||
)
|
)
|
||||||
assert p.returncode == ExitCode.ok
|
assert p.returncode == ExitCode.ok
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def graph_bad_icc(resources, outdir):
|
||||||
|
synth_input_file = outdir / 'graph-bad-icc.pdf'
|
||||||
|
with pikepdf.open(resources / 'graph.pdf') as pdf:
|
||||||
|
icc = pdf.make_stream(
|
||||||
|
b'invalid icc profile', N=3, Alternate=pikepdf.Name.DeviceRGB
|
||||||
|
)
|
||||||
|
pdf.pages[0].Resources.XObject['/Im0'].ColorSpace = pikepdf.Array(
|
||||||
|
[pikepdf.Name.ICCBased, icc]
|
||||||
|
)
|
||||||
|
pdf.save(synth_input_file)
|
||||||
|
yield synth_input_file
|
||||||
|
|
||||||
|
|
||||||
|
def test_corrupt_icc(graph_bad_icc, outpdf, caplog):
|
||||||
|
result = run_ocrmypdf_api(graph_bad_icc, outpdf)
|
||||||
|
assert result == ExitCode.ok
|
||||||
|
assert any(
|
||||||
|
'corrupt or unreadable ICC profile' in rec.message for rec in caplog.records
|
||||||
|
)
|
||||||
|
|||||||
+16
-8
@@ -4,23 +4,31 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
import pikepdf
|
import pikepdf
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from ocrmypdf.exceptions import MissingDependencyError
|
||||||
|
|
||||||
from .conftest import check_ocrmypdf
|
from .conftest import check_ocrmypdf
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('optimize', (0, 3))
|
@pytest.mark.parametrize('optimize', (0, 3))
|
||||||
@pytest.mark.parametrize('pdfa_level', (1, 2, 3))
|
@pytest.mark.parametrize('pdfa_level', (1, 2, 3))
|
||||||
def test_pdfa(resources, outpdf, optimize, pdfa_level):
|
def test_pdfa(resources, outpdf, optimize, pdfa_level):
|
||||||
check_ocrmypdf(
|
try:
|
||||||
resources / 'francais.pdf',
|
check_ocrmypdf(
|
||||||
outpdf,
|
resources / 'francais.pdf',
|
||||||
'--plugin',
|
outpdf,
|
||||||
'tests/plugins/tesseract_noop.py',
|
'--plugin',
|
||||||
f'--output-type=pdfa-{pdfa_level}',
|
'tests/plugins/tesseract_noop.py',
|
||||||
f'--optimize={optimize}',
|
f'--output-type=pdfa-{pdfa_level}',
|
||||||
)
|
f'--optimize={optimize}',
|
||||||
|
)
|
||||||
|
except MissingDependencyError as e:
|
||||||
|
if 'pngquant' in str(e) and optimize in (2, 3) and os.name == 'nt':
|
||||||
|
pytest.xfail("pngquant currently not available on Windows")
|
||||||
if pdfa_level in (2, 3):
|
if pdfa_level in (2, 3):
|
||||||
# PDF/A-2 allows ObjStm
|
# PDF/A-2 allows ObjStm
|
||||||
assert b'/ObjStm' in outpdf.read_bytes()
|
assert b'/ObjStm' in outpdf.read_bytes()
|
||||||
|
|||||||
Reference in New Issue
Block a user