PerfectVector
Bởi Irene Kim

Thêm dấu chìm SVG vào PDF có sẵn bằng Python

Ghép dấu chìm SVG vào PDF có sẵn bằng svglib, ReportLab và pypdf. Kiểm tra độ đục, trang cắt xén, xoay, văn bản giữ lại và thứ tự lớp phủ.

14 phút đọc
Trên trang này

Để thêm dấu chìm SVG vào PDF có sẵn bằng Python, chuyển SVG thành bản vẽ ReportLab, kết xuất lên một lớp phủ PDF nhỏ rồi ghép lớp đó vào từng trang gốc bằng pypdf. Tính kích thước và vị trí từ hộp cắt xén nhìn thấy của từng trang, đồng thời chuẩn hóa xoay trang trước khi ghép.

Dùng over=True khi dấu cần hiện trên nền trang đục. Dấu bán trong suốt vẫn có thể nằm trên. Với over=False, cùng thiết kế nằm dưới nội dung hiện có và có thể biến mất sau trang có màu nền.

Ví dụ đầy đủ bên dưới tạo SVG riêng và PDF nguồn ba trang. Nó gồm nhiều cỡ trang, hộp cắt xén có gốc khác không và trang xoay. Kết quả là PDF mới; giữ tệp gốc riêng.

Chọn thao tác tài liệu trước

Điểm bắt đầuQuy trình hữu ích
PDF có sẵn cần biểu trưng SVG trên mỗi trangKết xuất lớp phủ SVG và ghép vào trang hiện có như bên dưới
Tạo PDF mới từ đầuDùng trực tiếp quy trình vẽ SVG ReportLab
SVG hiện có đã chứa đúng hìnhGiữ nguồn vector gốc đó; không cần đồ lại
Logo chỉ có ảnh rasterChuẩn bị hình phù hợp trước, rồi thử riêng việc ghép PDF

Hướng dẫn dấu chìm của pypdf phân biệt nội dung đặt trên và dưới trang. Ở đây, “dấu chìm” là biểu trưng nhìn thấy; over xác định thứ tự lớp thực tế. Đây không phải dấu bảo mật, phương pháp chống sửa đổi hay quy trình tài liệu đã ký.

1. Bắt đầu với SVG nhỏ được hỗ trợ

Phép thử dùng hai đường có tô: khung tam giác tâm rỗng và ba thanh. Cả hai mang fill-opacity="0.25". Không có văn bản, bộ lọc, ảnh nhúng hoặc tài nguyên ngoài.

Đầu vào hẹp đó quan trọng. svglib hỗ trợ tập con SVG hữu ích, nhưng giới hạn trong tài liệu gồm CSS hạn chế và tính năng chưa hỗ trợ như mặt nạ, foreignObject. SVG trông đúng trong trình duyệt vẫn cần kiểm tra sau chuyển đổi. Nếu tệp có raster, đọc cách tìm ảnh nhúng trong SVG trước khi coi kết quả hoàn toàn vector.

Bản mẫu đầu có văn bản SVG cùng khung. Trong bộ công cụ thử, văn bản chuyển đổi được tô đục hoàn toàn trong khi đường dẫn giữ độ đục 0,25. Tôi thay văn bản bằng hình đường dẫn tự tạo và thử bản thay. Kết quả cuối xác nhận trường hợp chỉ đường dẫn này; không xác nhận độ đục đúng cho mọi kết hợp văn bản và kiểu SVG.

2. Chạy ví dụ đầy đủ

Môi trường được thử là Python 3.12.14, svglib 2.2.0, ReportLab 4.4.9 và pypdf 6.10.0. Tài liệu pypdf hiện hành cũng mô tả bản mới hơn, nên phân biệt phiên bản thử với phiên bản tài liệu.

Trong môi trường riêng, cài các phiên bản đó:

python -m venv .venv
source .venv/bin/activate
python -m pip install svglib==2.2.0 reportlab==4.4.9 pypdf==6.10.0

