mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-01-26 16:58:56 +08:00
236 lines
7.2 KiB
Python
236 lines
7.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Combine slide images into a single PDF presentation.
|
|
|
|
This script takes multiple slide images (PNG, JPG) and combines them
|
|
into a single PDF file, maintaining aspect ratio and quality.
|
|
|
|
Usage:
|
|
# Combine all PNG files in a directory
|
|
python slides_to_pdf.py slides/*.png -o presentation.pdf
|
|
|
|
# Combine specific files in order
|
|
python slides_to_pdf.py slide_01.png slide_02.png slide_03.png -o presentation.pdf
|
|
|
|
# From a directory (sorted by filename)
|
|
python slides_to_pdf.py slides/ -o presentation.pdf
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
try:
|
|
from PIL import Image
|
|
except ImportError:
|
|
print("Error: Pillow library not found. Install with: pip install Pillow")
|
|
sys.exit(1)
|
|
|
|
|
|
def get_image_files(paths: List[str]) -> List[Path]:
|
|
"""
|
|
Get list of image files from paths (files or directories).
|
|
|
|
Args:
|
|
paths: List of file paths or directory paths
|
|
|
|
Returns:
|
|
Sorted list of image file paths
|
|
"""
|
|
image_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'}
|
|
image_files = []
|
|
|
|
for path_str in paths:
|
|
path = Path(path_str)
|
|
|
|
if path.is_file():
|
|
if path.suffix.lower() in image_extensions:
|
|
image_files.append(path)
|
|
else:
|
|
print(f"Warning: Skipping non-image file: {path}")
|
|
elif path.is_dir():
|
|
# Get all images in directory
|
|
for ext in image_extensions:
|
|
image_files.extend(path.glob(f"*{ext}"))
|
|
image_files.extend(path.glob(f"*{ext.upper()}"))
|
|
else:
|
|
# Try glob pattern
|
|
parent = path.parent
|
|
pattern = path.name
|
|
if parent.exists():
|
|
matches = list(parent.glob(pattern))
|
|
for match in matches:
|
|
if match.suffix.lower() in image_extensions:
|
|
image_files.append(match)
|
|
|
|
# Remove duplicates and sort
|
|
image_files = list(set(image_files))
|
|
image_files.sort(key=lambda x: x.name)
|
|
|
|
return image_files
|
|
|
|
|
|
def combine_images_to_pdf(image_paths: List[Path], output_path: Path,
|
|
dpi: int = 150, verbose: bool = False) -> bool:
|
|
"""
|
|
Combine multiple images into a single PDF.
|
|
|
|
Args:
|
|
image_paths: List of image file paths
|
|
output_path: Output PDF path
|
|
dpi: Resolution for the PDF (default: 150)
|
|
verbose: Print progress information
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
if not image_paths:
|
|
print("Error: No image files found")
|
|
return False
|
|
|
|
if verbose:
|
|
print(f"Combining {len(image_paths)} images into PDF...")
|
|
|
|
# Load all images
|
|
images = []
|
|
for i, img_path in enumerate(image_paths):
|
|
try:
|
|
img = Image.open(img_path)
|
|
# Convert to RGB if necessary (PDF doesn't support RGBA)
|
|
if img.mode in ('RGBA', 'P'):
|
|
# Create white background
|
|
background = Image.new('RGB', img.size, (255, 255, 255))
|
|
if img.mode == 'P':
|
|
img = img.convert('RGBA')
|
|
background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
|
|
img = background
|
|
elif img.mode != 'RGB':
|
|
img = img.convert('RGB')
|
|
|
|
images.append(img)
|
|
|
|
if verbose:
|
|
print(f" [{i+1}/{len(image_paths)}] Loaded: {img_path.name} ({img.size[0]}x{img.size[1]})")
|
|
except Exception as e:
|
|
print(f"Error loading {img_path}: {e}")
|
|
return False
|
|
|
|
if not images:
|
|
print("Error: No images could be loaded")
|
|
return False
|
|
|
|
# Create output directory if needed
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Save as PDF
|
|
try:
|
|
# First image
|
|
first_image = images[0]
|
|
|
|
# Remaining images (if any)
|
|
remaining_images = images[1:] if len(images) > 1 else []
|
|
|
|
# Save to PDF
|
|
first_image.save(
|
|
output_path,
|
|
"PDF",
|
|
resolution=dpi,
|
|
save_all=True,
|
|
append_images=remaining_images
|
|
)
|
|
|
|
if verbose:
|
|
print(f"\n✓ PDF created: {output_path}")
|
|
print(f" Total slides: {len(images)}")
|
|
file_size = output_path.stat().st_size
|
|
if file_size > 1024 * 1024:
|
|
print(f" File size: {file_size / (1024 * 1024):.1f} MB")
|
|
else:
|
|
print(f" File size: {file_size / 1024:.1f} KB")
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error creating PDF: {e}")
|
|
return False
|
|
finally:
|
|
# Close all images
|
|
for img in images:
|
|
img.close()
|
|
|
|
|
|
def main():
|
|
"""Command-line interface."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Combine slide images into a single PDF presentation",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Combine PNG files using glob pattern
|
|
python slides_to_pdf.py slides/*.png -o presentation.pdf
|
|
|
|
# Combine specific files in order
|
|
python slides_to_pdf.py title.png intro.png methods.png results.png -o talk.pdf
|
|
|
|
# Combine all images from a directory (sorted by filename)
|
|
python slides_to_pdf.py slides/ -o presentation.pdf
|
|
|
|
# With custom DPI and verbose output
|
|
python slides_to_pdf.py slides/*.png -o presentation.pdf --dpi 200 -v
|
|
|
|
Supported formats: PNG, JPG, JPEG, GIF, WEBP, BMP
|
|
|
|
Tips:
|
|
- Name your slide images with numbers for correct ordering:
|
|
01_title.png, 02_intro.png, 03_methods.png, etc.
|
|
- Use the generate_slide_image.py script to create slides first
|
|
- Standard presentation aspect ratio is 16:9 (1920x1080 or 1280x720)
|
|
"""
|
|
)
|
|
|
|
parser.add_argument("images", nargs="+",
|
|
help="Image files, directories, or glob patterns")
|
|
parser.add_argument("-o", "--output", required=True,
|
|
help="Output PDF file path")
|
|
parser.add_argument("--dpi", type=int, default=150,
|
|
help="PDF resolution in DPI (default: 150)")
|
|
parser.add_argument("-v", "--verbose", action="store_true",
|
|
help="Verbose output")
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Get image files
|
|
image_files = get_image_files(args.images)
|
|
|
|
if not image_files:
|
|
print("Error: No image files found matching the specified paths")
|
|
print("\nUsage examples:")
|
|
print(" python slides_to_pdf.py slides/*.png -o presentation.pdf")
|
|
print(" python slides_to_pdf.py slide1.png slide2.png -o presentation.pdf")
|
|
sys.exit(1)
|
|
|
|
print(f"Found {len(image_files)} image(s)")
|
|
if args.verbose:
|
|
for f in image_files:
|
|
print(f" - {f}")
|
|
|
|
# Combine into PDF
|
|
output_path = Path(args.output)
|
|
success = combine_images_to_pdf(
|
|
image_files,
|
|
output_path,
|
|
dpi=args.dpi,
|
|
verbose=args.verbose
|
|
)
|
|
|
|
if success:
|
|
print(f"\n✓ PDF created: {output_path}")
|
|
sys.exit(0)
|
|
else:
|
|
print(f"\n✗ Failed to create PDF")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|