PerfectVector
โดย Irene Kimอ่าน 4 นาที

ภาพ SVG ใน WeasyPrint: แก้โลโก้หายใน PDF

แก้โลโก้ SVG ที่หายใน PDF จาก WeasyPrint ด้วย base URL ที่ถูกต้องและขนาด CSS ที่ชัดเจน แล้วตรวจพาธเวกเตอร์ สัดส่วน และภาพใน PDF ที่บันทึก

ในหน้านี้

WeasyPrint วาง SVG ใน PDF ผ่านองค์ประกอบภาพ HTML ได้ หากโลโก้หายเมื่อส่ง HTML เป็นสตริง ให้ตรวจ URL ภาพก่อน: พาธสัมพัทธ์ต้องมี base URL เมื่อโหลดไฟล์ได้แล้ว ให้ตั้งขนาดสำหรับพิมพ์ใน CSS และตรวจ PDF ที่บันทึก เอกสาร API ปัจจุบันของ WeasyPrint อธิบายข้อกำหนด base_url นี้

ตัวอย่างด้านล่างทำให้เกิดกรณีภาพหายด้วยโลโก้รูปกรอบที่สร้างขึ้นเอง และตรวจว่า PDF ที่แก้แล้วมีเรขาคณิตการวาดเวกเตอร์ตามขนาดที่ขอ หากมี SVG อยู่แล้ว ให้เริ่มจากไฟล์นั้น การแปลงเวกเตอร์จะเข้ามาในกระบวนการนี้เฉพาะเมื่อภาพต้นทางเป็นพิกเซล

ตรวจการโหลดก่อนเปลี่ยนภาพ

PDF อาจสร้างสำเร็จแม้โลโก้หาย ตรวจคำเตือนทรัพยากร แก้พาธภาพ และเรนเดอร์ SVG เดิมใหม่ก่อนแก้รูปร่างหรือแปลงเป็นรูปแบบอื่น

ทำให้เกิด SVG หายในเอกสารเล็ก ๆ

เราทดสอบด้วย Python 3.13.9 และ WeasyPrint 70.0 ใช้สภาพแวดล้อมโครงการและติดตั้งเวอร์ชันที่กำหนด:

python -m pip install "weasyprint==70.0"
python -m weasyprint --info

WeasyPrint ยังต้องใช้ไลบรารี native รวมถึง Pango ทำตามคำแนะนำติดตั้งตามระบบปฏิบัติการ หากคำสั่ง import หรือแสดงเวอร์ชันล้มเหลว การขาดไลบรารี native เป็นคนละปัญหากับโลโก้หาย

สร้างโครงสร้างโฟลเดอร์นี้:

example/
  render.py
  assets/
    logo.svg

บันทึกภาพสาธิตต้นฉบับนี้เป็น assets/logo.svg ช่องเปิดตรงกลางมีประโยชน์ให้ตรวจมากกว่าสี่เหลี่ยมทึบ ภาพนี้เป็นเรขาคณิตที่เขียนด้วยมือ ไม่ใช่ผลแปลงจาก PerfectVector

<svg xmlns="http://www.w3.org/2000/svg"
     width="120" height="60" viewBox="0 0 120 60">
  <path fill="#087f8c" fill-rule="evenodd"
        d="M0 0H120V60H0Z M20 15V45H100V15Z"/>
</svg>

ใส่โค้ดต่อไปนี้ใน render.py:

from pathlib import Path
import logging
from weasyprint import HTML
 
logging.basicConfig(level=logging.WARNING)
root = Path(__file__).resolve().parent
 
html = """<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4; margin: 20mm; }
    body { margin: 0; }
    .logo { display: block; width: 60mm; height: auto; }
  </style>
</head>
<body>
  <img class="logo" src="assets/logo.svg" alt="Frame logo">
</body>
</html>
"""
 
HTML(string=html).write_pdf(root / "missing-base.pdf")
 
HTML(
    string=html,
    base_url=root.as_uri() + "/",
).write_pdf(root / "resolved.pdf")

รัน python render.py ใน PDF แรกของเราไม่มีกรอบ และข้อความทางเลือกของภาพปรากฏแทน บันทึกเหตุการณ์มีข้อความ:

