未加密-批量图片转PDF(每张图片都独立转换PDF)-Python脚本

查看 15|回复 1
作者:jie04551   
这个脚本是将每张图片都独立转换PDF 文件名不变
[Python] 纯文本查看 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
img2singlepdf.py - Convert each image to its own PDF (same basename).
Examples:
  python img2singlepdf.py imgs/*.jpg
  python img2singlepdf.py folder_with_images --outdir export_pdfs --pagesize A4 --margin 36
  python img2singlepdf.py *.png --fit contain --autorotate
"""
import argparse
import os
import re
import sys
import glob
from typing import List, Tuple
from PIL import Image, ImageOps
PRESETS = {
    "A4": (595, 842),       # 210x297 mm at 72 dpi
    "LETTER": (612, 792),   # 8.5x11 in at 72 dpi
    "LEGAL": (612, 1008),
    "A3": (842, 1191),
    "A5": (420, 595),
}
def parse_pagesize(s: str) -> Tuple[int, int]:
    if not s:
        return PRESETS["A4"]
    key = s.strip().upper()
    if key in PRESETS:
        return PRESETS[key]
    m = re.match(r"^\s*(\d+)\s*[xX]\s*(\d+)\s*$", s)
    if m:
        return int(m.group(1)), int(m.group(2))
    raise argparse.ArgumentTypeError(f"Invalid pagesize: {s}. Use A4, Letter, or WxH like 1240x1754.")
def collect_images(paths: List[str]) -> List[str]:
    imgs = []
    for p in paths:
        if os.path.isdir(p):
            for root, _, files in os.walk(p):
                for f in files:
                    if re.search(r"\.(jpe?g|png|webp|tiff?|bmp)$", f, re.I):
                        imgs.append(os.path.join(root, f))
        else:
            expanded = glob.glob(p)
            if expanded:
                imgs.extend([e for e in expanded if os.path.isfile(e)])
            elif os.path.isfile(p):
                imgs.append(p)
    # 去重并保持顺序
    seen, uniq = set(), []
    for x in imgs:
        ab = os.path.abspath(x)
        if ab not in seen:
            seen.add(ab)
            uniq.append(ab)
    return uniq
def to_rgb_safely(im: Image.Image) -> Image.Image:
    if im.mode in ("RGBA", "LA"):
        bg = Image.new("RGB", im.size, (255, 255, 255))
        bg.paste(im, mask=im.split()[-1])
        return bg
    if im.mode != "RGB":
        return im.convert("RGB")
    return im
def autorotate_page_size(img_w: int, img_h: int, page_w: int, page_h: int):
    if (img_w >= img_h) != (page_w >= page_h):
        return page_h, page_w
    return page_w, page_h
def resize_with_fit(img: Image.Image, target_w: int, target_h: int, fit: str) -> Image.Image:
    fit = fit.lower()
    if fit == "none":
        return img
    if fit == "stretch":
        return img.resize((target_w, target_h), Image.LANCZOS)
    if fit == "contain":
        return ImageOps.contain(img, (target_w, target_h), Image.LANCZOS)
    if fit == "cover":
        # scale to cover then center-crop
        sw, sh = img.size
        ratio = max(target_w / sw, target_h / sh)
        new_size = (max(1, int(sw * ratio)), max(1, int(sh * ratio)))
        img = img.resize(new_size, Image.LANCZOS)
        left = (img.width - target_w) // 2
        top = (img.height - target_h) // 2
        return img.crop((left, top, left + target_w, top + target_h))
    raise ValueError(f"Unknown fit: {fit}")
def make_pdf_for_image(img_path: str, outdir: str, pagesize: Tuple[int,int], margin: int, fit: str, autorotate: bool, dpi: int):
    base = os.path.splitext(os.path.basename(img_path))[0]
    out_pdf = os.path.join(outdir, base + ".pdf")
    with Image.open(img_path) as im:
        im = to_rgb_safely(im)
        points_w, points_h = pagesize
        if autorotate:
            points_w, points_h = autorotate_page_size(im.width, im.height, points_w, points_h)
        pixel_w = int(points_w * dpi / 72 + 0.5)
        pixel_h = int(points_h * dpi / 72 + 0.5)
        pixel_margin = int(margin * dpi / 72 + 0.5)
        cw, ch = max(1, pixel_w - 2 * pixel_margin), max(1, pixel_h - 2 * pixel_margin)
        page = Image.new("RGB", (pixel_w, pixel_h), (255, 255, 255))
        work = resize_with_fit(im, cw, ch, fit)
        # 居中粘贴
        x = pixel_margin + (cw - work.width) // 2
        y = pixel_margin + (ch - work.height) // 2
        page.paste(work, (x, y))
        # 保存为单页 PDF,Title 使用原文件名
        page.save(out_pdf, "PDF", resolution=float(dpi), save_all=False, title=base)
    return out_pdf
def main():
    ap = argparse.ArgumentParser(description="Convert each image to its own PDF with same basename.")
    ap.add_argument("inputs", nargs="+", help="Image files, folders, or globs.")
    ap.add_argument("--outdir", default=".", help="Output directory for PDFs. Default: current directory")
    ap.add_argument("--pagesize", default="A4", type=parse_pagesize,
                    help="Page size (A4/Letter/Legal/A3/A5 or WxH like 1240x1754). Default: A4")
    ap.add_argument("--margin", type=int, default=36, help="Margin in points (72pt = 1 inch). Default: 36")
    ap.add_argument("--fit", choices=["contain", "cover", "stretch", "none"], default="contain",
                    help="How image fits the page content area. Default: contain")
    ap.add_argument("--autorotate", action="store_true", help="Auto-rotate page orientation to match image.")
    ap.add_argument("--dpi", type=int, default=300, help="Output resolution in DPI (dots per inch). Default: 300")
    args = ap.parse_args()
    imgs = collect_images(args.inputs)
    if not imgs:
        print("No images found.", file=sys.stderr)
        sys.exit(1)
    os.makedirs(args.outdir, exist_ok=True)
    page_w, page_h = args.pagesize
    margin = max(0, int(args.margin))
    ok, fail = 0, 0
    for p in imgs:
        try:
            out_pdf = make_pdf_for_image(
                p, args.outdir, (page_w, page_h), margin, args.fit, args.autorotate, args.dpi
            )
            print(f"OK  -> {out_pdf}")
            ok += 1
        except Exception as e:
            print(f"FAIL -> {p}: {e}", file=sys.stderr)
            fail += 1
    print(f"Done. Success: {ok}, Failed: {fail}")
if __name__ == "__main__":
    main()
运行命令
[Python] 纯文本查看 复制代码python tu.py imgs/*.jpg
获取当前目录的imgs文件夹所有jpg格式的图片转换成PDF到当前目录
指定dpi
[Python] 纯文本查看 复制代码python tu.py imgs/*.jpg --dpi 600
指定 转换的dpi 为600   默认300



图片, 每张

Faiz4399   

正好要用,非常感谢
您需要登录后才可以回帖 登录 | 立即注册

返回顶部