用 Canva 设计双面名片,按印刷厂要求导出带出血和裁切标记的 PDF,再用 Python 脚本做一次“交付前预检”:尺寸是否包含出血、字体是否嵌入、页数是否正确,避免送印后才发现白边或字体被替换。
先看清成品
成品是一份可以直接交给印刷厂的
名片.pdf。本文附带的preflight.py已用 pypdf 实际测试,3 个测试全部通过:带出血、字体已嵌入的 2 页名片通过;没有出血、字体没嵌入、页数不对的文件,三个问题都能找出来;横竖方向都能识别。本文环境没有登录 Canva,没有用真实的 Canva 导出文件测试,也没有实际送印。
说明:中国大陆用户使用的是 Canva 可画(canva.cn),账号与国际版不通用,部分功能和素材可能不同。本文按国际版帮助中心编写,菜单名称以你看到的界面为准。
注意:只使用你有权使用的字体、照片和标志。Canva 模板和素材的商用范围以 Canva 许可协议为准;不要把他人的商标、明星照片用于商业宣传。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
| 双面名片设计 | 90×54 毫米(常见尺寸,以印刷厂为准) |
| 印刷 PDF | PDF 打印格式,带出血和裁切标记 |
| 预检脚本 | 尺寸、字体、页数三项检查 |
需要准备哪些环境?
| 项目 | 要求 | 说明 |
|---|---|---|
| Canva 账号 | 免费可用 | CMYK 色彩模式需要付费计划(以帮助中心为准) |
| Python | 3.8+ + pip install pypdf |
本文使用 pypdf 6.21.1 |
| 印刷厂要求 | 成品尺寸、出血、色彩模式 | 先问清楚 |
第一步:怎样在 Canva 里设计?
- 创建设计 → 自定义尺寸:90×54 毫米。
- 菜单 文件 → 设置 → 显示打印出血。
- 背景色和图片要一直延伸到出血区域的最外边。
- 文字和标志放在安全区内(离裁切线至少 3 毫米)。
- 加第二页做背面。
Canva 帮助中心说明:需要裁切的产品,每边出血固定为 0.125 英寸(3.175 毫米),大小不能修改。如果印刷厂要求的出血不同,以印刷厂为准。
所以带出血的 PDF 页面尺寸应为 90+6.35 = 96.35 毫米 × 54+6.35 = 60.35 毫米。
第二步:怎样导出印刷 PDF?
- 共享 → 下载。
- 文件类型选 PDF 打印。
- 勾选 裁切标记和出血。
- 有付费计划且印刷厂要求时,色彩模式选 CMYK。
- 下载。
第三步:怎样预检?
1. preflight.py
"""印刷文件预检:检查 Canva 导出的 PDF 尺寸(含出血)、字体是否嵌入、页数。
Canva 帮助中心:需要裁切的产品,出血为每边 0.125 英寸(3.175 毫米)。
用法:python preflight.py 名片.pdf --trim 90x54 [--pages 2] [--no-bleed]
"""
import argparse
import sys
from pypdf import PdfReader
PT_PER_MM = 72 / 25.4
BLEED_MM = 3.175
TOL_MM = 0.6 # 允许的误差
def fonts_on_page(page):
"""返回 (字体名, 是否嵌入) 列表,包括 Type0 的子字体。"""
out = []
res = page.get("/Resources") or {}
fonts = res.get("/Font") or {}
for ref in fonts.values():
f = ref.get_object()
targets = [d.get_object() for d in f.get("/DescendantFonts", [])] or [f]
for t in targets:
desc = t.get("/FontDescriptor")
desc = desc.get_object() if desc else {}
embedded = any(k in desc for k in ("/FontFile", "/FontFile2", "/FontFile3"))
out.append((str(t.get("/BaseFont", f.get("/BaseFont"))), embedded))
return out
def check(path, trim, pages=None, bleed=True):
tw, th = trim
expect = (tw + 2 * BLEED_MM, th + 2 * BLEED_MM) if bleed else (tw, th)
r = PdfReader(path)
problems, notes = [], []
if pages and len(r.pages) != pages:
problems.append(f"页数 {len(r.pages)},应为 {pages}")
for i, p in enumerate(r.pages, 1):
w, h = float(p.mediabox.width) / PT_PER_MM, float(p.mediabox.height) / PT_PER_MM
ok = any(abs(w - a) <= TOL_MM and abs(h - b) <= TOL_MM for a, b in (expect, expect[::-1]))
msg = f"第 {i} 页尺寸 {w:.1f}×{h:.1f} mm"
if ok:
notes.append(msg + " ✓")
else:
problems.append(msg + f",应为 {expect[0]:.1f}×{expect[1]:.1f} mm" + ("(含出血)" if bleed else ""))
for name, emb in fonts_on_page(p):
if not emb:
problems.append(f"第 {i} 页字体 {name} 未嵌入,印刷厂可能替换字体")
return problems, notes
def main(argv=None):
ap = argparse.ArgumentParser()
ap.add_argument("pdf")
ap.add_argument("--trim", required=True, help="成品尺寸,毫米,例如 90x54")
ap.add_argument("--pages", type=int)
ap.add_argument("--no-bleed", action="store_true")
a = ap.parse_args(argv)
trim = tuple(float(x) for x in a.trim.lower().split("x"))
problems, notes = check(a.pdf, trim, a.pages, not a.no_bleed)
for n in notes:
print(n)
for p in problems:
print("✗ " + p)
print("预检通过" if not problems else f"发现 {len(problems)} 个问题")
return 0 if not problems else 1
if __name__ == "__main__":
sys.exit(main())
| 检查项 | 原因 |
|---|---|
| 尺寸 = 成品 + 2×3.175 毫米 | 没有出血,裁切后容易有白边 |
| 字体嵌入 | 没嵌入的字体在印刷厂电脑上会被替换 |
| 页数 | 双面名片应为 2 页 |
2. 运行
pip install pypdf
python preflight.py 名片.pdf --trim 90x54 --pages 2
合格文件的期望输出:
第 1 页尺寸 96.3×60.4 mm ✓
第 2 页尺寸 96.3×60.4 mm ✓
预检通过
本文对一份故意做错的文件实际运行的输出:
✗ 页数 1,应为 2
✗ 第 1 页尺寸 90.0×54.0 mm,应为 96.3×60.4 mm(含出血)
✗ 第 1 页字体 /Helvetica 未嵌入,印刷厂可能替换字体
发现 3 个问题
注意:勾选“裁切标记”后,有的导出文件会在出血外再加一圈放裁切标记的空白,页面尺寸会更大。如果预检报尺寸不符,用 PDF 阅读器看看页面边缘,确认是裁切标记造成的,并按印刷厂要求选择是否带裁切标记。
3. 测试
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas
from preflight import PT_PER_MM, check
FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
def make(path, w_mm, h_mm, pages=2, embed=True):
if embed:
pdfmetrics.registerFont(TTFont("DejaVu", FONT))
c = canvas.Canvas(str(path), pagesize=(w_mm * PT_PER_MM, h_mm * PT_PER_MM),
initialFontName="DejaVu" if embed else "Helvetica")
for _ in range(pages):
c.setFont("DejaVu" if embed else "Helvetica", 10)
c.drawString(20, 20, "Zhang Wei")
c.showPage()
c.save()
def test_good_card(tmp_path):
p = tmp_path / "ok.pdf"
make(p, 90 + 6.35, 54 + 6.35)
problems, notes = check(str(p), (90, 54), pages=2)
assert problems == [] and len(notes) == 2
def test_missing_bleed_and_font(tmp_path):
p = tmp_path / "bad.pdf"
make(p, 90, 54, pages=1, embed=False)
problems, _ = check(str(p), (90, 54), pages=2)
assert any("页数 1" in x for x in problems)
assert any("应为 96.3×60.4" in x for x in problems)
assert any("Helvetica 未嵌入" in x for x in problems)
def test_landscape_accepted(tmp_path):
p = tmp_path / "rot.pdf"
make(p, 54 + 6.35, 90 + 6.35, pages=1)
assert check(str(p), (90, 54))[0] == []
pip install reportlab pytest
python -m pytest -q
本文实际输出 3 passed。
出问题时该怎么调?
1. 报尺寸不对
| 实际尺寸 | 原因 |
|---|---|
| 正好是成品尺寸 | 没勾选出血 |
| 比预期大一圈 | 带了裁切标记的空白区 |
| 宽高对调 | 横竖方向不同,脚本已兼容 |
2. 报字体未嵌入
换一种字体重新导出,或询问印刷厂能否接受。
3. 背景没铺满出血
回到 Canva 打开“显示打印出血”,把背景拉到最外圈。
常见问题怎么处理?
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 印出来颜色发灰 | 屏幕 RGB 和印刷 CMYK 不同 | 用 CMYK 导出并要求打样 |
| 字被裁掉 | 离裁切线太近 | 留出安全距离 |
| 印刷厂要 5 毫米出血 | Canva 出血固定 | 问印刷厂是否接受 3 毫米,或换专业排版软件 |
| 二维码扫不出 | 太小或对比度低 | 至少约 2 厘米,深色码浅色底 |
| 不知道纸张选什么 | 与设计无关 | 问印刷厂要样品 |
项目资源
| 用途 | 地址 |
|---|---|
| 边距、出血和裁切标记 | https://www.canva.com/help/margins-bleed-crop-marks/ |
| pypdf 文档 | https://pypdf.readthedocs.io/ |
| Canva 价格和计划对比 | https://www.canva.com/pricing/ |
| Canva 可画(中国大陆) | https://www.canva.cn/ |
建议先导出一份 PDF 跑预检,再让印刷厂打一张样确认颜色和裁切。
确认无误后再大批量印刷,并把这个预检脚本用在以后的每一份印刷文件上。