import os
import subprocess
import shutil
import webbrowser
import sys
import argparse
import platform
import configparser
INPUT_DIR = "input"
OUTPUT_DIR = "output"
SETTINGS_FILE = "settings.ini"
QUALITY_SETTINGS = {
"screen": "/screen",
"ebook": "/ebook",
"printer": "/printer",
"prepress": "/prepress"
}
def create_default_settings():
config_content =
"""
; ==========================================
; PDF 壓縮設定
; ==========================================
;
; 可用品質:
;
; screen = 最小檔案,適合螢幕閱讀
; ebook = 一般文件,推薦使用
; printer = 列印品質
; prepress = 印刷品質
;
; 修改 quality 的值即可
;
; ==========================================
[default]
quality = ebook
; quality = screen
; quality = printer
; quality = prepress
"""
with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
f.write(config_content)
def load_settings():
if not os.path.exists(SETTINGS_FILE):
create_default_settings()
print(f"📄 已建立預設設定檔: {SETTINGS_FILE}")
config = configparser.ConfigParser()
config.read(SETTINGS_FILE, encoding="utf-8")
quality = config.get(
"default",
"quality",
fallback="ebook"
).lower()
if quality not in QUALITY_SETTINGS:
print(
f"⚠ settings.ini 中的 quality='{quality}' 無效"
)
print("⚠ 已自動改用 ebook")
quality = "ebook"
return quality
def check_ghostscript():
gs = shutil.which("gswin64c") or shutil.which("gs")
if gs is None:
print("❌ [未安裝 Ghostscript]")
if platform.system() == "Windows":
print("👉 請下載 Windows 版本 (gswin64)")
else:
print("👉 請下載對應系統版本")
print("👉 即將開啟下載頁面...")
webbrowser.open(
"https://www.ghostscript.com/download/gsdnld.html"
)
print("⚠ 若已安裝,請確認 Ghostscript 已加入 PATH")
sys.exit(1)
return gs
def compress_pdf(
gs_command,
input_path,
output_path,
quality
):
cmd = [
gs_command,
"-sDEVICE=pdfwrite",
"-dCompatibilityLevel=1.4",
f"-dPDFSETTINGS={QUALITY_SETTINGS[quality]}",
"-dNOPAUSE",
"-dQUIET",
"-dBATCH",
f"-sOutputFile={output_path}",
input_path
]
result = subprocess.run(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True
)
if result.returncode != 0:
raise RuntimeError(result.stderr)
def process_file(
gs_command,
file,
quality
):
input_path = os.path.join(INPUT_DIR, file)
name, ext = os.path.splitext(file)
output_path = os.path.join(
OUTPUT_DIR,
f"{name}_compressed{ext}"
)
compress_pdf(
gs_command,
input_path,
output_path,
quality
)
original_size = os.path.getsize(input_path)
compressed_size = os.path.getsize(output_path)
print(f"✔ {file}")
print(f" 原始: {original_size / 1024:.2f} KB")
print(f" 壓縮: {compressed_size / 1024:.2f} KB")
if compressed_size >= original_size:
print(" ⚠ 壓縮後反而變大,已刪除壓縮檔\n")
os.remove(output_path)
return original_size, original_size
reduction = original_size - compressed_size
ratio = reduction / original_size * 100
print(f" 減少: {reduction / 1024:.2f} KB")
print(f" 壓縮率: {ratio:.2f}%\n")
return original_size, compressed_size
def main():
default_quality = load_settings()
parser = argparse.ArgumentParser(
description="PDF 批次壓縮工具"
)
parser.add_argument(
"--quality",
choices=QUALITY_SETTINGS.keys(),
default=default_quality,
help=f"壓縮品質 (預設: {default_quality})"
)
args = parser.parse_args()
print(f"🔧 壓縮模式: {args.quality}")
print()
gs_command = check_ghostscript()
if not os.path.exists(INPUT_DIR):
os.makedirs(INPUT_DIR)
print(
f"📁 已建立資料夾: {INPUT_DIR}"
)
print("請放入 PDF 後重新執行")
return
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
files = [
f for f in os.listdir(INPUT_DIR)
if f.lower().endswith(".pdf")
]
if not files:
print("📂 input 資料夾內沒有 PDF")
return
total_original = 0
total_compressed = 0
for file in files:
try:
original, compressed = process_file(
gs_command,
file,
args.quality
)
total_original += original
total_compressed += compressed
except Exception as e:
print(f"✖ 失敗: {file}")
print(e)
print()
print("================================")
if total_original > 0:
reduction = total_original - total_compressed
ratio = reduction / total_original * 100
print(
f"原始總大小: "
f"{total_original / 1024 / 1024:.2f} MB"
)
print(
f"壓縮總大小: "
f"{total_compressed / 1024 / 1024:.2f} MB"
)
print(
f"節省空間: "
f"{reduction / 1024 / 1024:.2f} MB"
)
print(
f"總壓縮率: "
f"{ratio:.2f}%"
)
print("================================")
os.system("PAUSE")
if __name__ == "__main__":
main()