Lưu mã sau thành watermark_demo.py. Nó gồm mọi hình đầu vào và tạo cả ba tệp PDF, nên không thiếu PDF hoặc SVG mẫu cần tải.

from io import BytesIO
from pathlib import Path
 
from pypdf import PdfReader, PdfWriter, Transformation
from pypdf.generic import RectangleObject
from reportlab.graphics import renderPDF
from reportlab.lib.colors import HexColor
from reportlab.pdfgen import canvas
from svglib.svglib import svg2rlg
 
SVG = '''<svg xmlns="http://www.w3.org/2000/svg" width="240" height="80"
             viewBox="0 0 240 80">
  <path fill="#315d6f" fill-opacity="0.25" fill-rule="nonzero"
        d="M10 60 L45 8 L80 60 Z M31 48 L59 48 L45 27 Z"/>
  <path fill="#315d6f" fill-opacity="0.25"
        d="M95 23 H216 V31 H95 Z M95 39 H190 V47 H95 Z
           M95 55 H205 V63 H95 Z"/>
</svg>'''
 
 
def make_demo_source(destination):
    stream = BytesIO()
    document = canvas.Canvas(stream, pagesize=(420, 300))
    specifications = [
        (420, 300, '#f5eddb', 'SOURCE PAGE ONE'),
        (300, 420, '#ffffff', 'SOURCE PAGE TWO'),
        (360, 260, '#e8eef0', 'SOURCE PAGE THREE')
    ]
    for width, height, background, label in specifications:
        document.setPageSize((width, height))
        document.setFillColor(HexColor(background))
        document.rect(0, 0, width, height, fill=1, stroke=0)
        document.setFillColor(HexColor('#183a42'))
        document.setFont('Helvetica-Bold', 16)
        document.drawString(28, height - 40, label)
        document.setFont('Helvetica', 10)
        document.drawString(28, height - 62, 'Original text remains in the PDF content.')
        document.drawString(28, 28, 'Original vector line and opaque page background')
        document.setStrokeColor(HexColor('#c47b44'))
        document.setLineWidth(3)
        document.line(28, 48, width - 28, 48)
        document.showPage()
    document.save()
    reader = PdfReader(stream)
    reader.pages[1].cropbox = RectangleObject([10, 20, 290, 400])
    reader.pages[2].rotate(90)
    writer = PdfWriter()
    for page in reader.pages:
        writer.add_page(page)
    writer.write(destination)
 
 
def make_overlay(svg_path, width, height):
    drawing = svg2rlg(str(svg_path))
    if drawing is None or drawing.width <= 0 or drawing.height <= 0:
        raise ValueError('The SVG did not produce a usable drawing')
    scale = width * 0.65 / drawing.width
    left = (width - drawing.width * scale) / 2
    bottom = (height - drawing.height * scale) / 2
    drawing.scale(scale, scale)
    stream = BytesIO()
    overlay = canvas.Canvas(stream, pagesize=(width, height))
    renderPDF.draw(drawing, overlay, left, bottom)
    overlay.save()
    return PdfReader(stream).pages[0]
 
 
def add_artwork(source_path, svg_path, output_path, *, over):
    reader = PdfReader(source_path)
    if reader.is_encrypted:
        raise ValueError('Encrypted inputs are outside this example')
    if reader.get_fields():
        raise ValueError('Form handling is outside this example')
    writer = PdfWriter()
    writer.clone_document_from_reader(reader)
    # Cloning in pypdf 6.10.0 does not retain the source header version.
    # This overlay uses transparency, introduced in PDF 1.4.
    writer.pdf_header = max(reader.pdf_header, '%PDF-1.4')
    for page in writer.pages:
        if page.rotation:
            page.transfer_rotation_to_content()
        box = page.cropbox
        width, height = float(box.width), float(box.height)
        overlay = make_overlay(svg_path, width, height)
        page.merge_transformed_page(
            overlay,
            Transformation().translate(float(box.left), float(box.bottom)),
            over=over,
            expand=False
        )
    writer.write(output_path)
 
 
