把pdf字体替换成微软雅黑

This commit is contained in:
chenqiang 2025-09-08 11:55:46 +08:00
parent 836b38c4b8
commit be857ff36a
15 changed files with 108 additions and 214 deletions

4
.gitignore vendored
View File

@ -25,4 +25,6 @@ pnpm-debug.log*
#Electron-builder output
/dist_electron
/high_version
/high_version
/output

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -49,19 +49,12 @@ function getFontPath() {
// 现在使用getFontPath()函数来获取字体路径
const FONT_PATH = getFontPath();
// 优先使用SourceHanSansSC字体
const primaryFontPath = path.join(FONT_PATH, 'SourceHanSansSC-Regular.otf');
const boldFontPath = path.join(FONT_PATH, 'SourceHanSansSC-Bold.otf');
// 保留宋体作为备选
const simsunPath = path.join(FONT_PATH, 'simsun.ttf');
const fallbackFontPath = path.join(FONT_PATH, 'simsun.ttc'); // 备选字体路径
// 统一使用微软雅黑字体
const msyhFontPath = path.join(FONT_PATH, 'msyh.ttf');
// 输出找到的字体路径供调试
console.log('FONT_PATH:', FONT_PATH);
console.log('primaryFontPath:', primaryFontPath);
console.log('boldFontPath:', boldFontPath);
console.log('simsunPath:', simsunPath);
console.log('fallbackFontPath:', fallbackFontPath);
console.log('msyhFontPath:', msyhFontPath, '存在?', fs.existsSync(msyhFontPath));
/**
* 服务层获取所有考生列表
@ -95,95 +88,43 @@ async function generatePdfService(pdfData, fileName) {
// 加载中文字体的标志
let chineseFontLoaded = false;
let boldFontLoaded = false;
// 保存当前字体
let currentFont = null;
// 修改字体加载逻辑
try {
// 1. 尝试加载SourceHanSansSC常规字体
if (fs.existsSync(primaryFontPath)) {
try {
doc.registerFont('SourceHanSans', primaryFontPath);
doc.font('SourceHanSans');
currentFont = 'SourceHanSans';
// 专门的字体加载函数
function loadMicrosoftYaHeiFont() {
try {
// 只加载微软雅黑字体
if (fs.existsSync(msyhFontPath)) {
doc.registerFont('MicrosoftYaHei', msyhFontPath);
doc.font('MicrosoftYaHei');
currentFont = 'MicrosoftYaHei';
chineseFontLoaded = true;
console.log('成功加载SourceHanSansSC-Regular.otf字体');
} catch (error) {
console.error('加载SourceHanSansSC-Regular.otf字体失败:', error);
}
}
// 2. 尝试加载SourceHanSansSC粗体字体(用于标题)
if (fs.existsSync(boldFontPath)) {
try {
doc.registerFont('SourceHanSansBold', boldFontPath);
boldFontLoaded = true;
console.log('成功加载SourceHanSansSC-Bold.otf字体');
} catch (error) {
console.error('加载SourceHanSansSC-Bold.otf字体失败:', error);
}
}
// 3. 如果SourceHanSansSC字体加载失败尝试加载宋体
if (!chineseFontLoaded) {
if (fs.existsSync(simsunPath)) {
try {
doc.registerFont('SimSun', simsunPath);
doc.font('SimSun');
currentFont = 'SimSun';
chineseFontLoaded = true;
console.log('成功加载simsun.ttf字体');
} catch (ttfError) {
console.error('加载simsun.ttf字体失败:', ttfError);
// 尝试加载备选TTC字体
if (fs.existsSync(fallbackFontPath)) {
try {
doc.registerFont('SimSun', fallbackFontPath);
doc.font('SimSun');
currentFont = 'SimSun';
chineseFontLoaded = true;
console.log('成功加载simsun.ttc字体');
} catch (ttcError) {
console.error('加载simsun.ttc字体失败:', ttcError);
}
}
}
console.log('成功加载msyh.ttf字体');
return true;
} else {
console.warn(`未找到simsun.ttf字体文件: ${simsunPath}`);
// 检查是否有备选TTC字体
if (fs.existsSync(fallbackFontPath)) {
try {
doc.registerFont('SimSun', fallbackFontPath);
doc.font('SimSun');
currentFont = 'SimSun';
chineseFontLoaded = true;
console.log('成功加载simsun.ttc字体');
} catch (error) {
console.error('加载simsun.ttc字体失败:', error);
}
}
console.error('微软雅黑字体文件不存在:', msyhFontPath);
return false;
}
} catch (error) {
console.error('加载微软雅黑字体失败:', error);
return false;
}
}
if (!chineseFontLoaded) {
console.warn('无法加载中文字体,将使用默认字体,可能导致中文显示异常');
// 在macOS上尝试使用系统字体
if (process.platform === 'darwin') {
try {
doc.font('Arial Unicode MS'); // macOS内置支持多语言的字体
currentFont = 'Arial Unicode MS';
chineseFontLoaded = true;
console.log('成功加载系统Arial Unicode MS字体');
} catch (error) {
console.error('加载系统字体失败:', error);
}
// 尝试加载字体
if (!loadMicrosoftYaHeiFont()) {
console.warn('无法加载微软雅黑字体,将使用系统字体');
// 在macOS上尝试使用系统字体
if (process.platform === 'darwin') {
try {
doc.font('Arial Unicode MS'); // macOS内置支持多语言的字体
currentFont = 'Arial Unicode MS';
chineseFontLoaded = true;
console.log('成功加载系统Arial Unicode MS字体');
} catch (error) {
console.error('加载系统字体失败:', error);
}
}
} catch (error) {
console.error('加载字体失败:', error);
console.warn('将使用默认字体,可能导致中文显示异常');
}
// 保存到文件
@ -195,14 +136,8 @@ async function generatePdfService(pdfData, fileName) {
// 保存当前字体
const tempFont = currentFont;
try {
// 尝试使用粗体字体
if (boldFontLoaded) {
doc.fontSize(20).font('SourceHanSansBold').text(pdfData.title, { align: 'center' }).moveDown();
} else if (process.platform === 'darwin') {
doc.fontSize(20).font('Arial Unicode MS Bold').text(pdfData.title, { align: 'center' }).moveDown();
} else {
doc.fontSize(20).text(pdfData.title, { align: 'center' }).moveDown();
}
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(20).text(pdfData.title, { align: 'center' }).moveDown();
} catch (error) {
console.error('设置标题字体失败:', error);
doc.fontSize(20).text(pdfData.title, { align: 'center' }).moveDown();
@ -222,17 +157,10 @@ async function generatePdfService(pdfData, fileName) {
// 保存当前字体
const tempFont = currentFont;
try {
// 尝试使用SourceHanSansBold粗体字体
if (boldFontLoaded) {
doc.fontSize(item.fontSize || 16).font('SourceHanSansBold').text(item.text, item.options || {}).moveDown();
} else if (process.platform === 'darwin') {
doc.fontSize(item.fontSize || 16).font('Arial Unicode MS Bold').text(item.text, item.options || {}).moveDown();
} else {
// 如果没有粗体字体,使用当前字体加大字号
doc.fontSize(item.fontSize || 16).text(item.text, item.options || {}).moveDown();
}
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(item.fontSize || 16).text(item.text, item.options || {}).moveDown();
} catch (error) {
console.error('切换到粗体字体失败:', error);
console.error('切换到标题字体失败:', error);
doc.fontSize(item.fontSize || 16).text(item.text, item.options || {}).moveDown();
}
// 恢复之前的字体
@ -279,30 +207,11 @@ async function generatePdfService(pdfData, fileName) {
doc.rect(marginLeft + i * cellWidth, currentY, cellWidth, cellHeight).stroke();
// 保存当前字体
const tempFont = currentFont;
try {
if (boldFontLoaded) {
doc.font('SourceHanSansBold');
} else if (process.platform === 'darwin') {
doc.font('Arial Unicode MS Bold');
}
// 垂直居中显示文本
doc.fontSize(fontSize).text(header, marginLeft + i * cellWidth + 5, currentY + (cellHeight - lines.length * 15) / 2, {
width: cellWidth - 10,
height: cellHeight - 10
});
} catch (error) {
console.error('设置表头字体失败:', error);
doc.fontSize(fontSize).text(header, marginLeft + i * cellWidth + 5, currentY + (cellHeight - lines.length * 15) / 2, {
width: cellWidth - 10,
height: cellHeight - 10
});
}
// 恢复字体
if (currentFont) {
doc.font(currentFont);
}
// 微软雅黑没有单独的粗体,使用普通字体
doc.fontSize(fontSize).text(header, marginLeft + i * cellWidth + 5, currentY + (cellHeight - lines.length * 15) / 2, {
width: cellWidth - 10,
height: cellHeight - 10
});
});
// 移动到下一行,考虑最高的表头单元格高度
const headerLines = headers.map(header => calculateLines(header, cellWidth - 10).length);
@ -470,7 +379,6 @@ async function copyToDesk(filePath) {
* @param {string} jsonString - 包含试卷信息的JSON字符串
* @returns {Promise<string>} - 生成的PDF文件绝对路径
*/
// 在generatePaperPdf函数中删除重复定义的字体路径变量
async function generatePaperPdf(jsonString) {
try {
// 解析JSON字符串
@ -543,57 +451,49 @@ async function generatePaperPdf(jsonString) {
margin: 50
});
// 加载中文字体 - 直接使用文件开头定义的全局变量,不要重新定义
// 注意这里不再重新定义FONT_PATH等变量直接使用全局变量
// 加载中文字体 - 直接使用文件开头定义的全局变量
let chineseFontLoaded = false;
let boldFontLoaded = false;
let currentFont = null;
// 专门的字体加载函数
function loadMicrosoftYaHeiFont() {
try {
// 只加载微软雅黑字体
if (fs.existsSync(msyhFontPath)) {
doc.registerFont('MicrosoftYaHei', msyhFontPath);
doc.font('MicrosoftYaHei');
currentFont = 'MicrosoftYaHei';
chineseFontLoaded = true;
console.log('成功加载msyh.ttf字体');
return true;
} else {
console.error('微软雅黑字体文件不存在:', msyhFontPath);
return false;
}
} catch (error) {
console.error('加载微软雅黑字体失败:', error);
return false;
}
}
// 添加字体加载状态日志
console.log('尝试加载字体:');
console.log('- 主要字体:', primaryFontPath, '存在?', fs.existsSync(primaryFontPath));
console.log('- 粗体字体:', boldFontPath, '存在?', fs.existsSync(boldFontPath));
console.log('- 宋体:', simsunPath, '存在?', fs.existsSync(simsunPath));
console.log('- 备选字体:', fallbackFontPath, '存在?', fs.existsSync(fallbackFontPath));
console.log('- 微软雅黑字体:', msyhFontPath, '存在?', fs.existsSync(msyhFontPath));
// 尝试加载字体
try {
if (fs.existsSync(primaryFontPath)) {
doc.registerFont('SourceHanSans', primaryFontPath);
doc.font('SourceHanSans');
currentFont = 'SourceHanSans';
chineseFontLoaded = true;
console.log('成功加载主要字体');
} else if (fs.existsSync(simsunPath)) {
doc.registerFont('SimSun', simsunPath);
doc.font('SimSun');
currentFont = 'SimSun';
chineseFontLoaded = true;
console.log('成功加载宋体');
} else if (fs.existsSync(fallbackFontPath)) {
doc.registerFont('SimSun', fallbackFontPath);
doc.font('SimSun');
currentFont = 'SimSun';
chineseFontLoaded = true;
console.log('成功加载备选字体');
} else if (process.platform === 'darwin') {
doc.font('Arial Unicode MS');
currentFont = 'Arial Unicode MS';
chineseFontLoaded = true;
console.log('使用系统默认字体 Arial Unicode MS');
if (!loadMicrosoftYaHeiFont()) {
console.warn('无法加载微软雅黑字体,可能导致中文显示异常');
// 在macOS上尝试使用系统字体
if (process.platform === 'darwin') {
try {
doc.font('Arial Unicode MS');
currentFont = 'Arial Unicode MS';
chineseFontLoaded = true;
console.log('使用系统默认字体 Arial Unicode MS');
} catch (error) {
console.error('加载系统字体失败:', error);
}
}
if (fs.existsSync(boldFontPath)) {
doc.registerFont('SourceHanSansBold', boldFontPath);
boldFontLoaded = true;
console.log('成功加载粗体字体');
}
if (!chineseFontLoaded) {
console.warn('无法加载中文字体,可能导致中文显示异常');
}
} catch (error) {
console.error('加载字体失败:', error);
}
// 保存到文件
@ -601,11 +501,7 @@ async function generatePaperPdf(jsonString) {
doc.pipe(writeStream);
// 添加标题
if (boldFontLoaded) {
doc.font('SourceHanSansBold');
} else if (process.platform === 'darwin' && currentFont === 'Arial Unicode MS') {
doc.font('Arial Unicode MS Bold');
}
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(24).text('机考试卷', { align: 'center' }).moveDown(2);
// 恢复常规字体
@ -652,20 +548,11 @@ async function generatePaperPdf(jsonString) {
doc.rect(marginLeft + i * adjustedCellWidth, currentY, adjustedCellWidth, cellHeight).stroke();
if (boldFontLoaded) {
doc.font('SourceHanSansBold');
} else if (process.platform === 'darwin' && currentFont === 'Arial Unicode MS') {
doc.font('Arial Unicode MS Bold');
}
// 微软雅黑没有单独的粗体,使用普通字体
doc.fontSize(fontSize).text(header, marginLeft + i * adjustedCellWidth + 5, currentY + (cellHeight - lines.length * 15) / 2, {
width: adjustedCellWidth - 10,
height: cellHeight - 10
});
if (currentFont) {
doc.font(currentFont);
}
});
// 移动到下一行(无间隙)
@ -737,17 +624,16 @@ async function generatePaperPdf(jsonString) {
['总题量', '试卷总分', '考试得分'],
[[totalQuestions.toString(), totalScore.toString(), realScore.toString()]]
);
// 留2行间距
doc.moveDown(2);
// 添加题目信息
paperData.questions.forEach((question, index) => {
// 题目类型和描述
if (boldFontLoaded) {
doc.font('SourceHanSansBold');
} else if (process.platform === 'darwin' && currentFont === 'Arial Unicode MS') {
doc.font('Arial Unicode MS Bold');
}
doc.fontSize(14).text(`${index + 1} 题 (${question.question_type_name})`, doc.page.margins.left).moveDown();
// 上面留3行间距
doc.moveDown(3);
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(14).text(`${index + 1} 题 (${question.question_type_name})`, doc.page.margins.left).moveDown(1); // 增加题干编号与上方内容的间距
if (currentFont) {
doc.font(currentFont);
@ -756,11 +642,12 @@ async function generatePaperPdf(jsonString) {
doc.fontSize(12).text(question.question_description, {
x: doc.page.margins.left,
width: doc.page.width - doc.page.margins.left - doc.page.margins.right
}).moveDown();
});
// 留1行间距
doc.moveDown(1);
// 添加图片
if (question.images && question.images.length > 0) {
doc.fontSize(12).text('图片:', doc.page.margins.left).moveDown();
// doc.fontSize(12).text('图片:', doc.page.margins.left).moveDown();
question.images.forEach((image) => {
if (image.image_base64) {
addBase64Image(image.image_base64);
@ -791,15 +678,16 @@ async function generatePaperPdf(jsonString) {
}
});
}
// 留1行间距
// doc.moveDown(1);
// 添加填空题
if (question.blanks && question.blanks.length > 0) {
question.blanks.forEach((blank, blankIndex) => {
if (boldFontLoaded) {
doc.font('SourceHanSansBold');
} else if (process.platform === 'darwin' && currentFont === 'Arial Unicode MS') {
doc.font('Arial Unicode MS Bold');
}
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
// 对于第一个问题,减少与题干的间距
// if (blankIndex === 0) {
// doc.moveUp(5); // 向上调整一点,减少与题干的间距
// }
doc.fontSize(12).text(`填空 ${blankIndex + 1}`, doc.page.margins.left).moveDown();
if (currentFont) {
@ -822,17 +710,18 @@ async function generatePaperPdf(jsonString) {
],
100
);
doc.moveDown(1);
});
}
// 添加选择题
if (question.choices && question.choices.length > 0) {
question.choices.forEach((choice, choiceIndex) => {
if (boldFontLoaded) {
doc.font('SourceHanSansBold');
} else if (process.platform === 'darwin' && currentFont === 'Arial Unicode MS') {
doc.font('Arial Unicode MS Bold');
}
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
// 对于第一个问题,减少与题干的间距
// if (choiceIndex === 0) {
// doc.moveUp(5); // 向上调整一点,减少与题干的间距
// }
doc.fontSize(12).text(`选择题 ${choiceIndex + 1}`, doc.page.margins.left).moveDown();
if (currentFont) {
@ -848,9 +737,11 @@ async function generatePaperPdf(jsonString) {
const optionsText = choice.choice_options.map((option, i) => {
const optionLabel = String.fromCharCode(65 + i); // A, B, C, D...
return `${optionLabel}. ${option}`;
}).join(' ');
doc.fontSize(10).text(`选项: ${optionsText}`, doc.page.margins.left).moveDown();
}).join(' '); // 使用多个空格作为选项间的间距
doc.fontSize(10).text(`选项: ${optionsText}`, doc.page.margins.left);
}
// 留一行间距
doc.moveDown(1);
drawTable(
['正确答案', '考生答案', '分值', '得分'],
[
@ -863,6 +754,7 @@ async function generatePaperPdf(jsonString) {
],
100
);
doc.moveDown(1);
});
}