Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14a85f9473 | ||
|
|
d22a1b3367 | ||
|
|
b913e5dfef | ||
|
|
dd8a5a4c72 | ||
|
|
36e9a54f02 | ||
|
|
3707af3b74 | ||
|
|
ced7ad9164 | ||
|
|
54bbbfdeb3 | ||
|
|
7f73a6ed1e | ||
|
|
dce206d3dc | ||
|
|
9304c856cf | ||
|
|
e5df98cbdf | ||
|
|
19bf3aeb00 | ||
|
|
e86be0031c | ||
|
|
6425977998 | ||
|
|
d57df2d980 | ||
|
|
664d0c7969 | ||
|
|
a354663ee1 | ||
|
|
b21b048ec4 |
@@ -19,5 +19,5 @@ repos:
|
|||||||
rev: 19.10b0
|
rev: 19.10b0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
language_version: python3.8
|
language_version: python
|
||||||
exclude: ^src/ocrmypdf/lib/_leptonica.py
|
exclude: ^src/ocrmypdf/lib/_leptonica.py
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ apt-get install tesseract-ocr-chi-sim # Example: Install Chinese Simplified lan
|
|||||||
|
|
||||||
# Arch Linux users
|
# Arch Linux users
|
||||||
pacman -S tesseract-data-eng tesseract-data-deu # Example: Install the English and German language packs
|
pacman -S tesseract-data-eng tesseract-data-deu # Example: Install the English and German language packs
|
||||||
|
|
||||||
|
# brew macOS users
|
||||||
|
brew install tesseract-lang
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then pass the `-l LANG` argument to OCRmyPDF to give a hint as to what languages it should search for. Multiple languages can be requested.
|
You can then pass the `-l LANG` argument to OCRmyPDF to give a hint as to what languages it should search for. Multiple languages can be requested.
|
||||||
|
|||||||
+1
-2
@@ -156,9 +156,8 @@ stages:
|
|||||||
# versionSpec: "$(python.version)"
|
# versionSpec: "$(python.version)"
|
||||||
- bash: |
|
- bash: |
|
||||||
brew update
|
brew update
|
||||||
brew unlink python@2
|
|
||||||
brew upgrade python
|
brew upgrade python
|
||||||
echo "Using Python `python3 --version`"
|
echo "Using `python3 --version`"
|
||||||
displayName: "Update brew and Python"
|
displayName: "Update brew and Python"
|
||||||
- bash: |
|
- bash: |
|
||||||
brew install \
|
brew install \
|
||||||
|
|||||||
@@ -12,6 +12,22 @@ may be unreliable. Use the API to depend on precise behavior.
|
|||||||
The public API may be useful in scripts that launch OCRmyPDF processes or that
|
The public API may be useful in scripts that launch OCRmyPDF processes or that
|
||||||
wish to use some of its features for working with PDFs.
|
wish to use some of its features for working with PDFs.
|
||||||
|
|
||||||
|
v11.3.2
|
||||||
|
=======
|
||||||
|
|
||||||
|
- Explicitly require pikepdf 2.0.0 or newer when running on Python 3.9. (There are
|
||||||
|
concerns about the stability of pybind11 2.5.x with Python 3.9, which is used in
|
||||||
|
pikepdf 1.x.)
|
||||||
|
- Fixed another issue related to page rotation.
|
||||||
|
- Fixed an issue where image marked as image masks were not properly considered
|
||||||
|
as optimization candidates.
|
||||||
|
- On some systems, unpaper seems to be unable to process the PNGs we offer it
|
||||||
|
as input. We now convert the input to PNM format, which unpaper always accepts.
|
||||||
|
Fixes #665 and #667.
|
||||||
|
- DPI sent to unpaper is now rounded to a more reasonable number of decimal digits.
|
||||||
|
- Debug and error messages from unpaper were being suppressed.
|
||||||
|
- Some documentation tweaks.
|
||||||
|
|
||||||
v11.3.1
|
v11.3.1
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# requirements.txt can be used to replicate the developer's build environment
|
# requirements.txt can be used to replicate the developer's build environment
|
||||||
# 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.3
|
||||||
coloredlogs == 14.0 # technically optional
|
coloredlogs == 14.0 # technically optional
|
||||||
img2pdf == 0.3.6
|
img2pdf == 0.4.0
|
||||||
pdfminer.six == 20200517
|
pdfminer.six == 20201018
|
||||||
pikepdf == 1.16.1
|
pikepdf == 2.0.0
|
||||||
pluggy == 0.13.1
|
pluggy == 0.13.1
|
||||||
Pillow == 7.1.2
|
Pillow == 8.0.1
|
||||||
reportlab == 3.5.42
|
reportlab == 3.5.55
|
||||||
tqdm == 4.46.1
|
tqdm == 4.51.0
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ setup(
|
|||||||
'coloredlogs >= 14.0', # strictly optional
|
'coloredlogs >= 14.0', # strictly optional
|
||||||
'img2pdf >= 0.3.0, < 0.5', # pure Python, so track HEAD closely
|
'img2pdf >= 0.3.0, < 0.5', # pure Python, so track HEAD closely
|
||||||
'pdfminer.six >= 20191110, != 20200720, <= 20201018',
|
'pdfminer.six >= 20191110, != 20200720, <= 20201018',
|
||||||
'pikepdf >= 1.14.0, < 3',
|
"pikepdf >= 1.14.0, < 3 ; python_version < '3.9'",
|
||||||
|
"pikepdf >= 2.0.0 ; python_version >= '3.9'",
|
||||||
'Pillow >= 7.0.0',
|
'Pillow >= 7.0.0',
|
||||||
'pluggy >= 0.13.0, < 1.0',
|
'pluggy >= 0.13.0, < 1.0',
|
||||||
'reportlab >= 3.3.0', # oldest released version with sane image handling
|
'reportlab >= 3.3.0', # oldest released version with sane image handling
|
||||||
|
|||||||
@@ -122,8 +122,6 @@ def rasterize_pdf(
|
|||||||
stderr = p.stderr.decode(errors='replace')
|
stderr = p.stderr.decode(errors='replace')
|
||||||
if _gs_error_reported(stderr):
|
if _gs_error_reported(stderr):
|
||||||
log.error(stderr)
|
log.error(stderr)
|
||||||
elif stderr:
|
|
||||||
log.debug(stderr)
|
|
||||||
|
|
||||||
with Image.open(BytesIO(p.stdout)) as im:
|
with Image.open(BytesIO(p.stdout)) as im:
|
||||||
if rotation is not None:
|
if rotation is not None:
|
||||||
|
|||||||
@@ -55,21 +55,21 @@ def _setup_unpaper_io(tmpdir: Path, input_file: Path) -> Tuple[Path, Path]:
|
|||||||
"Failed to convert image to a supported format."
|
"Failed to convert image to a supported format."
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
if im_modified or input_file.suffix != '.png':
|
if im_modified or input_file.suffix != '.pnm':
|
||||||
input_png = tmpdir / 'input.png'
|
input_pnm = tmpdir / 'input.pnm'
|
||||||
im.save(input_png, format='PNG', compress_level=1)
|
im.save(input_pnm, format='PPM')
|
||||||
else:
|
else:
|
||||||
# No changes, PNG input, just use the file we already have
|
# No changes, PNG input, just use the file we already have
|
||||||
input_png = input_file
|
input_pnm = input_file
|
||||||
output_pnm = tmpdir / f'output{suffix}'
|
output_pnm = tmpdir / f'output{suffix}'
|
||||||
return input_png, output_pnm
|
return input_pnm, output_pnm
|
||||||
|
|
||||||
|
|
||||||
def run(input_file, output_file, dpi, mode_args):
|
def run(input_file, output_file, dpi, mode_args):
|
||||||
args_unpaper = ['unpaper', '-v', '--dpi', str(dpi)] + mode_args
|
args_unpaper = ['unpaper', '-v', '--dpi', str(round(dpi, 6))] + mode_args
|
||||||
|
|
||||||
with TemporaryDirectory() as tmpdir:
|
with TemporaryDirectory() as tmpdir:
|
||||||
input_png, output_pnm = _setup_unpaper_io(Path(tmpdir), input_file)
|
input_pnm, output_pnm = _setup_unpaper_io(Path(tmpdir), input_file)
|
||||||
|
|
||||||
# To prevent any shenanigans from accepting arbitrary parameters in
|
# To prevent any shenanigans from accepting arbitrary parameters in
|
||||||
# --unpaper-args, we:
|
# --unpaper-args, we:
|
||||||
@@ -78,31 +78,26 @@ def run(input_file, output_file, dpi, mode_args):
|
|||||||
# 3) append absolute paths for the input and output file
|
# 3) append absolute paths for the input and output file
|
||||||
# This should ensure that a user cannot clobber some other file with
|
# This should ensure that a user cannot clobber some other file with
|
||||||
# their unpaper arguments (whether intentionally or otherwise)
|
# their unpaper arguments (whether intentionally or otherwise)
|
||||||
args_unpaper.extend([os.fspath(input_png), os.fspath(output_pnm)])
|
args_unpaper.extend([os.fspath(input_pnm), os.fspath(output_pnm)])
|
||||||
|
external_run(
|
||||||
|
args_unpaper,
|
||||||
|
close_fds=True,
|
||||||
|
check=True,
|
||||||
|
universal_newlines=True,
|
||||||
|
stderr=STDOUT, # unpaper writes logging output to stdout and stderr
|
||||||
|
stdout=PIPE, # and cannot send file output to stdout
|
||||||
|
cwd=tmpdir,
|
||||||
|
logs_errors_to_stdout=True,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
proc = external_run(
|
with Image.open(output_pnm) as imout:
|
||||||
args_unpaper,
|
imout.save(output_file, dpi=(dpi, dpi))
|
||||||
check=True,
|
except (FileNotFoundError, OSError):
|
||||||
close_fds=True,
|
raise SubprocessOutputError(
|
||||||
universal_newlines=True,
|
"unpaper: failed to produce the expected output file. "
|
||||||
stderr=STDOUT, # unpaper writes logging output to stdout and stderr
|
+ " Called with: "
|
||||||
cwd=tmpdir, # and cannot send file output to stdout
|
+ str(args_unpaper)
|
||||||
stdout=PIPE,
|
) from None
|
||||||
)
|
|
||||||
except CalledProcessError as e:
|
|
||||||
log.debug(e.stderr)
|
|
||||||
raise e from e
|
|
||||||
else:
|
|
||||||
log.debug(proc.stderr)
|
|
||||||
try:
|
|
||||||
with Image.open(output_pnm) as imout:
|
|
||||||
imout.save(output_file, dpi=(dpi, dpi))
|
|
||||||
except (FileNotFoundError, OSError):
|
|
||||||
raise SubprocessOutputError(
|
|
||||||
"unpaper: failed to produce the expected output file. "
|
|
||||||
+ " Called with: "
|
|
||||||
+ str(args_unpaper)
|
|
||||||
) from None
|
|
||||||
|
|
||||||
|
|
||||||
def validate_custom_args(args: str):
|
def validate_custom_args(args: str):
|
||||||
|
|||||||
@@ -536,9 +536,6 @@ def create_ocr_image(image: Path, page_context: PageContext):
|
|||||||
|
|
||||||
# Pillow requires integer DPI
|
# Pillow requires integer DPI
|
||||||
dpi = tuple(round(coord) for coord in im.info['dpi'])
|
dpi = tuple(round(coord) for coord in im.info['dpi'])
|
||||||
if page_context.pageinfo.rotation != 0:
|
|
||||||
log.info(f"Rotating {page_context.pageinfo.rotation}")
|
|
||||||
im = im.rotate(page_context.pageinfo.rotation)
|
|
||||||
im.save(output_file, dpi=dpi)
|
im.save(output_file, dpi=dpi)
|
||||||
return output_file
|
return output_file
|
||||||
|
|
||||||
|
|||||||
@@ -307,7 +307,14 @@ class NeverRaise(Exception):
|
|||||||
pass # pylint: disable=unnecessary-pass
|
pass # pylint: disable=unnecessary-pass
|
||||||
|
|
||||||
|
|
||||||
def configure_debug_logging(log_filename, prefix=''):
|
def configure_debug_logging(log_filename, prefix: str = ''):
|
||||||
|
"""
|
||||||
|
Create a debug log file at a specified location.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
log_filename: Where to the put the log file.
|
||||||
|
prefix: The logging domain prefix that should be sent to the log.
|
||||||
|
"""
|
||||||
log_file_handler = logging.FileHandler(log_filename, delay=True)
|
log_file_handler = logging.FileHandler(log_filename, delay=True)
|
||||||
log_file_handler.setLevel(logging.DEBUG)
|
log_file_handler.setLevel(logging.DEBUG)
|
||||||
formatter = logging.Formatter(
|
formatter = logging.Formatter(
|
||||||
|
|||||||
+6
-5
@@ -10,7 +10,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import BinaryIO, Iterable, Union
|
from typing import AnyStr, BinaryIO, Iterable, Optional, Union
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from ocrmypdf._logging import PageNumberFilter, TqdmConsole
|
from ocrmypdf._logging import PageNumberFilter, TqdmConsole
|
||||||
@@ -26,7 +26,8 @@ except ModuleNotFoundError:
|
|||||||
coloredlogs = None
|
coloredlogs = None
|
||||||
|
|
||||||
|
|
||||||
PathOrIO = Union[BinaryIO, os.PathLike, str, bytes]
|
StrPath = Union[os.PathLike, AnyStr]
|
||||||
|
PathOrIO = Union[BinaryIO, StrPath]
|
||||||
|
|
||||||
|
|
||||||
class Verbosity(IntEnum):
|
class Verbosity(IntEnum):
|
||||||
@@ -202,7 +203,7 @@ def ocr( # pylint: disable=unused-argument
|
|||||||
language: Iterable[str] = None,
|
language: Iterable[str] = None,
|
||||||
image_dpi: int = None,
|
image_dpi: int = None,
|
||||||
output_type=None,
|
output_type=None,
|
||||||
sidecar: os.PathLike = None,
|
sidecar: Optional[StrPath] = None,
|
||||||
jobs: int = None,
|
jobs: int = None,
|
||||||
use_threads: bool = None,
|
use_threads: bool = None,
|
||||||
title: str = None,
|
title: str = None,
|
||||||
@@ -239,7 +240,7 @@ def ocr( # pylint: disable=unused-argument
|
|||||||
user_words: os.PathLike = None,
|
user_words: os.PathLike = None,
|
||||||
user_patterns: os.PathLike = None,
|
user_patterns: os.PathLike = None,
|
||||||
fast_web_view: float = None,
|
fast_web_view: float = None,
|
||||||
plugins: Iterable[Union[str, Path]] = None,
|
plugins: Iterable[StrPath] = None,
|
||||||
keep_temporary_files: bool = None,
|
keep_temporary_files: bool = None,
|
||||||
progress_bar: bool = None,
|
progress_bar: bool = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -261,7 +262,7 @@ def ocr( # pylint: disable=unused-argument
|
|||||||
read.
|
read.
|
||||||
output_file: If a :class:`pathlib.Path`, ``str`` or ``bytes``, this is
|
output_file: If a :class:`pathlib.Path`, ``str`` or ``bytes``, this is
|
||||||
interpreted as file system path to the output file. If the object
|
interpreted as file system path to the output file. If the object
|
||||||
appears to be a writable stream (with methods such as ``.read()`` and
|
appears to be a writable stream (with methods such as ``.write()`` and
|
||||||
``.seek()``), the output will be written to this stream. If
|
``.seek()``), the output will be written to this stream. If
|
||||||
``output_file`` is ``"-"``, the output will be written to ``sys.stdout``
|
``output_file`` is ``"-"``, the output will be written to ``sys.stdout``
|
||||||
(provided that standard output does not seem to be a terminal device).
|
(provided that standard output does not seem to be a terminal device).
|
||||||
|
|||||||
+23
-19
@@ -115,25 +115,29 @@ def extract_image_jbig2(
|
|||||||
and filtdp[0] != Name.JBIG2Decode
|
and filtdp[0] != Name.JBIG2Decode
|
||||||
and jbig2enc.available()
|
and jbig2enc.available()
|
||||||
):
|
):
|
||||||
try:
|
# Save any colorspace associated with the image, so that we
|
||||||
# Save any colorspace associated with the image, so that we
|
# will export a pure 1-bit PNG with no palette or ICC profile.
|
||||||
# will export a pure 1-bit PNG with no palette or ICC profile.
|
# Showing the palette or ICC to jbig2enc will cause it to perform
|
||||||
# Showing the palette or ICC to jbig2enc will cause it to perform
|
# colorspace transform to 1bpp, which will conflict the palette or
|
||||||
# colorspace transform to 1bpp, which will conflict the palette or
|
# ICC if it exists.
|
||||||
# ICC if it exists.
|
colorspace = pim.obj.get(pikepdf.Name.ColorSpace, None)
|
||||||
colorspace = pim.obj.ColorSpace
|
if colorspace is not None or pim.image_mask:
|
||||||
# Set to DeviceGray temporarily; we already in 1 bpc.
|
try:
|
||||||
pim.obj.ColorSpace = pikepdf.Name.DeviceGray
|
# Set to DeviceGray temporarily; we already in 1 bpc.
|
||||||
imgname = root / f'{xref:08d}'
|
pim.obj.ColorSpace = pikepdf.Name.DeviceGray
|
||||||
with imgname.open('wb') as f:
|
imgname = root / f'{xref:08d}'
|
||||||
ext = pim.extract_to(stream=f)
|
with imgname.open('wb') as f:
|
||||||
imgname.rename(imgname.with_suffix(ext))
|
ext = pim.extract_to(stream=f)
|
||||||
except pikepdf.UnsupportedImageTypeError:
|
imgname.rename(imgname.with_suffix(ext))
|
||||||
return None
|
except pikepdf.UnsupportedImageTypeError:
|
||||||
finally:
|
return None
|
||||||
# Restore image colorspace after temporarily setting it to DeviceGray
|
finally:
|
||||||
pim.obj.ColorSpace = colorspace
|
# Restore image colorspace after temporarily setting it to DeviceGray
|
||||||
return XrefExt(xref, ext)
|
if colorspace is not None:
|
||||||
|
pim.obj.ColorSpace = colorspace
|
||||||
|
else:
|
||||||
|
del pim.obj.ColorSpace
|
||||||
|
return XrefExt(xref, ext)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -818,12 +818,12 @@ class PdfInfo:
|
|||||||
check_pages=check_pages,
|
check_pages=check_pages,
|
||||||
detailed_analysis=detailed_analysis,
|
detailed_analysis=detailed_analysis,
|
||||||
)
|
)
|
||||||
self._needs_rendering = pdf.root.get('/NeedsRendering', False)
|
self._needs_rendering = pdf.Root.get('/NeedsRendering', False)
|
||||||
self._has_acroform = False
|
self._has_acroform = False
|
||||||
if '/AcroForm' in pdf.root:
|
if '/AcroForm' in pdf.Root:
|
||||||
if len(pdf.root.AcroForm.get('/Fields', [])) > 0:
|
if len(pdf.Root.AcroForm.get('/Fields', [])) > 0:
|
||||||
self._has_acroform = True
|
self._has_acroform = True
|
||||||
elif '/XFA' in pdf.root.AcroForm:
|
elif '/XFA' in pdf.Root.AcroForm:
|
||||||
self._has_acroform = True
|
self._has_acroform = True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -25,13 +25,21 @@ from ocrmypdf.exceptions import MissingDependencyError
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def run(args, *, env=None, **kwargs):
|
def run(args, *, env=None, logs_errors_to_stdout=False, **kwargs):
|
||||||
"""Wrapper around :py:func:`subprocess.run`
|
"""Wrapper around :py:func:`subprocess.run`
|
||||||
|
|
||||||
The main purpose of this wrapper is to log subprocess output in an orderly
|
The main purpose of this wrapper is to log subprocess output in an orderly
|
||||||
fashion that indentifies the responsible subprocess. An additional
|
fashion that indentifies the responsible subprocess. An additional
|
||||||
task is that this function goes to greater lengths to find possible Windows
|
task is that this function goes to greater lengths to find possible Windows
|
||||||
locations of our dependencies when they are not on the system PATH.
|
locations of our dependencies when they are not on the system PATH.
|
||||||
|
|
||||||
|
Arguments should be identical to ``subprocess.run``, except for following:
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
logs_errors_to_stdout: If True, indicates that the process writes its error
|
||||||
|
messages to stdout rather than stderr, so stdout should be logged
|
||||||
|
if there is an error. If False, stderr is logged. Could be used with
|
||||||
|
stderr=STDOUT, stdout=PIPE for example.
|
||||||
"""
|
"""
|
||||||
if not env:
|
if not env:
|
||||||
env = os.environ
|
env = os.environ
|
||||||
@@ -43,25 +51,29 @@ def run(args, *, env=None, **kwargs):
|
|||||||
args = _fix_windows_args(program, args, env)
|
args = _fix_windows_args(program, args, env)
|
||||||
|
|
||||||
log.debug("Running: %s", args)
|
log.debug("Running: %s", args)
|
||||||
process_log = log.getChild('subprocess.' + os.path.basename(program))
|
process_log = log.getChild(os.path.basename(program))
|
||||||
if sys.version_info < (3, 7) and os.name == 'nt':
|
if sys.version_info < (3, 7) and os.name == 'nt':
|
||||||
# Can't use close_fds=True on Windows with Python 3.6 or older
|
# Can't use close_fds=True on Windows with Python 3.6 or older
|
||||||
# https://bugs.python.org/issue19575, etc.
|
# https://bugs.python.org/issue19575, etc.
|
||||||
kwargs['close_fds'] = False
|
kwargs['close_fds'] = False
|
||||||
|
|
||||||
stderr = None
|
stderr = None
|
||||||
|
stderr_name = 'stderr' if not logs_errors_to_stdout else 'stdout'
|
||||||
try:
|
try:
|
||||||
proc = subprocess_run(args, env=env, **kwargs)
|
proc = subprocess_run(args, env=env, **kwargs)
|
||||||
except CalledProcessError as e:
|
except CalledProcessError as e:
|
||||||
stderr = getattr(e, 'stderr', None)
|
stderr = getattr(e, stderr_name, None)
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
stderr = getattr(proc, 'stderr', None)
|
stderr = getattr(proc, stderr_name, None)
|
||||||
finally:
|
finally:
|
||||||
if process_log.isEnabledFor(logging.DEBUG) and stderr:
|
if process_log.isEnabledFor(logging.DEBUG) and stderr:
|
||||||
with suppress(AttributeError, UnicodeDecodeError):
|
with suppress(AttributeError, UnicodeDecodeError):
|
||||||
stderr = stderr.decode('utf-8', 'replace')
|
stderr = stderr.decode('utf-8', 'replace')
|
||||||
process_log.debug("stderr = %s", stderr)
|
if logs_errors_to_stdout:
|
||||||
|
process_log.debug("stdout/stderr = %s", stderr)
|
||||||
|
else:
|
||||||
|
process_log.debug("stderr = %s", stderr)
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -302,8 +302,8 @@ def test_kodak_toc(resources, outpdf):
|
|||||||
|
|
||||||
p = pikepdf.open(outpdf)
|
p = pikepdf.open(outpdf)
|
||||||
|
|
||||||
if pikepdf.Name.First in p.root.Outlines:
|
if pikepdf.Name.First in p.Root.Outlines:
|
||||||
assert isinstance(p.root.Outlines.First, pikepdf.Dictionary)
|
assert isinstance(p.Root.Outlines.First, pikepdf.Dictionary)
|
||||||
|
|
||||||
|
|
||||||
def test_metadata_fixup_warning(resources, outdir, caplog):
|
def test_metadata_fixup_warning(resources, outdir, caplog):
|
||||||
|
|||||||
@@ -137,9 +137,9 @@ def test_report_file_size(tmp_path, caplog):
|
|||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
|
||||||
waste_of_space = b'Dummy' * 5000
|
waste_of_space = b'Dummy' * 5000
|
||||||
pdf.root.Dummy = waste_of_space
|
pdf.Root.Dummy = waste_of_space
|
||||||
pdf.save(in_)
|
pdf.save(in_)
|
||||||
pdf.root.Dummy2 = waste_of_space + waste_of_space
|
pdf.Root.Dummy2 = waste_of_space + waste_of_space
|
||||||
pdf.save(out)
|
pdf.save(out)
|
||||||
|
|
||||||
with patch('ocrmypdf._validation.jbig2enc.available', return_value=True), patch(
|
with patch('ocrmypdf._validation.jbig2enc.available', return_value=True), patch(
|
||||||
|
|||||||
Reference in New Issue
Block a user