Windows · python-docx · win32print

學習筆記排版 +雙面打印
完整小白指南

把課程問卷的問題+答案,用 Python 自動排版成美觀的 Word 文件(剛好 2 頁 A4),
再一鍵發送到 Brother 印表機做雙面打印。
完全免手動,Claude 幫你全搞定。

📋 整個流程一目了然

你只需要:書本問題截圖 + 答案文字,Claude 幫你完成以下全部步驟:

📷 書本截圖
📝 問題文字
📄 Word 文件
📏 確認 2 頁
🖨️ 雙面打印
你要做的Claude 做的
拍書本截圖,貼給 Claude讀圖,提取問題文字
確認問題和答案正確用 python-docx 生成排版好的 .docx
等印表機出紙確認頁數 → 設雙面 → 發送打印
✅ 這個指南的實際案例

基督生平第二冊第四單元課後複習:測驗 4A / 4B / 4C,共 16 題,最終排版成剛好 2 頁 A4,雙面打印到 Brother MFC-L2770DW。

🔧 前置準備

確認以下套件已安裝在 D:\Backup\Downloads\.venv\

Terminal — 安裝套件
source /d/Backup/Downloads/.venv/Scripts/activate
pip install python-docx pywin32

確認印表機名稱(一次就夠,記住名字):

Python — 查印表機名稱
PYTHONUTF8=1 py -c "
import win32print
for p in win32print.EnumPrinters(
        win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS):
    print(p[2])
"

輸出範例:Brother MFC-L2770DW series Printer — 記住這個名字,後面會用到。

📄 用 Python 建立排版好的 Word 文件

核心套件是 python-docx。以下是排版規格和對應代碼:

排版設定代碼
紙張A4section.page_height = Cm(29.7)
上下邊距2.2 cmsection.top_margin = Cm(2.2)
左右邊距2.5 cmsection.left_margin = Cm(2.5)
正文字體11pt,行距 15ptrun.font.size = Pt(11)
問題11pt 斜體run.font.italic = True
答案11pt 粗體,藍色run.font.color.rgb = RGBColor(0x1F,0x49,0x7D)
Python — 基本結構模板
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

doc = Document()
BLUE = RGBColor(0x1F, 0x49, 0x7D)

# ① 設 A4 邊距
for s in doc.sections:
    s.page_height = Cm(29.7)
    s.page_width  = Cm(21.0)
    s.top_margin    = Cm(2.2)
    s.bottom_margin = Cm(2.2)
    s.left_margin   = Cm(2.5)
    s.right_margin  = Cm(2.5)

# ② 加標題
p = doc.add_paragraph()
p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run('課程名稱 — 第X單元複習 2026-XX-XX')
r.font.size = Pt(14); r.font.bold = True

# ③ 加區塊標題(帶底線)
def section_header(title):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(10)
    run = para.add_run(title)
    run.font.size = Pt(12); run.font.bold = True
    run.font.color.rgb = BLUE
    # 加底線
    pPr = para._p.get_or_add_pPr()
    pBdr = OxmlElement('w:pBdr')
    bottom = OxmlElement('w:bottom')
    bottom.set(qn('w:val'), 'single')
    bottom.set(qn('w:sz'), '4')
    bottom.set(qn('w:color'), '1F497D')
    pBdr.append(bottom); pPr.append(pBdr)

# ④ 加問題+答案
def qa(q_text, a_text):
    # 問題(斜體)
    pq = doc.add_paragraph()
    pq.paragraph_format.space_before = Pt(5)
    rq = pq.add_run(q_text)
    rq.font.size = Pt(11); rq.font.italic = True
    # 答案(粗體藍色,縮排)
    pa = doc.add_paragraph()
    pa.paragraph_format.left_indent = Cm(0.6)
    ra = pa.add_run(a_text)
    ra.font.size = Pt(11); ra.font.bold = True
    ra.font.color.rgb = BLUE

# ⑤ 使用
section_header('測驗 4A — 耶穌的受洗')
qa('Q1. 在耶穌受洗時,祂大約多少歲?', 'a) 祂的受洗 b) 大約 30 歲(4A.2b)')

# ⑥ 儲存
doc.save('D:/tmp/學習筆記.docx')
print('完成!')
💡 填空題和選擇題的格式

填空題:問題用「___」表示空白,答案用「」括起來
qa('填寫申6:4:"___ 啊,你要聽!"', '「以色列」啊,你要聽!')

選擇題:選項用 □ 開頭,答案行寫「答:x, y」

📏 用 win32com 確認剛好 2 頁

存完 .docx 後,用以下代碼確認頁數。如果不是 2 頁,調整字體大小再重試。

Python — 確認頁數
import win32com.client, os

path = os.path.abspath('D:/tmp/學習筆記.docx')
word = win32com.client.Dispatch('Word.Application')
word.Visible = False
doc = word.Documents.Open(path)

