设计实践

用 Canva 设计双面名片,按印刷厂要求导出带出血和裁切标记的 PDF,再用 Python 脚本做一次“交付前预检”

用 Canva 设计双面名片,按印刷厂要求导出带出血和裁切标记的 PDF,再用 Python 脚本做一次“交付前预检”:尺寸是否包含出血、字体是否嵌入、页数是否正确,避免送印后才发现白边或字体被替换。

BINBHO · 图文教程

用 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 里设计?

  1. 创建设计 → 自定义尺寸:90×54 毫米。
  2. 菜单 文件 → 设置 → 显示打印出血。
  3. 背景色和图片要一直延伸到出血区域的最外边。
  4. 文字和标志放在安全区内(离裁切线至少 3 毫米)。
  5. 加第二页做背面。

Canva 帮助中心说明:需要裁切的产品,每边出血固定为 0.125 英寸(3.175 毫米),大小不能修改。如果印刷厂要求的出血不同,以印刷厂为准。

所以带出血的 PDF 页面尺寸应为 90+6.35 = 96.35 毫米 × 54+6.35 = 60.35 毫米。

第二步:怎样导出印刷 PDF?

  1. 共享 → 下载。
  2. 文件类型选 PDF 打印。
  3. 勾选 裁切标记和出血。
  4. 有付费计划且印刷厂要求时,色彩模式选 CMYK。
  5. 下载。

第三步:怎样预检?

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 跑预检,再让印刷厂打一张样确认颜色和裁切。

确认无误后再大批量印刷,并把这个预检脚本用在以后的每一份印刷文件上。