if __name__ == '__main__':
    output = Path('watermark-output')
    output.mkdir(exist_ok=True)
    artwork = output / 'emblem.svg'
    artwork.write_text(SVG)
    source = output / 'source.pdf'
    make_demo_source(source)
    add_artwork(source, artwork, output / 'overlay-top.pdf', over=True)
    add_artwork(source, artwork, output / 'underlay.pdf', over=False)

Chạy:

python watermark_demo.py

Mở watermark-output/source.pdf, overlay-top.pdf và underlay.pdf. Trang một cần hiện biểu trưng trong bản phủ trên nhưng giấu nó trong bản lót dưới vì nền gốc đục. Hai trang còn lại kiểm tra cắt xén và xoay thay vì lặp cùng hình học trang.

Với PDF thông thường chưa ký của bạn, giữ make_overlay() và add_artwork(), rồi gọi:

add_artwork(
    Path("original.pdf"),
    Path("emblem.svg"),
    Path("watermarked.pdf"),
    over=True
)

Bộ ghi giữ phiên bản tiêu đề nguồn tối thiểu PDF 1.4 vì lớp phủ dùng độ trong suốt. Trong pypdf 6.10.0, chỉ nhân bản tài liệu có thể để tiêu đề mặc định PDF 1.3 của bộ ghi, ngay cả khi nguồn khai báo bản cao hơn. Phép gán rõ ràng tránh hạ phiên bản đó; không phải xác minh tuân thủ. Tài liệu tiêu đề của bộ ghi khuyến nghị phiên bản hỗ trợ mọi tính năng dùng trong tệp.

Các điều kiện bảo vệ từ chối đầu vào mã hóa và trường biểu mẫu được phát hiện. Chúng không phải kiểm toán tương thích hoặc chữ ký đầy đủ. Tài liệu đã ký, biểu mẫu, chú thích, mã hóa và yêu cầu riêng của tài liệu cần quy trình cùng xác minh riêng trước khi thay tệp đang dùng.

3. Đặt hình theo tọa độ trang nhìn thấy

Một point PDF bằng 1/72 inch. Trong phiên bản svglib này, kích thước SVG 240 × 80 không kèm đơn vị tạo bản vẽ 180 × 60 point: một pixel CSS thành 0,75 point. Mã đọc drawing.width và drawing.height thay vì mặc định pixel SVG bằng point PDF.

Ví dụ đặt vùng hiển thị SVG bằng 65% chiều rộng mỗi trang nhìn thấy, giữ tỷ lệ và căn giữa vùng đó. Nó căn hình chữ nhật bản vẽ khai báo, không phải khối lượng thị giác của biểu trưng. Giá trị 65% là lựa chọn bố cục ví dụ, không phải tiêu chuẩn dấu chìm. Tỷ lệ chỉ theo chiều rộng phù hợp biểu trưng ngang ở đây. Hình thay thế cao có thể vượt chiều cao trang; khi điều chỉnh hàm, giới hạn tỷ lệ theo cả chiều rộng và chiều cao có sẵn, rồi kiểm tra mọi trang trước khi dùng PDF của bạn.

TrangTình trạng nguồnHộp cắt nhìn thấy đầu ra, theo pointChiều rộng vùng SVG danh nghĩa
1Trang ngang 420 × 3000, 0 đến 420, 300273pt
2Trang 300 × 420 cắt xén vào trong10, 20 đến 290, 400182pt
3Trang 360 × 260 xoay 90°0, 0 đến 260, 360 sau chuẩn hóa169pt

Gọi transfer_rotation_to_content() chuyển xoay vào nội dung và các hộp trang. Nó không làm thẳng chữ gốc theo người xem. Văn bản trang ba vẫn theo hướng hiển thị của nguồn, còn biểu trưng mới được đặt trong tọa độ nhìn thấy đã chuẩn hóa. API trang pypdf khuyến nghị chuyển xoay trước khi ghép trang.

