证书配置
This commit is contained in:
parent
2180950c5e
commit
66a93af122
@ -88,7 +88,13 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
// 设置 QuestPDF 许可证
|
// 设置 QuestPDF 许可证
|
||||||
QuestPDF.Settings.License = LicenseType.Community;
|
QuestPDF.Settings.License = LicenseType.Community;
|
||||||
|
|
||||||
// 尝试配置中文字体
|
// 配置 QuestPDF 字体管理
|
||||||
|
// QuestPDF 2025.12+ 会自动使用 SkiaSharp 的字体检测
|
||||||
|
// 我们不需要显式注册字体,只需要确保系统中有可用的中文字体
|
||||||
|
|
||||||
|
_logger.LogInformation("QuestPDF 配置完成,使用 SkiaSharp 字体系统");
|
||||||
|
|
||||||
|
// 尝试配置中文字体(检测并记录)
|
||||||
ConfigureChineseFonts();
|
ConfigureChineseFonts();
|
||||||
|
|
||||||
_logger.LogInformation("PDF 字体系统初始化完成");
|
_logger.LogInformation("PDF 字体系统初始化完成");
|
||||||
@ -102,7 +108,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置中文字体 - 增强版,支持字体文件检测和注册
|
/// 配置中文字体 - 修复版,正确使用 QuestPDF 字体注册机制
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ConfigureChineseFonts()
|
private void ConfigureChineseFonts()
|
||||||
{
|
{
|
||||||
@ -116,105 +122,83 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
|
|
||||||
_logger.LogInformation($"当前操作系统: {(isLinux ? "Linux" : isWindows ? "Windows" : "其他")}");
|
_logger.LogInformation($"当前操作系统: {(isLinux ? "Linux" : isWindows ? "Windows" : "其他")}");
|
||||||
|
|
||||||
// Linux 字体路径列表
|
// Linux 字体路径列表(优先级排序)
|
||||||
var linuxFontPaths = new[]
|
var linuxFontPaths = new[]
|
||||||
{
|
{
|
||||||
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "WenQuanYi Zen Hei"),
|
||||||
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", "WenQuanYi Micro Hei"),
|
||||||
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "Noto Sans CJK SC"),
|
||||||
"/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
|
("/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", "Noto Sans CJK SC"),
|
||||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", "DejaVu Sans"),
|
||||||
"/usr/local/share/fonts/wqy-zenhei.ttc"
|
("/usr/local/share/fonts/wqy-zenhei.ttc", "WenQuanYi Zen Hei")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Windows 字体路径列表
|
// Windows 字体路径列表(优先级排序)
|
||||||
var windowsFontPaths = new[]
|
var windowsFontPaths = new[]
|
||||||
{
|
{
|
||||||
@"C:\Windows\Fonts\msyh.ttc", // 微软雅黑
|
(@"C:\Windows\Fonts\msyh.ttc", "Microsoft YaHei"), // 微软雅黑
|
||||||
@"C:\Windows\Fonts\simsun.ttc", // 宋体
|
(@"C:\Windows\Fonts\simsun.ttc", "SimSun"), // 宋体
|
||||||
@"C:\Windows\Fonts\simhei.ttf" // 黑体
|
(@"C:\Windows\Fonts\simhei.ttf", "SimHei"), // 黑体
|
||||||
|
(@"C:\Windows\Fonts\msyhl.ttc", "Microsoft YaHei Light"),
|
||||||
|
(@"C:\Windows\Fonts\simkai.ttf", "KaiTi")
|
||||||
};
|
};
|
||||||
|
|
||||||
string fontPathToRegister = null;
|
string fontPathToRegister = null;
|
||||||
string fontNameToUse = null;
|
string fontNameToUse = null;
|
||||||
|
|
||||||
// 尝试查找字体文件
|
// 尝试查找并注册字体文件
|
||||||
if (isLinux)
|
var fontPathsToCheck = isLinux ? linuxFontPaths : windowsFontPaths;
|
||||||
|
foreach (var (path, name) in fontPathsToCheck)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("正在 Linux 系统中查找字体文件...");
|
if (File.Exists(path))
|
||||||
foreach (var path in linuxFontPaths)
|
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
_logger.LogInformation($"找到字体文件: {path} ({name})");
|
||||||
|
fontPathToRegister = path;
|
||||||
|
fontNameToUse = name;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"找到字体文件: {path}");
|
// 尝试在 QuestPDF 中注册字体(如果支持)
|
||||||
fontPathToRegister = path;
|
// QuestPDF 会自动搜索系统字体,也可以显式注册
|
||||||
|
_logger.LogInformation($"尝试注册字体: {fontNameToUse}");
|
||||||
|
|
||||||
if (path.Contains("wqy-zenhei")) fontNameToUse = "WenQuanYi Zen Hei";
|
// 先测试字体是否可用
|
||||||
else if (path.Contains("wqy-microhei")) fontNameToUse = "WenQuanYi Micro Hei";
|
if (TestFontAvailability(fontNameToUse))
|
||||||
else if (path.Contains("Noto")) fontNameToUse = "Noto Sans CJK SC";
|
{
|
||||||
else if (path.Contains("DejaVu")) fontNameToUse = "DejaVu Sans";
|
_selectedChineseFont = fontNameToUse;
|
||||||
else fontNameToUse = "CustomChineseFont";
|
_logger.LogInformation($"字体配置成功: {_selectedChineseFont}");
|
||||||
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogDebug($"字体文件不存在: {path}");
|
_logger.LogWarning(ex, $"字体注册失败: {fontNameToUse},继续尝试下一个...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else if (isWindows)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("正在 Windows 系统中查找字体文件...");
|
|
||||||
foreach (var path in windowsFontPaths)
|
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
_logger.LogDebug($"字体文件不存在: {path}");
|
||||||
{
|
|
||||||
_logger.LogInformation($"找到字体文件: {path}");
|
|
||||||
fontPathToRegister = path;
|
|
||||||
|
|
||||||
if (path.Contains("msyh")) fontNameToUse = "Microsoft YaHei";
|
|
||||||
else if (path.Contains("simsun")) fontNameToUse = "SimSun";
|
|
||||||
else if (path.Contains("simhei")) fontNameToUse = "SimHei";
|
|
||||||
else fontNameToUse = "CustomChineseFont";
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果找到了字体文件,尝试显式注册
|
// 如果没有找到字体文件,尝试使用备选字体名称
|
||||||
if (!string.IsNullOrEmpty(fontPathToRegister))
|
if (string.IsNullOrEmpty(_selectedChineseFont))
|
||||||
{
|
{
|
||||||
try
|
_logger.LogWarning("未找到字体文件,使用备选字体名称列表...");
|
||||||
{
|
|
||||||
_logger.LogInformation($"正在注册字体: {fontNameToUse} ({fontPathToRegister})");
|
|
||||||
|
|
||||||
// 注意:QuestPDF 可能不支持直接从文件路径注册
|
|
||||||
// 我们采用替代方案:记录字体名称,并在生成时强制使用
|
|
||||||
_selectedChineseFont = fontNameToUse;
|
|
||||||
_logger.LogInformation($"字体配置完成: {_selectedChineseFont}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, $"字体注册失败,将尝试自动查找: {fontNameToUse}");
|
|
||||||
_selectedChineseFont = fontNameToUse; // 仍然记录下来
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("未找到字体文件,将使用备选字体列表...");
|
|
||||||
|
|
||||||
// 备选字体名称列表
|
// 备选字体名称列表(按优先级)
|
||||||
var chineseFontNames = new[]
|
var chineseFontNames = new[]
|
||||||
{
|
{
|
||||||
"WenQuanYi Zen Hei",
|
|
||||||
"WenQuanYi Micro Hei",
|
|
||||||
"Noto Sans CJK SC",
|
|
||||||
"Microsoft YaHei",
|
"Microsoft YaHei",
|
||||||
"SimSun",
|
"SimSun",
|
||||||
"SimHei",
|
"SimHei",
|
||||||
"DejaVu Sans"
|
"WenQuanYi Zen Hei",
|
||||||
|
"WenQuanYi Micro Hei",
|
||||||
|
"Noto Sans CJK SC",
|
||||||
|
"DejaVu Sans",
|
||||||
|
"Arial Unicode MS",
|
||||||
|
"PingFang SC",
|
||||||
|
"STSong"
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var fontName in chineseFontNames)
|
foreach (var fontName in chineseFontNames)
|
||||||
@ -229,8 +213,9 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug(ex, $"字体不可用: {fontName}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,6 +226,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
_logger.LogError("请在系统中安装中文字体:");
|
_logger.LogError("请在系统中安装中文字体:");
|
||||||
_logger.LogError(" Ubuntu/Debian: sudo apt-get install fonts-wqy-zenhei fonts-wqy-microhei fonts-noto-cjk");
|
_logger.LogError(" Ubuntu/Debian: sudo apt-get install fonts-wqy-zenhei fonts-wqy-microhei fonts-noto-cjk");
|
||||||
_logger.LogError(" CentOS/RHEL: sudo yum install wqy-microhei-fonts wqy-zenhei-fonts");
|
_logger.LogError(" CentOS/RHEL: sudo yum install wqy-microhei-fonts wqy-zenhei-fonts");
|
||||||
|
_logger.LogError(" Windows: 确保系统中有微软雅黑或宋体");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1265,7 +1251,7 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用QuestPDF生成PDF - 增强版,确保中文字体被正确应用
|
/// 使用QuestPDF生成PDF - 修复版,确保中文字体被正确应用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task GeneratePdfWithQuestPDF(Stream pdfStream, DocumentData documentData)
|
private async Task GeneratePdfWithQuestPDF(Stream pdfStream, DocumentData documentData)
|
||||||
{
|
{
|
||||||
@ -1279,16 +1265,42 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
var fontFallbackList = new[]
|
var fontFallbackList = new[]
|
||||||
{
|
{
|
||||||
chineseFontName,
|
chineseFontName,
|
||||||
|
"Microsoft YaHei",
|
||||||
|
"SimSun",
|
||||||
|
"SimHei",
|
||||||
"WenQuanYi Zen Hei",
|
"WenQuanYi Zen Hei",
|
||||||
"WenQuanYi Micro Hei",
|
"WenQuanYi Micro Hei",
|
||||||
"Noto Sans CJK SC",
|
"Noto Sans CJK SC",
|
||||||
"Microsoft YaHei",
|
"DejaVu Sans",
|
||||||
"SimSun",
|
"Arial Unicode MS"
|
||||||
"DejaVu Sans"
|
|
||||||
}.Where(f => !string.IsNullOrEmpty(f)).Distinct().ToArray();
|
}.Where(f => !string.IsNullOrEmpty(f)).Distinct().ToArray();
|
||||||
|
|
||||||
_logger.LogInformation($"字体回退列表: {string.Join(", ", fontFallbackList)}");
|
_logger.LogInformation($"字体回退列表: {string.Join(", ", fontFallbackList)}");
|
||||||
|
|
||||||
|
// 选择最终要使用的字体(尝试测试每一个)
|
||||||
|
string finalFont = null;
|
||||||
|
foreach (var font in fontFallbackList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (TestFontAvailability(font))
|
||||||
|
{
|
||||||
|
finalFont = font;
|
||||||
|
_logger.LogInformation($"选择最终字体: {finalFont}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(finalFont))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("未找到可用字体,将使用 QuestPDF 默认字体");
|
||||||
|
}
|
||||||
|
|
||||||
var questDocument = QuestPDF.Fluent.Document.Create(container =>
|
var questDocument = QuestPDF.Fluent.Document.Create(container =>
|
||||||
{
|
{
|
||||||
container.Page(page =>
|
container.Page(page =>
|
||||||
@ -1296,23 +1308,22 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
page.Margin(0);
|
page.Margin(0);
|
||||||
page.Size(PageSizes.A4);
|
page.Size(PageSizes.A4);
|
||||||
|
|
||||||
// 设置默认文本样式 - 强制应用字体
|
// 设置默认文本样式 - 使用选定的中文字体
|
||||||
page.DefaultTextStyle(x =>
|
page.DefaultTextStyle(x =>
|
||||||
{
|
{
|
||||||
var style = x.FontSize(12).FontColor(QuestPDF.Helpers.Colors.Black);
|
var style = x.FontSize(12).FontColor(QuestPDF.Helpers.Colors.Black);
|
||||||
|
|
||||||
// 尝试应用每一个字体,直到成功
|
// 应用选定的字体
|
||||||
foreach (var font in fontFallbackList)
|
if (!string.IsNullOrEmpty(finalFont))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
style = style.FontFamily(font);
|
style = style.FontFamily(finalFont);
|
||||||
_logger.LogDebug($"尝试应用字体到默认样式: {font}");
|
_logger.LogDebug($"应用字体到默认样式: {finalFont}");
|
||||||
break; // 成功应用一个就停止
|
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
continue;
|
_logger.LogWarning(ex, $"应用字体 {finalFont} 失败,使用默认字体");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1327,11 +1338,9 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
.FitArea();
|
.FitArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 Column 来放置所有内容,但禁用默认间距
|
// 使用绝对定位来放置文本内容(更精确)
|
||||||
page.Content().Column(column =>
|
page.Content().Layer(layer =>
|
||||||
{
|
{
|
||||||
column.Spacing(0);
|
|
||||||
|
|
||||||
double? firstParagraphY = null;
|
double? firstParagraphY = null;
|
||||||
double? firstParagraphOriginalY = null;
|
double? firstParagraphOriginalY = null;
|
||||||
|
|
||||||
@ -1366,24 +1375,26 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
|
|||||||
|
|
||||||
_logger.LogDebug("段落 {Index} 位置: X={X}, Y={Y}", i + 1, x, y);
|
_logger.LogDebug("段落 {Index} 位置: X={X}, Y={Y}", i + 1, x, y);
|
||||||
|
|
||||||
// 使用 Translate 来定位,配合 Spacing(0) 确保没有额外间隔
|
// 使用绝对定位放置文本
|
||||||
var textElement = column.Item().TranslateX(x).TranslateY(y).Text(paragraph.Text)
|
var textElement = layer
|
||||||
|
.TranslateX(x)
|
||||||
|
.TranslateY(y)
|
||||||
|
.Text(paragraph.Text)
|
||||||
.FontSize((float)fontSize)
|
.FontSize((float)fontSize)
|
||||||
.FontColor(QuestPDF.Helpers.Colors.Black)
|
.FontColor(QuestPDF.Helpers.Colors.Black)
|
||||||
.AlignLeft();
|
.AlignLeft();
|
||||||
|
|
||||||
// 尝试应用每一个字体到这个文本元素
|
// 应用选定的字体
|
||||||
foreach (var font in fontFallbackList)
|
if (!string.IsNullOrEmpty(finalFont))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
textElement = textElement.FontFamily(font);
|
textElement = textElement.FontFamily(finalFont);
|
||||||
_logger.LogDebug($"段落 {i + 1} 应用字体: {font}");
|
_logger.LogDebug($"段落 {i + 1} 应用字体: {finalFont}");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
continue;
|
_logger.LogDebug(ex, $"段落 {i + 1} 应用字体失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user