Relative URI reference without a base URI: assets/logo.svg

PDF ที่สองแสดงกรอบสีเขียวอมฟ้าพร้อมช่องเปิดครบ การเรียกทั้งสองครั้งสร้างไฟล์ได้ ดังนั้นการมีไฟล์ PDF อย่างเดียวจึงไม่ใช่การตรวจความสำเร็จที่มีประโยชน์

กำหนดโฟลเดอร์เริ่มต้นที่แน่นอนให้พาธสัมพัทธ์

assets/logo.svg ระบุไฟล์โดยอ้างอิงตำแหน่งอื่น base_url ในการเรียกครั้งที่สองให้ตำแหน่งเริ่มต้นนั้น root.as_uri() สร้าง URI ไฟล์แบบสัมบูรณ์ และเครื่องหมายทับท้ายทำให้เป็นฐานไดเรกทอรี โลโก้ยังอยู่ในโฟลเดอร์ assets เดิม และเนื้อหา SVG ไม่เปลี่ยน

การใช้ Path(__file__) ยังทำให้ตัวอย่างอิงโฟลเดอร์สคริปต์ แม้สั่งรันจากไดเรกทอรีทำงานอื่น ใน notebook ให้ใช้โฟลเดอร์โครงการที่ระบุชัดแทน เพราะโดยปกติไม่มี __file__

ภาพประกอบของภาพ SVG ในโฟลเดอร์ที่มีลูกศรเชื่อมไปยังภาพเดียวกันบนหน้าเอกสาร
ภาพประกอบการหาตำแหน่งทรัพยากร: เอกสารต้องมีเส้นทางไปยังไฟล์ SVG ก่อนจึงจะวางภาพได้

มีการทดสอบควบคุมอีกแบบที่มีประโยชน์ บันทึก HTML เป็น invoice.html ข้าง render.py แล้วโหลดตัวไฟล์โดยตรง:

HTML(filename=str(root / "invoice.html")).write_pdf(
    root / "from-file.pdf"
)

WeasyPrint อนุมานฐานจากชื่อไฟล์หรือ URL ขาเข้าได้ การทดสอบจากไฟล์ของเราได้กรอบเหมือนกรณีระบุฐานชัดเจน สตริง HTML ไม่มีชื่อไฟล์ที่สอดคล้องกันให้อนุมาน โหมดขาเข้าและกฎการหาตำแหน่งโดยอิงฐานเหล่านี้อธิบายในตัวสร้าง HTML

สำหรับภาพที่อยู่หลังระบบเข้าสู่ระบบ URL ที่ถูกต้องอาจยังไม่พอ เอกสาร URL fetcher ระบุว่าไคลเอนต์ HTTP เริ่มต้นไม่มีคุกกี้หรือการยืนยันตัวตน ใช้วิธีดึงทรัพยากรที่แอปพลิเคชันอนุมัติ และทดสอบในสภาพแวดล้อมที่สร้าง PDF อย่าคิดว่าโลโก้ที่เห็นในเบราว์เซอร์ซึ่งเข้าสู่ระบบแล้วจะเข้าถึงได้จากกระบวนการสร้าง PDF

ตั้งขนาดภาพแยกจากขนาดหน้า PDF

ตัวอย่างใช้ @page สำหรับกระดาษ A4 และระยะขอบ ส่วน .logo ตั้งภาพให้กว้าง 60 มม. อัตราส่วนต้นทาง 2:1 ทำให้สูง 30 มม. เมื่อใช้ height: auto ใน PDF ที่บันทึก กรอบวัดได้ 60 × 30 มม. และเริ่มห่างขอบบนกับซ้ายของหน้า 20 มม.

ภาพนี้เต็ม viewBox ทั้งหมด โลโก้ของคุณอาจมีพื้นที่ว่างภายในกรอบนั้น กล่องภาพกว้าง 60 มม. จึงอาจมีตัวโลโก้ที่ดูแคบกว่า ตรวจพื้นที่วาด SVG ก่อนชดเชยด้วยความกว้าง CSS ที่ใหญ่ขึ้นโดยไม่มีหลักเกณฑ์ คู่มือขนาดนำเข้า SVG อธิบายความต่างนี้เพิ่มเติม