Lớp phủ bắt đầu tại gốc riêng. Dịch theo cropbox.left và cropbox.bottom đưa nó vào vùng nhìn thấy của đích ngay cả khi hộp cắt bắt đầu khác không. expand=False ngăn phép ghép nới các hộp trang.

4. Kiểm tra PDF đã lưu, không chỉ bản xem SVG

So sánh PDF kết xuất thực cho thấy lớp lót bị giấu, lớp phủ trên bán trong suốt hiện rõ, trang dọc cắt xén và trang nguồn xoay có biểu trưng vector căn giữa
Kết xuất Poppler thực của PDF đã lưu. Nền trang gốc giấu lớp lót; lớp phủ trên vẫn hiện. Hai góc xem cuối kiểm tra gốc cắt khác không và trang vốn xoay.

Tôi mở lại PDF đã lưu và kiểm tra luồng nội dung, hộp trang, xoay, văn bản trích xuất cùng tài nguyên ảnh, rồi kết xuất hộp cắt nhìn thấy bằng Poppler 26.02 ở 96dpi. Kiểm tra tệp cuối vượt qua 40 mục. So sánh trình duyệt cũng xác minh mã băm tệp đã lưu, ảnh kết xuất tải vào và 12 điểm dò pixel ở độ phân giải gốc.

Kiểm tra trên mẫu nàyKết quả quan sát
Số trang và văn bản gốc đã biếtCả ba trang và văn bản nguồn dự kiến còn trong cả hai bản
Hình học lớp phủHai đường có tô, 18 đỉnh được kiểm tra, alpha phần tô 0,25 trên mỗi trang
Tài nguyên ảnh thêmKhông có trong các trang nguồn và đầu ra tự tạo này
Vị trí và hộp trangKhớp hộp cắt khai báo và tính toán phép biến đổi đã ghi; xoay đầu ra bằng không
Điểm dò khung và tâm rỗngPixel khung phủ trên khớp hòa màu dự kiến trong ±2 giá trị RGB; tâm rỗng giữ nền trang
Lớp lót dưới nền đụcKết xuất nguồn và lớp lót trang một khác không pixel ở 96dpi

Kiểm toán vị trí ban đầu không đạt so sánh nghiêm ngặt với tọa độ dấu phẩy động lý tưởng ở dung sai 0,00001pt. ReportLab ghi ma trận biến đổi với độ chính xác hữu hạn. Tôi giữ kết quả thất bại, tính độc lập ma trận ghi dự kiến và giới hạn làm tròn, rồi so đỉnh đã lưu với các giá trị đó. Chênh lệch tọa độ lý tưởng lớn nhất quan sát khoảng 0,000054pt, nằm trong giới hạn suy ra 0,000086pt. Điều này kiểm tra vị trí nhìn cân giữa trong các tệp này, không bảo đảm độ chính xác chung.

Để kiểm tra trực quan nhanh cục bộ khi đã cài Poppler:

pdftoppm -cropbox -r 96 -png watermark-output/overlay-top.pdf watermark-output/top
pdftoppm -cropbox -r 96 -png watermark-output/underlay.pdf watermark-output/below

Kiểm tra mọi trang ở mức phóng hữu ích. Cũng chọn hoặc trích xuất văn bản gốc dự kiến, kiểm tra kích thước trang và xem dấu chìm còn là đường hay thêm ảnh. Chỉ ảnh chụp sắc nét không xác nhận được các thuộc tính tệp đó. Phép thử không đo in ấn, tương thích trình xem, tiết kiệm dung lượng hoặc khả năng chống xóa dấu chìm.

Sửa lỗi ở đúng giai đoạn

