2
0
Fork 0
mirror of https://github.com/Vonng/ddia.git synced 2026-06-21 00:47:05 +08:00

🐛 fix(epub): 恢复 EPUB 导出功能,修复图片显示问题 (Fixes #388)

Co-Authored-By: Zexuan Peng <pengzexuan2001@gmail.com>
This commit is contained in:
zexuan.peng 2026-02-23 22:55:56 +08:00
parent 3e12648262
commit 909e56f915
4 changed files with 372 additions and 20 deletions

View file

@ -1,12 +1,20 @@
#!/usr/bin/env bash
set -e
# Set the directory containing Markdown files
SCRIPT_DIR=$(dirname "$0")
INPUT_DIR=$(cd "$(dirname "$SCRIPT_DIR")" && pwd)
OUTPUT_DIR="$INPUT_DIR/output"
TEMP_DIR="$OUTPUT_DIR/temp"
# Create output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"
mkdir -p "$TEMP_DIR"
# Preprocess Markdown files to convert Hugo shortcodes
echo "Preprocessing Markdown files..."
python3 "${SCRIPT_DIR}/preprocess-epub.py" "${INPUT_DIR}/content/zh" "$TEMP_DIR"
convert_to_epub() {
# convert all EPUB files into a single EPUB book
@ -24,28 +32,32 @@ convert_to_epub() {
--css="$css_file" \
--webtex \
--wrap=preserve \
"${INPUT_DIR}"/SUMMARY.md \
"${INPUT_DIR}"/README.md \
"${INPUT_DIR}"/preface.md \
"${INPUT_DIR}"/part-i.md \
"${INPUT_DIR}"/ch1.md \
"${INPUT_DIR}"/ch2.md \
"${INPUT_DIR}"/ch3.md \
"${INPUT_DIR}"/ch4.md \
"${INPUT_DIR}"/part-ii.md \
"${INPUT_DIR}"/ch5.md \
"${INPUT_DIR}"/ch6.md \
"${INPUT_DIR}"/ch7.md \
"${INPUT_DIR}"/ch8.md \
"${INPUT_DIR}"/ch9.md \
"${INPUT_DIR}"/part-iii.md \
"${INPUT_DIR}"/ch10.md \
"${INPUT_DIR}"/ch11.md \
"${INPUT_DIR}"/ch12.md \
"${INPUT_DIR}"/colophon.md \
"${INPUT_DIR}"/glossary.md
"${TEMP_DIR}"/_index.md \
"${TEMP_DIR}"/preface.md \
"${TEMP_DIR}"/part-i.md \
"${TEMP_DIR}"/ch1.md \
"${TEMP_DIR}"/ch2.md \
"${TEMP_DIR}"/ch3.md \
"${TEMP_DIR}"/ch4.md \
"${TEMP_DIR}"/part-ii.md \
"${TEMP_DIR}"/ch5.md \
"${TEMP_DIR}"/ch6.md \
"${TEMP_DIR}"/ch7.md \
"${TEMP_DIR}"/ch8.md \
"${TEMP_DIR}"/ch9.md \
"${TEMP_DIR}"/part-iii.md \
"${TEMP_DIR}"/ch10.md \
"${TEMP_DIR}"/ch11.md \
"${TEMP_DIR}"/ch12.md \
"${TEMP_DIR}"/ch13.md \
"${TEMP_DIR}"/ch14.md \
"${TEMP_DIR}"/colophon.md \
"${TEMP_DIR}"/glossary.md
echo "Converted EPUB book created at $OUTPUT_BOOK."
}
convert_to_epub
# Clean up temporary files
rm -rf "$TEMP_DIR"

114
bin/preprocess-epub.py Executable file
View file

@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
预处理 Markdown 文件 Hugo shortcode 转换为 Pandoc 可识别的格式
处理两种 shortcode
1. {{< figure src="/fig/xxx.png" caption="xxx" >}} ![xxx](static/fig/xxx.png)
2. {{< figure ... >}} ( src) 移除通常用于代码示例
"""
import re
import sys
import os
from pathlib import Path
def convert_figure_shortcode(text):
"""
转换 Hugo figure shortcode Markdown 图片语法
Args:
text: Markdown 文本内容
Returns:
转换后的文本
"""
# 先处理有 caption 的 figure shortcode
# 例如: {{< figure src="/fig/ddia_0302.png" caption="图 3-2. xxx" >}}
pattern_with_caption = r'\{\{< figure\s+src="([^"]+)"[^>]*\scaption="([^"]*)"[^>]*>\}\}'
def replace_with_caption(match):
src = match.group(1)
caption = match.group(2)
# 移除开头的斜杠,添加 static 前缀
if src.startswith('/'):
src = 'static' + src
# 返回 Markdown 图片语法
return f'![{caption}]({src})'
text = re.sub(pattern_with_caption, replace_with_caption, text)
# 再处理没有 caption 的 figure shortcode
pattern_without_caption = r'\{\{< figure\s+src="([^"]+)"[^>]*>\}\}'
def replace_without_caption(match):
src = match.group(1)
if src.startswith('/'):
src = 'static' + src
return f'[]({src})'
text = re.sub(pattern_without_caption, replace_without_caption, text)
# 移除完全没有 src 属性的 figure shortcode例如用于代码块的
pattern_no_src = r'\{\{< figure[^>]*>\}\}'
text = re.sub(pattern_no_src, '', text)
return text
def process_file(input_path, output_path):
"""
处理单个 Markdown 文件
Args:
input_path: 输入文件路径
output_path: 输出文件路径
"""
with open(input_path, 'r', encoding='utf-8') as f:
content = f.read()
# 转换内容
converted_content = convert_figure_shortcode(content)
# 写入输出文件
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
f.write(converted_content)
print(f"Processed: {input_path} -> {output_path}")
def main():
"""主函数"""
if len(sys.argv) < 2:
print("Usage: preprocess.py <input_file> [output_file]")
print(" or: preprocess.py <input_dir> <output_dir>")
sys.exit(1)
input_path = sys.argv[1]
if os.path.isfile(input_path):
# 处理单个文件
output_path = sys.argv[2] if len(sys.argv) > 2 else input_path
process_file(input_path, output_path)
elif os.path.isdir(input_path):
# 处理目录
output_dir = sys.argv[2]
input_dir = Path(input_path)
# 获取所有 .md 文件
md_files = list(input_dir.glob('*.md'))
for md_file in md_files:
output_file = os.path.join(output_dir, md_file.name)
process_file(str(md_file), output_file)
print(f"\nTotal processed: {len(md_files)} files")
else:
print(f"Error: {input_path} is not a valid file or directory")
sys.exit(1)
if __name__ == '__main__':
main()

221
js/epub.css Normal file
View file

@ -0,0 +1,221 @@
/* This defines styles and classes used in the book */
@page {
margin: 10px;
}
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p,
blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img,
ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center,
fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, figure, figcaption, footer, header,
hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video, ol,
ul, li, dl, dt, dd {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
vertical-align: baseline;
}
html {
line-height: 1.2;
font-family: Georgia, serif;
color: #1a1a1a;
}
p {
text-indent: 0;
margin: 1em 0;
widows: 2;
orphans: 2;
}
a, a:visited {
color: #1a1a1a;
}
img {
max-width: 100%;
}
sup {
vertical-align: super;
font-size: smaller;
}
sub {
vertical-align: sub;
font-size: smaller;
}
h1 {
margin: 3em 0 0 0;
font-size: 2em;
page-break-before: always;
line-height: 150%;
}
h2 {
margin: 1.5em 0 0 0;
font-size: 1.5em;
line-height: 135%;
}
h3 {
margin: 1.3em 0 0 0;
font-size: 1.3em;
}
h4 {
margin: 1.2em 0 0 0;
font-size: 1.2em;
}
h5 {
margin: 1.1em 0 0 0;
font-size: 1.1em;
}
h6 {
font-size: 1em;
}
h1, h2, h3, h4, h5, h6 {
text-indent: 0;
text-align: left;
font-weight: bold;
page-break-after: avoid;
page-break-inside: avoid;
}
ol, ul {
margin: 1em 0 0 1.7em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
}
code {
font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
/*pre {*/
/* margin: 1em 0;*/
/* overflow: auto;*/
/*}*/
pre code {
white-space: pre-wrap;
word-wrap: break-word;
background-color: #f5f5f5;
padding: 1em;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
background-color: #1a1a1a;
border: none;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th, td {
padding: 0.25em 0.5em 0.25em 0.5em;
}
th {
border-top: 1px solid #1a1a1a;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code {
white-space: pre-wrap;
}
span.smallcaps {
font-variant: small-caps;
}
/* This is the most compatible CSS, but it only allows two columns: */
div.column {
display: inline-block;
vertical-align: top;
width: 50%;
}
/* If you can rely on CSS3 support, use this instead: */
/* div.columns {
display: flex;
gap: min(4vw, 1.5em);
}
div.column {
flex: auto;
overflow-x: auto;
} */
div.hanging-indent {
margin-left: 1.5em;
text-indent: -1.5em;
}
ul.task-list {
list-style: none;
}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math {
display: block;
text-align: center;
margin: 0.5rem auto;
}
/* For title, author, and date on the cover page */
h1.title { }
p.author { }
p.date { }
nav#toc ol, nav#landmarks ol {
padding: 0;
margin-left: 1em;
}
nav#toc ol li, nav#landmarks ol li {
list-style-type: none;
margin: 0;
padding: 0;
}
a.footnote-ref {
vertical-align: super;
}
em, em em em, em em em em em {
font-style: italic;
}
em em, em em em em {
font-style: normal;
}
q {
quotes: """''"'";
}
@media screen { /* Workaround for iBooks issue; see #6242 */
.sourceCode {
overflow: visible !important;
white-space: pre-wrap !important;
}
}

5
metadata.yaml Normal file
View file

@ -0,0 +1,5 @@
---
title: 设计数据密集型应用
author: Martin Kleppmann
rights: Creative Commons Non-Commercial Share Alike 3.0
language: 中文