optimize: add type hints

This commit is contained in:
James R. Barlow
2020-07-07 22:18:50 -07:00
parent 49734d5456
commit b20a6e4c5d
+47 -21
View File
@@ -22,9 +22,10 @@ from collections import defaultdict
from functools import partial
from os import fspath
from pathlib import Path
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Tuple, Union
import pikepdf
from pikepdf import Dictionary, Name
from pikepdf import Dictionary, Name, Object, Pdf, PdfImage
from PIL import Image
from tqdm import tqdm
@@ -41,30 +42,32 @@ DEFAULT_JPEG_QUALITY = 75
DEFAULT_PNG_QUALITY = 70
def img_name(root, xref, ext):
def img_name(root: Path, xref: int, ext: str) -> str:
return fspath(root / f'{xref:08d}{ext}')
def png_name(root, xref):
def png_name(root: Path, xref: int) -> str:
return img_name(root, xref, '.png')
def jpg_name(root, xref):
def jpg_name(root: Path, xref: int) -> str:
return img_name(root, xref, '.jpg')
def tif_name(root, xref):
def tif_name(root: Path, xref: int) -> str:
return img_name(root, xref, '.tif')
def extract_image_filter(pike, root, image, xref):
def extract_image_filter(
pike: Pdf, root: Path, image: Object, xref: int
) -> Optional[Tuple[PdfImage, Tuple[Name, Any]]]:
if image.Subtype != Name.Image:
return None
if image.Length < 100:
log.debug("Skipping small image, xref %s", xref)
return None
pim = pikepdf.PdfImage(image)
pim = PdfImage(image)
if len(pim.filter_decodeparms) > 1:
log.debug("Skipping multiply filtered, xref %s", xref)
@@ -83,7 +86,9 @@ def extract_image_filter(pike, root, image, xref):
return pim, filtdp
def extract_image_jbig2(*, pike, root, image, xref, options):
def extract_image_jbig2(
*, pike: pikepdf.Pdf, root: Path, image: Object, xref: int, options
) -> Optional[Tuple[int, str]]:
result = extract_image_filter(pike, root, image, xref)
if result is None:
return None
@@ -105,7 +110,9 @@ def extract_image_jbig2(*, pike, root, image, xref, options):
return None
def extract_image_generic(*, pike, root, image, xref, options):
def extract_image_generic(
*, pike: Pdf, root: Path, image: PdfImage, xref: int, options
) -> Optional[Tuple[int, str]]:
result = extract_image_filter(pike, root, image, xref)
if result is None:
return None
@@ -174,7 +181,12 @@ def extract_image_generic(*, pike, root, image, xref, options):
return None
def extract_images(pike, root, options, extract_fn):
def extract_images(
pike: Pdf,
root: Path,
options,
extract_fn: Callable[..., Optional[Tuple[int, str]]],
) -> Iterator[Tuple[int, int, str]]:
"""Extract image using extract_fn
Enumerate images on each page, lookup their xref/ID number in the PDF.
@@ -227,7 +239,9 @@ def extract_images(pike, root, options, extract_fn):
yield pageno_for_xref[xref], xref, ext
def extract_images_generic(pike, root, options):
def extract_images_generic(
pike: Pdf, root: Path, options
) -> Tuple[List[int], List[int]]:
"""Extract any >=2bpp image we think we can improve"""
jpegs = []
@@ -242,7 +256,9 @@ def extract_images_generic(pike, root, options):
return jpegs, pngs
def extract_images_jbig2(pike, root, options):
def extract_images_jbig2(
pike: Pdf, root: Path, options
) -> Dict[int, List[Tuple[int, str]]]:
"""Extract any bitonal image that we think we can improve as JBIG2"""
jbig2_groups = defaultdict(list)
@@ -258,10 +274,12 @@ def extract_images_jbig2(pike, root, options):
return jbig2_groups
def _produce_jbig2_images(jbig2_groups, root, options):
def _produce_jbig2_images(
jbig2_groups: Dict[int, List[Tuple[int, str]]], root: Path, options
) -> None:
"""Produce JBIG2 images from their groups"""
def jbig2_group_args(root, groups):
def jbig2_group_args(root: Path, groups: Dict[int, List[Tuple[int, str]]]):
for group, xref_exts in groups.items():
prefix = f'group{group:08d}'
yield dict(
@@ -270,7 +288,7 @@ def _produce_jbig2_images(jbig2_groups, root, options):
out_prefix=prefix,
)
def jbig2_single_args(root, groups):
def jbig2_single_args(root, groups: Dict[int, List[Tuple[int, str]]]):
for group, xref_exts in groups.items():
prefix = f'group{group:08d}'
# Second loop is to ensure multiple images per page are unpacked
@@ -306,7 +324,9 @@ def _produce_jbig2_images(jbig2_groups, root, options):
)
def convert_to_jbig2(pike, jbig2_groups, root, options):
def convert_to_jbig2(
pike: Pdf, jbig2_groups: Dict[int, List[Tuple[int, str]]], root: Path, options
) -> None:
"""Convert images to JBIG2 and insert into PDF.
When the JBIG2 page group size is > 1 we do several JBIG2 images at once
@@ -344,7 +364,7 @@ def convert_to_jbig2(pike, jbig2_groups, root, options):
)
def transcode_jpegs(pike, jpegs, root, options):
def transcode_jpegs(pike: Pdf, jpegs: Sequence[int], root: Path, options) -> None:
for xref in tqdm(
jpegs, desc="JPEGs", unit='image', disable=not options.progress_bar
):
@@ -367,7 +387,13 @@ def transcode_jpegs(pike, jpegs, root, options):
im_obj.write(compdata.read(), filter=Name.DCTDecode)
def transcode_pngs(pike, images, image_name_fn, root, options):
def transcode_pngs(
pike: Pdf,
images: Sequence[int],
image_name_fn: Callable[[Path, int], str],
root: Path,
options,
) -> None:
modified = set()
if options.optimize >= 2:
png_quality = (
@@ -431,7 +457,7 @@ def transcode_pngs(pike, images, image_name_fn, root, options):
rewrite_png_as_g4(pike, im_obj, compdata)
def rewrite_png_as_g4(pike, im_obj, compdata):
def rewrite_png_as_g4(pike: Pdf, im_obj: Object, compdata) -> None:
im_obj.BitsPerComponent = 1
im_obj.Width = compdata.w
im_obj.Height = compdata.h
@@ -451,7 +477,7 @@ def rewrite_png_as_g4(pike, im_obj, compdata):
return
def rewrite_png(pike, im_obj, compdata):
def rewrite_png(pike: Pdf, im_obj: Object, compdata) -> None:
# When a PNG is inserted into a PDF, we more or less copy the IDAT section from
# the PDF and transfer the rest of the PNG headers to PDF image metadata.
# One thing we have to do is tell the PDF reader whether a predictor was used
@@ -504,7 +530,7 @@ def rewrite_png(pike, im_obj, compdata):
im_obj.write(compdata.read(), filter=Name.FlateDecode, decode_parms=dparms)
def optimize(input_file, output_file, context, save_settings):
def optimize(input_file: Path, output_file: Path, context, save_settings) -> None:
options = context.options
if options.optimize == 0:
safe_symlink(input_file, output_file)