Triệu chứngKiểm tra hoặc thay đổi
Dấu biến mấtSo over=False với over=True; tìm nền gốc đục
Dấu lệch trên trang cắt xénDùng kích thước và gốc hộp cắt, không dùng cỡ trang cố định
Trang xoay làm dấu theo hướng bất ngờChuẩn hóa xoay trước khi tính kích thước nhìn thấy; kiểm tra kết quả
Độ đục hoặc kiểu đổiĐơn giản SVG, kiểm tra trạng thái tô của PDF chuyển đổi và thử lại chính tệp đó
Vùng giống ảnh chụp hoặc có pixel vẫn cònKiểm tra ảnh nhúng trong SVG; lớp bọc SVG không biến bitmap thành đường
Văn bản, biểu mẫu hoặc hành vi tài liệu đổiDừng và xác minh tính năng thực của tài liệu thay vì cho rằng ghép thành công đã giữ mọi thứ

PDF gốc vẫn là nội dung trang; mã không raster hóa toàn trang. Điều đó không chứng minh mọi PDF đầu vào sẽ qua phép ghép tùy ý mà giữ nguyên. Dùng bản sao và xác định nội dung cùng tính năng phải tiếp tục hoạt động trước khi xử lý lô lớn.

Chuẩn bị hình còn thiếu trước khi ghép

Nếu logo hiện có đã là SVG sạch, giữ nó. Nếu chỉ còn logo raster phẳng phù hợp, công cụ vector hóa logo của PerfectVector có thể giúp chuẩn bị hình chỉnh được trước bước Python. Kiểm tra hình bóng, lỗ và chi tiết nhỏ khôi phục, rồi chạy lại chuyển đổi SVG cùng kiểm tra PDF đã lưu. Ví dụ dùng đường gốc tự tạo và không chuyển đổi PerfectVector. Vector hóa không ghép trang, xác minh chữ ký hoặc bảo đảm tương thích svglib.

Câu hỏi thường gặp

pypdf có đọc trực tiếp dấu chìm SVG không? Ví dụ trước hết chuyển SVG qua svglib và ReportLab thành lớp phủ PDF. pypdf sau đó ghép nội dung trang PDF; nó không diễn giải nguồn SVG trong quy trình này.

Vì sao dấu chìm biến mất sau trang PDF? Lớp lót có thể bị nền đục hiện có che. Dùng over=True cho lớp phủ trên nhìn thấy và đặt độ đục hình riêng. Kiểm tra kết quả thực trên từng trang.

Cách này có giữ vector và văn bản gốc không? Biểu trưng chỉ đường đã thử vẫn là đường có tô, và văn bản gốc dự kiến vẫn trích xuất được trên cả ba trang. Tính năng SVG và tài liệu PDF ngoài mẫu cần xác minh riêng.

Ghi lại PDF có giữ chữ ký số không? Đừng dùng ví dụ như quy trình tài liệu đã ký. Nó ghi lại tệp và không kiểm tra bảo toàn hoặc hiệu lực chữ ký. Giữ bản gốc đã ký và dùng quy trình phù hợp yêu cầu ký của nó.

Nguồn

  1. pypdf: Thêm con dấu hoặc dấu chìm — Thứ tự phủ trên, lót dưới, ghép và hướng dẫn xoay.
  2. pypdf: PageObject API — Hộp trang, chuyển xoay và hành vi ghép có biến đổi.
  3. Tài liệu dự án svglib — Chuyển SVG sang ReportLab, đơn vị và giới hạn tính năng hỗ trợ.
  4. Hướng dẫn đồ họa ReportLab — Đối tượng bản vẽ và kết xuất PDF trong hệ đồ họa ReportLab.
  5. pypdf 6.10.0: Phiên bản tiêu đề PDF — Khai báo phiên bản PDF hỗ trợ tính năng dùng trong đầu ra.

Giữ hình gốc khi bạn có. Khi chỉ còn logo raster phù hợp, chuẩn bị SVG chỉnh được bằng PerfectVector, kiểm tra hình rồi xác minh riêng PDF vừa ghép.

Thêm từ blog

PerfectVector

Bắt đầu với SVG gọn sạch hơn
và dễ chỉnh sửa hơn

Không cần thẻ tín dụng