背景
文档中心需要将现有 web 内容转化为 PDF 文档 供用户下载。 如:
一个模块下有很多页面,需要将这些页面合并到一个PDF文件中输出。
技术方案
输出目录树
- 收集目录产出 目录树文件。
处理文档将内容输出到一个HTML文件中
- 按目录树收集 HTML 文件。
- 按目录树处理 文档内的 H1 - H5 的内容。
- 处理 HTML 输出到 output.html中, 删除头尾部分。
将 HTML 转化为 PDF
- 将合并后的 HTML 转化为 PDF 文档。
- 为 PDF 添加封面
- 为 PDF 添加目录
- 为 PDF 添加页码
输出目录树
项目是基于 docusaurus.io/ 二次开发的,依据 docusaurus 的文档规则把所有 页面放置到 docs 目录下。
模块目录如下
以 docs 目录为起点,它下面的每个文件夹都是一个模块,但并不是所有文件夹都是模块。
模块干预
有一个模块配置列表
| Filefloder(不要修改这一列) | Module | Catalog | Version | ReleaseDate |
|---|---|---|---|---|
| android-callkit | 客户端 SDK 文档 | 融云音视频 (RTC) | Android CallKit 5.x | 2024-08-07 |
| android-calllib | 客户端 SDK 文档 | 融云音视频 (RTC) | Android CallLIb 5.x | 2024-07-22 |
| android-callplus | 客户端 SDK 文档 | 融云音视频 (RTC) | Android CallPlus 5.x | 2024-07-22 |
| android-imkit | 客户端 SDK 文档 | 融云即时通讯 (IM) | Android IMKit 5.x | 2024-07-22 |
| android-imlib | 客户端 SDK 文档 | 融云即时通讯 (IM) | Android IMLib 5.x | 2024-07-22 |
pdfOutline=/opt/com/docs/pdfOutline.md;
entry=$(cat $pdfOutline | sed '1,2d;' | awk -F '|' '{print $2}' | xargs)
for e in ${entry[@]} ;do
echo $e; ## 需求处理的模块
generateMap ./$e;
done
模块分析 如 android-callkit
结构分析
- assets 为静态资源不做为目录结构
- md 文件为一个目录《叶子节点》
- 文件夹为一个《分支节点》
内容分析
文件排序
文件 xxxxx.md
---
title: 状态码
sidebar_position: 70
---
# 状态码
## 其他信息
... ...
文件夹
文件 _category_.json
{
"position": 40,
"label": "升级指南",
"collapsible": true
}
对文件进行排序
- md文件需要按文件内容
sidebar_position排序 - 文件夹也参与
sidebar_position排序 - 文件夹的排序按
_category_.json文件中的position参与到文件的排序中。
脚本
1. 提取出当前文件夹下 md 文件进行排序
## 按 sidebar_position 生成目录
generateSidebar() {
find $1 -maxdepth 1 -type f -exec grep sidebar_position {} + | awk -F: '{print $3 ,$1}';
}
输出内容
60 ./release-notes.md
20 ./integration.md
1 ./prepare.md
30 ./demo.md
10 ./init.md
1 ./import.md
70 ./code.md
50 ./api-reference.md
2. 抽取与当前文件夹 的序列号
generateCategorySidebar() {
find $1 -mindepth 2 -maxdepth 2 -type f -name _category_.json -exec grep -H -r position {} + | sed 's/_category_.json//;s/ //;s/,//' | awk -F: '{print $3 ,$1}'
}
输出内容
40 ./upgrade/
3. 将上述内容输入到临时文件中 /tmp/docs/tmp.xxxx.txt
60 ./release-notes.md
20 ./integration.md
1 ./prepare.md
30 ./demo.md
10 ./init.md
1 ./import.md
70 ./code.md
50 ./api-reference.md
40 ./upgrade/
脚本
cat /tmp/docs/tmp.xxxx.txt | sort -n
结果
1 ./import.md
1 ./prepare.md
10 ./init.md
20 ./integration.md
30 ./demo.md
40 ./upgrade/
50 ./api-reference.md
60 ./release-notes.md
70 ./code.md
脚本
cat /tmp/docs/tmp.xxxx.txt | sort -n | awk '{print $2}'
最终结果
./import.md
./prepare.md
./init.md
./integration.md
./demo.md
./upgrade/
./api-reference.md
./release-notes.md
./code.md
4. 输出为XML目录结构
处理 3. 产出的 文件,如果是一个目录则再次使用 1.、2. 过程处理一下。
示例代码
generateMap() {
generateSidebar $1 >> $tmpfile
generateCategorySidebar $1 >> $tmpfile
generateOtherMd $1 >> $tmpfile
subdirs=$(cat $tmpfile | sort -n | awk '{print $2}')
for subdir in $subdirs; do
if [ -d $subdir ]; then
currentDir=$(echo $subdir | sed 's/\/$//')
if [ -f $currentDir/_category_.json ]; then
echo "$2<item title='$dirlabel' index='$3'>"
generateMap $currentDir "$2 " `expr $3 + 1`
echo "$2</item>"
fi
else
title=$(grep -o '^title:.*' $subdir | sed 's/title: //;s/"//g' | sed "s/'//g")
echo "$2<item title='$title' file='$subdir' href='$href' index='$3' />"
fi
done
}
getAllDir() {
for dir in $(find $1 \( -type d -name "_res" -o -name "partials" -o -name "assets" \) -prune -o -maxdepth 1 -type d -print); do
if [ $dir != "$1" ]; then
echo "generate $dir-menu.xml start"
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $dir-menu.xml
echo "<outline>" >> $dir-menu.xml
generateMap $dir '' 1 >> $dir-menu.xml
echo "</outline>" >> $dir-menu.xml
# break;
fi
done
}
完整内容
输出结果
<?xml version="1.0" encoding="UTF-8"?>
<outline>
<item title='导入 CallKit SDK' file='./android-callkit/import.md' href='http://docs.rongcloud.cn/android-callkit/import.html' index='1' />
<item title='实时音视频开发指导' file='./android-callkit/prepare.md' href='http://docs.rongcloud.cn/android-callkit/prepare.html' index='1' />
<item title='初始化' file='./android-callkit/init.md' href='http://docs.rongcloud.cn/android-callkit/init.html' index='1' />
<item title='实现音视频通话' file='./android-callkit/integration.md' href='http://docs.rongcloud.cn/android-callkit/integration.html' index='1' />
<item title='运行示例项目(Demo)' file='./android-callkit/demo.md' href='http://docs.rongcloud.cn/android-callkit/demo.html' index='1' />
<item title='升级指南' index='1'>
<item title='CallKit 3.X 升级到 5.X' file='./android-callkit/upgrade/upgrade-callkit-from-3x.md' href='http://docs.rongcloud.cn/android-callkit/upgrade/upgrade-callkit-from-3x.html' index='2' />
<item title='CallKit 5.X 升级' file='./android-callkit/upgrade/upgrade-callkit.md' href='http://docs.rongcloud.cn/android-callkit/upgrade/upgrade-callkit.html' index='2' />
</item>
<item title='客户端 API' file='./android-callkit/api-reference.md' href='http://docs.rongcloud.cn/android-callkit/api-reference.html' index='1' />
<item title='版本说明' file='./android-callkit/release-notes.md' href='http://docs.rongcloud.cn/android-callkit/release-notes.html' index='1' />
<item title='状态码' file='./android-callkit/code.md' href='http://docs.rongcloud.cn/android-callkit/code.html' index='1' />
<item title='' file='./android-callkit/test.123' href='http://docs.rongcloud.cn/android-callkit/test.123' index='1' />
</outline>
处理脚本
#!/bin/bash
cd /opt/com.rcloud/cn.rongcloud.docs/docs/;
mkdir -p /opt/com.rcloud/cn.rongcloud.docs/pdf-menu;
## 按 sidebar_position 生成目录
generateSidebar() {
find $1 -maxdepth 1 -type f -exec grep sidebar_position {} + | awk -F: '{print $3 ,$1}';
}
## 按 _category_.json 生成目录
generateCategorySidebar() {
find $1 -mindepth 2 -maxdepth 2 -type f -name _category_.json -exec grep -H -r position {} + | sed 's/_category_.json//;s/ //;s/,//' | awk -F: '{print $3 ,$1}'
}
## 没有 sidebar_position 的文件
generateOtherMd() {
find $1 \( -type f -name "_category_.json" \) -prune -o -maxdepth 1 -type f -exec grep -L sidebar_position {} + | awk -F: '{print 10000, $1}';
}
generateMap() {
subfile=$(echo $1 | sed 's/^\.\///')
mkdir -p /tmp/docs/$subfile
tmpfile=/tmp/docs/$subfile/$(date +%s);
touch $tmpfile;
generateSidebar $1 >> $tmpfile
generateCategorySidebar $1 >> $tmpfile
generateOtherMd $1 >> $tmpfile
subdirs=$(cat $tmpfile | sort -n | awk '{print $2}')
for subdir in $subdirs; do
if [ -d $subdir ]; then
# echo "dir $subdir"
currentDir=$(echo $subdir | sed 's/\/$//')
if [ -f $currentDir/_category_.json ]; then
dirlabel=$(grep '"label":.*' -o $currentDir/_category_.json | sed 's/"label": "\(.*\)",/\1/;s/"//g' | sed "s/'//g")
echo "$2<item title='$dirlabel' index='$3'>"
generateMap $currentDir "$2 " `expr $3 + 1`
echo "$2</item>"
fi
else
title=$(grep -o '^title:.*' $subdir | sed 's/title: //;s/"//g' | sed "s/'//g")
href=$( echo $subdir | sed 's/^\./http:\/\/docs.rongcloud.cn/;s/md$/html/;s/mdx$/html/')
echo "$2<item title='$title' file='$subdir' href='$href' index='$3' />"
fi
done
}
getAllDir() {
for dir in $(find $1 \( -type d -name "_res" -o -name "partials" -o -name "assets" \) -prune -o -maxdepth 1 -type d -print); do
# for dir in "./sms"; do
if [ $dir != "$1" ]; then
echo "generate $dir-menu.xml start"
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $dir-menu.xml
echo "<outline>" >> $dir-menu.xml
generateMap $dir '' 1 >> $dir-menu.xml
echo "</outline>" >> $dir-menu.xml
# break;
fi
done
}
date "+%Y-%m-%d %H:%M:%S"
getAllDir . ' '
date "+%Y-%m-%d %H:%M:%S"
# 遍历源目录中所有以 "-menu.xml" 结尾的文件
for file in ./*-menu.xml; do
# 获取文件名(不包括路径)
filename=$(basename "$file")
# 构建目标文件路径
dest_file="../pdf-menu/$filename"
# 检查目标文件是否存在
if [ ! -e "$dest_file" ]; then
# 如果目标文件不存在,则移动文件
mv "$file" "$dest_file"
echo "Moved: $filename"
else
# 如果目标文件已经存在,则跳过并给出提示
echo "Skipped: $filename (already exists)"
fi
done
find . -name "*-menu.xml" | xargs rm -fr