diff --git a/pdf_cleaner.py b/pdf_cleaner.py index 86ee242..a02b7f1 100755 --- a/pdf_cleaner.py +++ b/pdf_cleaner.py @@ -1,11 +1,7 @@ #!/usr/bin/env -S uv run import os -import shutil import subprocess -from pathlib import Path -from typing import List, Tuple - import click import cv2 import img2pdf @@ -13,7 +9,6 @@ import numpy as np from pdf2image import convert_from_path import settings -from settings import OptimizationLevel from PIL import Image import concurrent.futures @@ -23,8 +18,6 @@ class Settings: OPTIPNG_OPTIMIZATION_LEVEL = 7 PDF_BORDER_SIZE = 50 -settings = Settings() - def ensure_temp_dir(): """Ensure temporary directory exists and return its path.""" temp_dir = "temp_processed_images" @@ -282,7 +275,7 @@ def run_optipng(file_path: str) -> bool: bool: True if successful, False otherwise """ try: - result = subprocess.run( + subprocess.run( ['optipng', f'-o{settings.OPTIPNG_OPTIMIZATION_LEVEL}', file_path], capture_output=True, text=True, @@ -307,13 +300,12 @@ def optimize(level): 2: Level 1 + convert to 1-bit monochrome 3: Level 2 + optipng optimization """ - temp_dir = ensure_temp_dir() temp_files = get_temp_files() if not temp_files: print("No pages found in temporary directory. Run 'extract' first.") return - opt_level = OptimizationLevel(level) + opt_level = settings.OptimizationLevel(level) total_files = len(temp_files) successful = { @@ -325,10 +317,10 @@ def optimize(level): print(f"Processing {total_files} images at optimization level {level}...") # Step 1: Convert to monochrome if level >= 2 - if int(opt_level) >= int(OptimizationLevel.MONOCHROME): + if int(opt_level) >= int(settings.OptimizationLevel.MONOCHROME): print("\nConverting to monochrome...") with concurrent.futures.ThreadPoolExecutor() as executor: - futures = {executor.submit(convert_to_monochrome, file_path, (opt_level == OptimizationLevel.MONOCHROME)): file_path for file_path in temp_files} + futures = {executor.submit(convert_to_monochrome, file_path, (opt_level == settings.OptimizationLevel.MONOCHROME)): file_path for file_path in temp_files} for future in concurrent.futures.as_completed(futures): file_path = futures[future] try: @@ -343,7 +335,7 @@ def optimize(level): # Step 2: Always trim whitespace print("\nTrimming whitespace from images...") with concurrent.futures.ThreadPoolExecutor() as executor: - futures = {executor.submit(trim_whitespace, file_path, (opt_level == OptimizationLevel.TRIM)): file_path for file_path in temp_files} + futures = {executor.submit(trim_whitespace, file_path, (opt_level == settings.OptimizationLevel.TRIM)): file_path for file_path in temp_files} for future in concurrent.futures.as_completed(futures): file_path = futures[future] try: @@ -356,7 +348,7 @@ def optimize(level): print(f"{os.path.basename(file_path)} generated an exception: {exc}") # Step 3: Run optipng if level = 3 - if opt_level == OptimizationLevel.FULL: + if opt_level == settings.OptimizationLevel.FULL: if check_optipng_installed(): print("\nOptimizing PNG files with optipng...") with concurrent.futures.ThreadPoolExecutor() as executor: @@ -377,9 +369,9 @@ def optimize(level): # Print summary print("\nOptimization complete!") print(f"Successfully trimmed: {successful['trim']}/{total_files} images") - if int(opt_level) >= int(OptimizationLevel.MONOCHROME): + if int(opt_level) >= int(settings.OptimizationLevel.MONOCHROME): print(f"Successfully converted to monochrome: {successful['monochrome']}/{total_files} images") - if opt_level == OptimizationLevel.FULL and check_optipng_installed(): + if opt_level == settings.OptimizationLevel.FULL and check_optipng_installed(): print(f"Successfully optimized with optipng: {successful['optipng']}/{total_files} images") @cli.command() diff --git a/pyproject.toml b/pyproject.toml index 8b6bf44..d520234 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,3 +12,8 @@ dependencies = [ "click>=8.1.7", "pydantic>=2.10.3", ] + +[dependency-groups] +dev = [ + "ruff>=0.8.1", +] diff --git a/uv.lock b/uv.lock index e8c7af7..7b4cc5e 100644 --- a/uv.lock +++ b/uv.lock @@ -40,12 +40,20 @@ dependencies = [ { name = "pydantic" }, ] +[package.dev-dependencies] +dev = [ + { name = "ruff" }, +] + [package.metadata] requires-dist = [ { name = "click", specifier = ">=8.1.7" }, { name = "pydantic", specifier = ">=2.10.3" }, ] +[package.metadata.requires-dev] +dev = [{ name = "ruff", specifier = ">=0.8.1" }] + [[package]] name = "pydantic" version = "2.10.3" @@ -99,6 +107,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/c3/b15fb833926d91d982fde29c0624c9f225da743c7af801dace0d4e187e71/pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", size = 1882983 }, ] +[[package]] +name = "ruff" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d0/8ff5b189d125f4260f2255d143bf2fa413b69c2610c405ace7a0a8ec81ec/ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f", size = 3313222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/d6/1a6314e568db88acdbb5121ed53e2c52cebf3720d3437a76f82f923bf171/ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5", size = 10532605 }, + { url = "https://files.pythonhosted.org/packages/89/a8/a957a8812e31facffb6a26a30be0b5b4af000a6e30c7d43a22a5232a3398/ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087", size = 10278243 }, + { url = "https://files.pythonhosted.org/packages/a8/23/9db40fa19c453fabf94f7a35c61c58f20e8200b4734a20839515a19da790/ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209", size = 9917739 }, + { url = "https://files.pythonhosted.org/packages/e2/a0/6ee2d949835d5701d832fc5acd05c0bfdad5e89cfdd074a171411f5ccad5/ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871", size = 10779153 }, + { url = "https://files.pythonhosted.org/packages/7a/25/9c11dca9404ef1eb24833f780146236131a3c7941de394bc356912ef1041/ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1", size = 10304387 }, + { url = "https://files.pythonhosted.org/packages/c8/b9/84c323780db1b06feae603a707d82dbbd85955c8c917738571c65d7d5aff/ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5", size = 11360351 }, + { url = "https://files.pythonhosted.org/packages/6b/e1/9d4bbb2ace7aad14ded20e4674a48cda5b902aed7a1b14e6b028067060c4/ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d", size = 12022879 }, + { url = "https://files.pythonhosted.org/packages/75/28/752ff6120c0e7f9981bc4bc275d540c7f36db1379ba9db9142f69c88db21/ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26", size = 11610354 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/967b61c2cc8ebd1df877607fbe462bc1e1220b4a30ae3352648aec8c24bd/ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1", size = 12813976 }, + { url = "https://files.pythonhosted.org/packages/7f/29/e059f945d6bd2d90213387b8c360187f2fefc989ddcee6bbf3c241329b92/ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c", size = 11154564 }, + { url = "https://files.pythonhosted.org/packages/55/47/cbd05e5a62f3fb4c072bc65c1e8fd709924cad1c7ec60a1000d1e4ee8307/ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa", size = 10760604 }, + { url = "https://files.pythonhosted.org/packages/bb/ee/4c3981c47147c72647a198a94202633130cfda0fc95cd863a553b6f65c6a/ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540", size = 10391071 }, + { url = "https://files.pythonhosted.org/packages/6b/e6/083eb61300214590b188616a8ac6ae1ef5730a0974240fb4bec9c17de78b/ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9", size = 10896657 }, + { url = "https://files.pythonhosted.org/packages/77/bd/aacdb8285d10f1b943dbeb818968efca35459afc29f66ae3bd4596fbf954/ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5", size = 11228362 }, + { url = "https://files.pythonhosted.org/packages/39/72/fcb7ad41947f38b4eaa702aca0a361af0e9c2bf671d7fd964480670c297e/ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790", size = 8803476 }, + { url = "https://files.pythonhosted.org/packages/e4/ea/cae9aeb0f4822c44651c8407baacdb2e5b4dcd7b31a84e1c5df33aa2cc20/ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6", size = 9614463 }, + { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621 }, +] + [[package]] name = "typing-extensions" version = "4.12.2"