Spaces:
Sleeping
Sleeping
Download pdf_utils.py from roseluo/WaveSign-API: direct link, hf CLI and curl.
- Browser
- Download file 3.41 kB
-
https://huggingface.co/spaces/roseluo/WaveSign-API/resolve/main/pdf_utils.py
- Command line
-
hf download hf://spaces/roseluo/WaveSign-API/pdf_utils.py
-
curl -L -o pdf_utils.py https://huggingface.co/spaces/roseluo/WaveSign-API/resolve/main/pdf_utils.py
3.41 kB
| """ | |
| WaveSign β PDF Support via Rasterization | |
| ----------------------------------------- | |
| Pipeline: | |
| 1. Rasterize original PDF pages at 300 DPI | |
| 2. Embed diffraction watermark into each page image | |
| 3. Repackage watermarked pages into a signed image-PDF | |
| 4. Rasterize the signed PDF again β sign those final images | |
| (ensures verification always matches the exact output PDF pixels) | |
| Output is an image-PDF (not text-searchable) β correct for tamper-evident signing. | |
| """ | |
| import io | |
| from pdf2image import convert_from_bytes | |
| from reportlab.pdfgen import canvas as rl_canvas | |
| from reportlab.lib.utils import ImageReader | |
| from pypdf import PdfReader | |
| from core import embed_watermark, sign_image, verify_image, detect_mode | |
| DPI = 300 | |
| def pdf_to_images(pdf_bytes: bytes, dpi: int = DPI) -> list: | |
| """Rasterize all PDF pages. Returns list of PIL Images.""" | |
| return convert_from_bytes(pdf_bytes, dpi=dpi) | |
| def images_to_pdf(images: list) -> bytes: | |
| """Pack PIL Images into a single PDF. Each image fills its page.""" | |
| buf = io.BytesIO() | |
| first_w, first_h = images[0].size | |
| c = rl_canvas.Canvas(buf, pagesize=(first_w * 72 / DPI, first_h * 72 / DPI)) | |
| for img in images: | |
| w_px, h_px = img.size | |
| pw, ph = w_px * 72 / DPI, h_px * 72 / DPI | |
| c.setPageSize((pw, ph)) | |
| img_buf = io.BytesIO() | |
| img.save(img_buf, format="PNG") | |
| img_buf.seek(0) | |
| c.drawImage(ImageReader(img_buf), 0, 0, width=pw, height=ph) | |
| c.showPage() | |
| c.save() | |
| buf.seek(0) | |
| return buf.read() | |
| def sign_pdf(pdf_bytes: bytes, secret: str, dpi: int = DPI, strength: float = 0.03): | |
| """ | |
| Sign all pages of a PDF. | |
| Critical: signatures are computed from the FINAL rasterized output PDF, | |
| not from intermediate watermarked images. This ensures verify_pdf always | |
| produces matching results without round-trip pixel drift. | |
| Returns: (signed_pdf_bytes, signatures_list, n_pages) | |
| """ | |
| # Step 1: rasterize original pages | |
| images = pdf_to_images(pdf_bytes, dpi=dpi) | |
| # Step 2: embed watermark into each page | |
| watermarked = [] | |
| for img in images: | |
| mode = detect_mode(img) | |
| wm = embed_watermark(img, secret, strength=strength, mode=mode) | |
| watermarked.append(wm) | |
| # Step 3: pack watermarked pages into signed PDF | |
| signed_pdf_bytes = images_to_pdf(watermarked) | |
| # Step 4: rasterize signed PDF β sign these exact pixels | |
| final_images = pdf_to_images(signed_pdf_bytes, dpi=dpi) | |
| n_pages = len(final_images) | |
| sigs = [] | |
| for i, img in enumerate(final_images): | |
| sig = sign_image(img, secret, mode='document') | |
| sig["page_index"] = i | |
| sigs.append(sig) | |
| return signed_pdf_bytes, sigs, n_pages | |
| def verify_pdf(pdf_bytes: bytes, secret: str, signatures: list) -> list: | |
| """ | |
| Verify all pages of a signed PDF against per-page signatures. | |
| Returns list of per-page result dicts with page_index key. | |
| """ | |
| images = pdf_to_images(pdf_bytes, dpi=DPI) | |
| results = [] | |
| for i, (img, sig) in enumerate(zip(images, signatures)): | |
| result = verify_image(img, secret, sig) | |
| result["page_index"] = i | |
| results.append(result) | |
| return results | |
| def get_pdf_page_count(pdf_bytes: bytes) -> int: | |
| """Quick page count without rasterization.""" | |
| return len(PdfReader(io.BytesIO(pdf_bytes)).pages) | |