คง write_pdf() ที่ค่าเริ่มต้น zoom=1 เมื่อตรวจขนาดจริง เอกสารอ้างอิงผลลัพธ์ PDF ของ WeasyPrint เตือนว่าการเปลี่ยน zoom จะปรับสเกลหน่วย CSS ทางกายภาพและขนาดหน้าที่มีชื่อด้วย

สิ่งที่เห็นสิ่งที่ตรวจเป็นอันดับแรกขั้นตอนถัดไป
ข้อความทางเลือกแสดงแทนโลโก้URL ภาพและบันทึกทรัพยากรให้ base URL ที่ใช้ได้หรือ URL สัมบูรณ์ที่เข้าถึงได้
โลโก้แสดงขนาดผิดความกว้าง CSS และอัตราส่วนต้นทางตั้งความกว้างด้วยหน่วยจริงและตรวจพื้นที่วาด SVG
ภาพบางส่วนหายขอบเขตต้นทางและคุณสมบัติ SVG ที่เกี่ยวข้องลดเหลือตัวอย่างเล็กที่ยังผิดและเปรียบเทียบ PDF ที่ส่งออก
เรนเดอร์ในเครื่องได้ แต่บนเซิร์ฟเวอร์ไม่ได้ไฟล์ ฟอนต์ และการเข้าถึง URL ที่มีอยู่ทดสอบด้วยทรัพยากรของกระบวนการที่นำขึ้นใช้งาน

ตรวจเนื้อหาและรูปลักษณ์ของ PDF

เอกสารรูปแบบของ WeasyPrint ปัจจุบันระบุว่าภาพ SVG เรนเดอร์เป็นเวกเตอร์ในผลลัพธ์ PDF แต่ไม่ได้หมายความว่าไฟล์ที่ลงท้าย .svg ทุกไฟล์เป็นภาพเวกเตอร์ทั้งหมด เพราะ SVG ฝังบิตแมปได้ ตรวจด้วยคู่มือวินิจฉัยภาพฝัง หากผลลัพธ์ยังดูแตกเป็นพิกเซล

สำหรับตัวอย่างง่าย ๆ นี้ เราตรวจ PDF ที่บันทึกด้วย PyMuPDF 1.26.7:

กรณีขาเข้ารายการวาดภาพฝังกรอบที่มองเห็น
สตริง HTML ไม่มีฐาน00หาย; มีข้อความทางเลือก
สตริง HTML มีโฟลเดอร์ฐาน1060 × 30 มม. ช่องเปิดคงอยู่
HTML โหลดจากชื่อไฟล์10เหมือนกรณีสตริง HTML ที่ระบุฐานถูกต้อง

ผลเหล่านี้ใช้กับกรอบที่ให้ ไม่ใช่คุณสมบัติ SVG ทุกอย่าง พาธประกอบหนึ่งพาธก็ไม่ได้หมายถึงรูปทรงที่เห็นหนึ่งรูปเสมอ เพราะพาธย่อยด้านในกำหนดช่องเปิด

หากต้องการตรวจ resolved.pdf ของคุณ ติดตั้ง pymupdf==1.26.7 แล้วรัน:

import pymupdf
 
with pymupdf.open("resolved.pdf") as document:
    page = document[0]
    print("Drawing records:", len(page.get_drawings()))
    print("Embedded images:", len(page.get_images()))
    for drawing in page.get_drawings():
        box = drawing["rect"]
        print("Bounds in mm:", box.width * 25.4 / 72,
              box.height * 25.4 / 72)

เอกสารอ้างอิง page ของ PyMuPDF อธิบายเมธอดตรวจรายการวาดและภาพ จำนวนเหล่านี้เป็นข้อมูลวินิจฉัย ไม่ใช่ใบรับรองความเที่ยงตรง เปิด PDF ตรวจด้วย: ดูช่องเปิด สี สัดส่วน และตัวอักษรที่ขนาดส่งมอบจริง ตรวจภาพซับซ้อนทีละคุณสมบัติและตรวจซ้ำเมื่ออัปเกรดตัวเรนเดอร์

