210 lines
12 KiB
Python
210 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
完整版:每日英语听力材料 PDF 生成脚本(学习用)
|
||
|
||
本版相对精简版的变化:
|
||
- 填入今天(2026-10-01 国庆主题)完整的 10 句材料
|
||
- 注释更细,每个环节说明"为什么这么做"
|
||
- 加入"中文翻译 + 听力技巧 + 页脚"完整结构
|
||
|
||
依赖:pip install reportlab
|
||
系统字体(以 Ubuntu/Debian 为例):
|
||
- DejaVu Sans:英文 + IPA 音标字符
|
||
- 文鼎 UMingCN(/usr/share/fonts/truetype/arphic/uming.ttc):中文字符
|
||
"""
|
||
import html as h
|
||
|
||
from reportlab.lib import colors
|
||
from reportlab.lib.pagesizes import A4
|
||
from reportlab.lib.styles import ParagraphStyle
|
||
from reportlab.lib.units import mm
|
||
from reportlab.pdfbase import pdfmetrics
|
||
from reportlab.pdfbase.ttfonts import TTFont
|
||
from reportlab.platypus import (Paragraph, SimpleDocTemplate, Spacer,
|
||
Table, TableStyle)
|
||
|
||
# =====================================================================
|
||
# 第 1 步:注册字体
|
||
# ---------------------------------------------------------------------
|
||
# reportlab 内置的 Helvetica/Times 等字体只有拉丁字符,
|
||
# 中文和 IPA 音标(/ˈtə/ 这类符号)必须加载系统字体。
|
||
# 注意:reportlab 只支持 TrueType 轮廓,不支持 CFF(OTF),
|
||
# 所以 NotoSansCJK.otf 用不了,中文选了文鼎 TrueType。
|
||
# =====================================================================
|
||
BASE = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
|
||
BASE_BOLD = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
|
||
CJK = '/usr/share/fonts/truetype/arphic/uming.ttc'
|
||
|
||
# 注册英文/IPA 字体(DejaVu 覆盖音标符号)
|
||
pdfmetrics.registerFont(TTFont('Base', BASE))
|
||
pdfmetrics.registerFont(TTFont('Base-Bold', BASE_BOLD))
|
||
# 中文用 .ttc 集合字体,subfontIndex=0 表示取第 0 个子字体(UMingCN)
|
||
pdfmetrics.registerFont(TTFont('CJK', CJK, subfontIndex=0))
|
||
|
||
|
||
def esc(s: str) -> str:
|
||
"""转义 < > & 等字符。Paragraph 内容按 XML 解析,不转义会把内容当标签。"""
|
||
return h.escape(s, quote=False)
|
||
|
||
|
||
# =====================================================================
|
||
# 第 2 步:定义排版样式(ParagraphStyle)
|
||
# ---------------------------------------------------------------------
|
||
# fontName 指定字体;leading 是行高;spaceBefore/After 是段前后间距;
|
||
# HexColor 定义颜色('#8B0000' 是深红,'#2F5D8A' 是深蓝)。
|
||
# =====================================================================
|
||
title = ParagraphStyle('title', fontName='Base-Bold', fontSize=18, leading=24,
|
||
textColor=colors.HexColor('#B22222'), alignment=1, spaceAfter=6)
|
||
meta = ParagraphStyle('meta', fontName='CJK', fontSize=10, leading=14,
|
||
textColor=colors.HexColor('#666666'), alignment=1, spaceAfter=10)
|
||
h1 = ParagraphStyle('h1', fontName='CJK', fontSize=13, leading=18,
|
||
textColor=colors.HexColor('#8B0000'), spaceBefore=10, spaceAfter=6)
|
||
en = ParagraphStyle('en', fontName='Base', fontSize=11.5, leading=17, spaceBefore=4)
|
||
ipa = ParagraphStyle('ipa', fontName='Base', fontSize=9.5, leading=14,
|
||
textColor=colors.HexColor('#555555'), leftIndent=6, spaceAfter=2)
|
||
note = ParagraphStyle('note', fontName='CJK', fontSize=9.5, leading=14,
|
||
textColor=colors.HexColor('#2F5D8A'), leftIndent=12, spaceAfter=4)
|
||
zh = ParagraphStyle('zh', fontName='CJK', fontSize=11, leading=18, spaceBefore=3)
|
||
tip = ParagraphStyle('tip', fontName='CJK', fontSize=10.5, leading=16, spaceBefore=2)
|
||
foot = ParagraphStyle('foot', fontName='CJK', fontSize=9, leading=13,
|
||
textColor=colors.HexColor('#999999'))
|
||
|
||
|
||
def sen(num: str, text: str):
|
||
"""句子行:编号用粗体(Base-Bold),正文用普通字体(Base)。"""
|
||
return Paragraph('<font name="Base-Bold">%s</font> %s' % (esc(num), esc(text)), en)
|
||
|
||
|
||
# =====================================================================
|
||
# 第 3 步:准备内容数据(今天的 10 句国庆主题)
|
||
# =====================================================================
|
||
sentences = [
|
||
# (编号, 英文原文, IPA 音标, 发音要点中文说明)
|
||
("1.", "Today is October 1st — China's National Day.",
|
||
"/təˈdeɪ ɪz oʊkˈtoʊbər fɜːrst | ˈtʃaɪnəz ˈnæʃənəl deɪ/",
|
||
"● 连读:October 1st 中 /r/ 与 /f/ 自然衔接。"),
|
||
("2.", "Streets are decorated with red flags, and cities glow with lanterns and flowers.",
|
||
"/striːts ər ˈdekəreɪtɪd wɪð red flægz | ənd ˈsɪtiz gloʊ wɪð ˈlæntərnz ənd ˈflaʊərz/",
|
||
"● 重读:decorated、glow 是画面词,加重读出画面感。"),
|
||
("3.", "For many, this is the start of a seven-day holiday filled with joy.",
|
||
"/fɔːr ˈmeni | ðɪs ɪz ðə stɑːrt əv ə ˈsevən deɪ ˈhɑːlədeɪ fɪld wɪð dʒɔɪ/",
|
||
"● 弱读:of a 读 /əv ə/,轻快不拖。"),
|
||
("4.", "Families gather for meals and laughter.",
|
||
"/ˈfæməliz ˈgæðər fɔːr miːlz ənd ˈlæftər/",
|
||
"● 重读:gather、laughter 读出团聚的温馨。"),
|
||
("5.", "Some people travel to famous mountains and rivers, while others enjoy quiet time at home.",
|
||
"/sʌm ˈpiːpəl ˈtrævəl tuː ˈfeɪməs ˈmaʊntənz ənd ˈrɪvərz | waɪl ˈʌðərz ɪnˈdʒɔɪ ˈkwaɪət taɪm æt hoʊm/",
|
||
"● 对比:some...while others... 前后语调一扬一落。"),
|
||
("6.", "In the evening, the night sky comes alive with fireworks, painting the darkness with color.",
|
||
"/ɪn ði ˈiːvnɪŋ | ðə naɪt skaɪ kʌmz əˈlaɪv wɪð ˈfaɪrwɜːrks | ˈpeɪntɪŋ ðə ˈdɑːrknəs wɪð ˈkʌlər/",
|
||
"● 连读:comes alive → /kʌmz əˈlaɪv/,t 弱化带过。"),
|
||
("7.", "On this day, we also take a moment to look back at how far the country has come.",
|
||
"/ɑːn ðɪs deɪ | wi ɔːlsoʊ teɪk ə ˈmoʊmənt tə lʊk bæk æt haʊ fɑːr ðə ˈkʌntri hæz kʌm/",
|
||
"● 重读:how far 是本句情感重心,放慢强调。"),
|
||
("8.", "From ancient civilization to modern achievement, the story continues every day, written by ordinary people like you and me.",
|
||
"/frəm ˈeɪnʃənt ˌsɪvələˈzeɪʃən tuː ˈmɑːdərn əˈtʃiːvmənt | ðə ˈstɔːri kənˈtɪnjuːz ˈevri deɪ | ˈrɪtən baɪ ˈɔːrdəneri ˈpiːpəl laɪk juː ənd miː/",
|
||
"● 节奏:长句用逗号自然换气,ordinary people 放轻读出温暖感。"),
|
||
("9.", "So whatever you do today — travel, rest, or celebrate — remember to enjoy the moment.",
|
||
"/soʊ wətˈevər ju duː təˈdeɪ | ˈtrævəl | rest | ɔːr ˈseləbreɪt | rɪˈmembər tə ɪnˈdʒɔɪ ðə ˈmoʊmənt/",
|
||
"● 并列:travel / rest / celebrate 三个词逐个重读,节奏感十足。"),
|
||
("10.", "Wishing you a wonderful National Day, full of happiness and good memories!",
|
||
"/ˈwɪʃɪŋ ju ə ˈwʌndərfəl ˈnæʃənəl deɪ | fʊl əv ˈhæpinəs ənd gʊd ˈmeməriz/",
|
||
"● 语气:收尾上扬,读出祝福的温度。"),
|
||
]
|
||
|
||
translation = ("今天是 10 月 1 日——中国的国庆节。街道装点着红旗,城市里灯笼与鲜花交相辉映。对许多人来说,"
|
||
"这是充满欢乐的七天假期的开始。\n\n"
|
||
"家人团聚,共享美食与欢笑。有人去游览名山大川,有人在家享受悠闲时光。到了晚上,夜空被烟花点亮,"
|
||
"为黑暗染上绚丽的色彩。\n\n"
|
||
"在这个日子里,我们也停下来回望国家走过的路。从古老的文明到现代的成就,故事每天都在继续,"
|
||
"由你我这样的普通人书写着。\n\n"
|
||
"所以无论你今天做什么——旅行、休息,还是庆祝——记得享受当下。祝你国庆节快乐,幸福满满,"
|
||
"留下美好的回忆!")
|
||
|
||
words = [
|
||
# (单词, 音标, 中文释义)
|
||
("decorate", "/ˈdekəreɪt/", "装饰"),
|
||
("glow", "/gloʊ/", "发光,焕发光彩"),
|
||
("lantern", "/ˈlæntərn/", "灯笼"),
|
||
("fireworks", "/ˈfaɪrwɜːrks/", "烟花"),
|
||
("come alive", "—", "热闹起来,焕发生机"),
|
||
("look back", "—", "回顾"),
|
||
("achievement", "/əˈtʃiːvmənt/", "成就"),
|
||
("ordinary", "/ˈɔːrdəneri/", "普通的,平凡的"),
|
||
]
|
||
|
||
tips = [
|
||
"1. 早餐时 / 通勤路上:把音频当背景声循环放 2–3 遍,不求听懂每个词,只抓熟悉的词和整体语气;",
|
||
"2. 散步时:听到认识的词(如 fireworks、family)心里默念一遍;",
|
||
"3. 睡前:再精听一遍今天带音标的句子,跟着读两句。",
|
||
"泛听养“语感”,精听抠“细节”,两者搭配,假期结束你会发现自己耳朵更灵了。",
|
||
]
|
||
|
||
# =====================================================================
|
||
# 第 4 步:组装文档内容(story 是一个"流",元素按顺序排列并自动分页)
|
||
# =====================================================================
|
||
story = []
|
||
|
||
# 标题:一行里混排中文和英文,用 <font> 标签分别指定字体
|
||
story.append(Paragraph('<font name="Base-Bold">Happy National Day!</font>'
|
||
'<font name="CJK">(国庆节快乐)</font>', title))
|
||
story.append(Paragraph('每日英语听力推送 · 2026-10-01(周四)· 国庆主题 · 约1分钟朗读量', meta))
|
||
story.append(Spacer(1, 4))
|
||
|
||
# 一、英文原文(10 句,每句 = 句子 + 音标 + 要点)
|
||
story.append(Paragraph('一、英文原文(逐句 + IPA 音标)', h1))
|
||
for num, text, ip, n in sentences:
|
||
story.append(sen(num, text))
|
||
story.append(Paragraph(esc(ip), ipa))
|
||
story.append(Paragraph(esc(n), note))
|
||
|
||
# 二、中文翻译(<br/> 实现段内换行)
|
||
story.append(Paragraph('二、中文翻译', h1))
|
||
story.append(Paragraph('<br/>'.join(esc(t) for t in translation.split('\n\n')), zh))
|
||
|
||
# 三、生词与短语(用 Table 表格呈现,更清晰)
|
||
story.append(Paragraph('三、生词与短语', h1))
|
||
cell_en = ParagraphStyle('c1', fontName='Base', fontSize=10, leading=13)
|
||
cell_ipa = ParagraphStyle('c2', fontName='Base', fontSize=10, leading=13,
|
||
textColor=colors.HexColor('#555555'))
|
||
cell_zh = ParagraphStyle('c3', fontName='CJK', fontSize=10, leading=13)
|
||
head = ParagraphStyle('ch', fontName='CJK', fontSize=10, leading=13, textColor=colors.white)
|
||
data = [[Paragraph('单词/短语', head), Paragraph('音标', head), Paragraph('释义', head)]]
|
||
for w, ip, z in words:
|
||
data.append([Paragraph(esc(w), cell_en),
|
||
Paragraph(esc(ip), cell_ipa),
|
||
Paragraph(esc(z), cell_zh)])
|
||
tbl = Table(data, colWidths=[52*mm, 58*mm, 62*mm])
|
||
tbl.setStyle(TableStyle([
|
||
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#8B0000')), # 表头深红底
|
||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#F5F0E6')]), # 斑马纹
|
||
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#CCCCCC')), # 网格线
|
||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||
('LEFTPADDING', (0, 0), (-1, -1), 5),
|
||
('TOPPADDING', (0, 0), (-1, -1), 4),
|
||
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
|
||
]))
|
||
story.append(tbl)
|
||
|
||
# 四、听力技巧
|
||
story.append(Paragraph('四、听力小技巧:假日“泛听”法', h1))
|
||
for t in tips:
|
||
story.append(Paragraph(esc(t), tip))
|
||
|
||
# 页脚说明
|
||
story.append(Spacer(1, 10))
|
||
story.append(Paragraph('<font name="CJK">本文档可离线保存,配合 MP3 音频文件使用:先听 → 逐句跟读 → 对照理解。</font>', foot))
|
||
|
||
# =====================================================================
|
||
# 第 5 步:写出 PDF
|
||
# ---------------------------------------------------------------------
|
||
# SimpleDocTemplate 自动分页;title 参数写入 PDF 文档元信息。
|
||
# =====================================================================
|
||
doc = SimpleDocTemplate("每日英语听力_2026-10-01_国庆主题_完整版.pdf", pagesize=A4,
|
||
rightMargin=18*mm, leftMargin=18*mm,
|
||
topMargin=16*mm, bottomMargin=16*mm,
|
||
title="每日英语听力 2026-10-01 国庆主题")
|
||
doc.build(story)
|
||
print("PDF generated OK")
|
||
#(注:内容由AI生成)
|