pages = doc.ComputeStatistics(2)   # 2 = wdStatisticPages
print(f'頁數: {pages}')

doc.Close(False)
word.Quit()
結果調整方法
pages = 1把字體從 11pt 改 12pt,或把 space_before 從 5pt 改 8pt
pages = 2 ✅完美,直接打印
pages = 3把字體從 11pt 改 10pt,或把邊距從 2.5 改 2.0

🖨️ 雙面打印到 Brother(正確方法)

🚨 新手必踩的坑:win32com PrintOut 不支持 Duplex 參數!

以下寫法一定會報錯(TypeError: unexpected keyword argument 'Duplex'):
doc.PrintOut(Duplex=True)
doc.PrintOut(Duplex=1)
doc.PrintOut(Printer='Brother...')

正確方法是:先用 win32print 修改 DevMode,再調用 PrintOut。

正確的三步流程:

1
設雙面(修改 DevMode)
Duplex 值:1 = 關閉,2 = 長邊翻頁(標準雙面),3 = 短邊翻頁
2
設為默認印表機 + 打印
PrintOut 的參數全用位置參數(不用關鍵字),避免 TypeError
3
恢復原始設定
把 Duplex 改回 1,把默認印表機改回來,不影響其他打印任務
Python — 完整雙面打印代碼
import win32print, win32com.client, os

PRINTER = 'Brother MFC-L2770DW series Printer'
PATH = os.path.abspath('D:/tmp/學習筆記.docx')

# ── Step 1:設雙面 ─────────────────────────────
h = win32print.OpenPrinter(PRINTER,
        {'DesiredAccess': win32print.PRINTER_ALL_ACCESS})
s = win32print.GetPrinter(h, 2)
s['pDevMode'].Duplex = 2        # 2 = 長邊翻頁(標準雙面)
win32print.SetPrinter(h, 2, s, 0)
win32print.ClosePrinter(h)

# ── Step 2:打印 ───────────────────────────────
old = win32print.GetDefaultPrinter()
win32print.SetDefaultPrinter(PRINTER)

word = win32com.client.Dispatch('Word.Application')
word.Visible = False
doc = word.Documents.Open(PATH)
doc.PrintOut(False, False, 0, '', '', '', 0, 1)   # 全部位置參數!
doc.Close(False)
word.Quit()

# ── Step 3:恢復 ───────────────────────────────
h2 = win32print.OpenPrinter(PRINTER,
        {'DesiredAccess': win32print.PRINTER_ALL_ACCESS})
s2 = win32print.GetPrinter(h2, 2)
s2['pDevMode'].Duplex = 1       # 改回關閉
win32print.SetPrinter(h2, 2, s2, 0)
win32print.ClosePrinter(h2)
win32print.SetDefaultPrinter(old)

print('已發送到 Brother 印表機 ✅')
✅ 確認打印成功

看到「已發送到 Brother 印表機 ✅」後,走去印表機等出紙。Brother 會自動翻面,只需放入一張 A4 紙(正反面自動打印)。

🚀 完整整合腳本(存成 .py 文件用)

把以上所有步驟整合成一個完整腳本。每次要做新課程時,只需改 問題和答案部分,其餘不用動。

儲存到 D:/tmp/build_study_notes.py,然後執行:

Terminal
PYTHONUTF8=1 py D:/tmp/build_study_notes.py
D:/tmp/build_study_notes.py — 完整模板
# -*- coding: utf-8 -*-
# ══════════════════════════════════════════════
#  學習筆記排版 + 雙面打印模板
#  使用方法:修改 「問題和答案」 區塊,其餘不動
# ══════════════════════════════════════════════
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import win32print, win32com.client, os

# ── 設定 ──────────────────────────────────────
OUTPUT = 'D:/tmp/學習筆記.docx'   # 輸出路徑
PRINTER = 'Brother MFC-L2770DW series Printer'
BLUE = RGBColor(0x1F, 0x49, 0x7D)
GREY = RGBColor(0x55, 0x55, 0x55)

# ── 建立文件 ──────────────────────────────────
doc = Document()
for s in doc.sections:
    s.page_height = Cm(29.7); s.page_width = Cm(21.0)
    s.top_margin = Cm(2.2);   s.bottom_margin = Cm(2.2)
    s.left_margin = Cm(2.5);  s.right_margin = Cm(2.5)

def section_header(title):
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(10)
    p.paragraph_format.space_after = Pt(3)
    p.paragraph_format.line_spacing = Pt(16)
    r = p.add_run(title); r.font.size = Pt(12)
    r.font.bold = True; r.font.color.rgb = BLUE
    pPr = p._p.get_or_add_pPr()
    pBdr = OxmlElement('w:pBdr')
    b = OxmlElement('w:bottom')
    b.set(qn('w:val'), 'single'); b.set(qn('w:sz'), '4')
    b.set(qn('w:color'), '1F497D')
    pBdr.append(b); pPr.append(pBdr)