เมื่อใดควรเตรียม SVG ใหม่ก่อน

หากโลโก้ที่มีอยู่เป็น PNG หรือ JPG เท่านั้น เครื่องมือแปลงโลโก้เป็นเวกเตอร์ ของ PerfectVector เตรียม SVG สำหรับประเมินก่อนวางในเอกสาร HTML ได้ ตรวจเส้นรอบรูป ช่องเปิดเล็ก และตัวอักษรในโปรแกรมแก้ไข แล้วทดสอบ SVG ผ่านกระบวนการ WeasyPrint เดิม คู่มือการแปลงภาพเป็นเวกเตอร์ อธิบายสิ่งที่เปลี่ยนเมื่อพิกเซลกลายเป็นพาธ

เก็บ SVG ต้นฉบับที่แก้ไขได้หากมี แก้การอ้างอิงไฟล์หรือการจัดวางแทนการไล่เส้นใหม่ ภาพถ่ายยังเป็นราสเตอร์ได้ และตัวอักษรแบรนด์ที่ต้องตรงเป๊ะอาจต้องใช้ฟอนต์ต้นฉบับหรือวาดใหม่ด้วยมือ การแปลงเวกเตอร์ไม่แก้ไฟล์เซิร์ฟเวอร์ที่หาย การยืนยันตัวตน หรือคุณสมบัติเรนเดอร์ที่ไม่รองรับ

หากเพียงต้องการแปลง SVG เป็นหน้า PDF ของตัวเอง กระบวนการส่งออก CairoSVG อธิบายงานที่เล็กกว่านั้น WeasyPrint เหมาะกับกรณีเอกสาร HTML ซึ่งภาพอยู่ร่วมหน้ากับเนื้อหาอื่น

คำถามที่พบบ่อย

ทำไม WeasyPrint สร้าง PDF โดยไม่มีโลโก้ SVG? การโหลดภาพล้มเหลวอาจยังปล่อยให้ส่วนอื่นของเอกสารเรนเดอร์ได้ ตรวจบันทึกทรัพยากรและ URL ภาพ ในการทดสอบที่ให้ สตริง HTML ที่มีพาธภาพสัมพัทธ์ต้องมี base URL ระบุชัด

ต้องแปลง SVG เป็น PNG ก่อนหรือไม่? ไม่ WeasyPrint รับภาพ SVG ได้ เก็บภาพเวกเตอร์ที่รองรับเป็น SVG เมื่อเป็นผลลัพธ์ที่ตั้งใจ และตรวจ PDF ที่บันทึก การส่งออกเป็นราสเตอร์เป็นทางเลือกเมื่อจงใจเลือกความตรงของรูปลักษณ์แทนเรขาคณิตเวกเตอร์

base_url เปลี่ยนขนาดโลโก้เมื่อพิมพ์หรือไม่? ไม่ มันให้ฐานสำหรับหาตำแหน่งทรัพยากรจาก URL สัมพัทธ์ ตั้งขนาดการวางภาพด้วย CSS และตรวจขนาดกับ viewBox ของ SVG แยกต่างหาก

แหล่งอ้างอิง

  1. เอกสารอ้างอิง API ของ WeasyPrint — อธิบายขาเข้า HTML, base URL, ผลลัพธ์ SVG และพฤติกรรม zoom ของ PDF
  2. WeasyPrint — ขั้นตอนเริ่มต้น — ครอบคลุมการติดตั้งและการดึงทรัพยากร รวมถึงข้อจำกัดการยืนยันตัวตน
  3. เอกสารอ้างอิง Page ของ PyMuPDF — อธิบายเมธอดตรวจรายการวาดและภาพฝังที่ใช้ในตัวอย่าง

มีเพียงโลโก้ราสเตอร์ใช่ไหม? เตรียม SVG ด้วย PerfectVector ตรวจรูปร่างและตัวอักษร แล้ววางใน HTML และตรวจรูปลักษณ์กับขนาดจริงใน PDF ที่บันทึก

เพิ่มเติมจากบล็อก

เริ่มต้นด้วย SVG ที่เรียบร้อยขึ้น
และแก้ไขได้ง่ายกว่า