def qa(q_text, a_text):
    pq = doc.add_paragraph()
    pq.paragraph_format.space_before = Pt(5)
    pq.paragraph_format.space_after = Pt(1)
    pq.paragraph_format.line_spacing = Pt(15)
    rq = pq.add_run(q_text)
    rq.font.size = Pt(11); rq.font.italic = True
    pa = doc.add_paragraph()
    pa.paragraph_format.space_before = Pt(0)
    pa.paragraph_format.space_after = Pt(2)
    pa.paragraph_format.line_spacing = Pt(15)
    pa.paragraph_format.left_indent = Cm(0.6)
    ra = pa.add_run(a_text)
    ra.font.size = Pt(11); ra.font.bold = True
    ra.font.color.rgb = BLUE

# ══════════════════════════════════════════════
#  ↓↓↓ 修改這裡:標題、問題、答案 ↓↓↓
# ══════════════════════════════════════════════

# 標題
pt = doc.add_paragraph()
pt.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
pt.paragraph_format.space_after = Pt(6)
r1 = pt.add_run('課程名稱 — 第X單元複習')
r1.font.size = Pt(14); r1.font.bold = True
r2 = pt.add_run('  2026-XX-XX')
r2.font.size = Pt(10); r2.font.color.rgb = GREY

# 測驗 A
section_header('測驗 A — 主題名稱')
qa('Q1. 問題文字...', 'a) 答案一 b) 答案二(參考頁碼)')
qa('Q2. 填空:"___"', '「填入答案」')

# 測驗 B
section_header('測驗 B — 主題名稱')
qa('Q1. 問題...', '答案(參考頁碼)')

# ══════════════════════════════════════════════
#  ↑↑↑ 修改到這裡為止 ↑↑↑
# ══════════════════════════════════════════════

# 頁腳
pf = doc.add_paragraph()
pf.paragraph_format.space_before = Pt(10)
pf.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
rf = pf.add_run('★ = 課程重點必背 | 括號內為教材參考頁碼')
rf.font.size = Pt(9); rf.font.color.rgb = GREY

doc.save(OUTPUT)
print(f'✅ 文件已儲存:{OUTPUT}')

# ── 確認頁數 ──────────────────────────────────
word = win32com.client.Dispatch('Word.Application')
word.Visible = False
d = word.Documents.Open(os.path.abspath(OUTPUT))
pages = d.ComputeStatistics(2)
print(f'📄 頁數:{pages}')
d.Close(False); word.Quit()

if pages != 2:
    print(f'⚠️  不是 2 頁!請調整字體大小再重跑。')
    exit(1)

# ── 雙面打印 ──────────────────────────────────
h = win32print.OpenPrinter(PRINTER, {'DesiredAccess': win32print.PRINTER_ALL_ACCESS})
s = win32print.GetPrinter(h, 2)
s['pDevMode'].Duplex = 2
win32print.SetPrinter(h, 2, s, 0)
win32print.ClosePrinter(h)

old = win32print.GetDefaultPrinter()
win32print.SetDefaultPrinter(PRINTER)
word2 = win32com.client.Dispatch('Word.Application')
word2.Visible = False
d2 = word2.Documents.Open(os.path.abspath(OUTPUT))
d2.PrintOut(False, False, 0, '', '', '', 0, 1)
d2.Close(False); word2.Quit()

h2 = win32print.OpenPrinter(PRINTER, {'DesiredAccess': win32print.PRINTER_ALL_ACCESS})
s2 = win32print.GetPrinter(h2, 2)
s2['pDevMode'].Duplex = 1
win32print.SetPrinter(h2, 2, s2, 0)
win32print.ClosePrinter(h2)
win32print.SetDefaultPrinter(old)
print(f'🖨️  已發送到 {PRINTER}')

⚡ 做成 Claude 技能:/print-study-notes

把這個流程保存為 Claude 技能,下次只需一句話就能啟動:

你說的話(觸發技能)
繼續基督生平第五單元任務
(附上書本問題截圖)

技能文件位置:D:\Backup\Downloads\.claude\commands\print-study-notes.md

1
截圖書本問題 → 直接貼給 Claude(Ctrl+V)
Claude 會讀取圖片中的問題文字
2
Claude 生成 Python 腳本並執行
自動把問題+答案排版成 Word,確認 2 頁
3
Claude 發送雙面打印
走去印表機取紙,一張 A4 兩面印好
📌 記憶:每次課程的文件路徑

Claude 有持久記憶,會記住每個單元的文件路徑和已完成狀態。
只需說「繼續第X單元任務」,Claude 就會找到對應文件繼續工作。