修复证书生成问题

This commit is contained in:
马梓彭 2026-05-19 11:12:41 +08:00
parent f823dfb7eb
commit 8c298544b4
2 changed files with 45 additions and 46 deletions

View File

@ -295,7 +295,15 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
// 生成PDF
var pdfStream = new MemoryStream();
await GeneratePdfWithQuestPDF(pdfStream, documentData);
try
{
await GeneratePdfWithQuestPDF(pdfStream, documentData);
}
catch (Exception ex)
{
var a = 1;
throw;
}
pdfStream.Position = 0;
_logger.LogInformation("DOCX转PDF完成,PDF大小: {Size} bytes", pdfStream.Length);
@ -793,8 +801,8 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
var outerXml = drawing.OuterXml;
_logger.LogDebug("Drawing 对象原始 XML (前800字符): {Xml}", outerXml.Substring(0, Math.Min(800, outerXml.Length)));
// 方法1: 查找 simplePos 属性(通常用于绝对定位)
// ExtractSimplePosition(outerXml, paragraphData);
// 方法1: 先查找 simplePos 属性(通常用于绝对定位)
ExtractSimplePosition(outerXml, paragraphData);
// 方法2: 如果 simplePos 没有值,查找 positionOffset 元素
if (paragraphData.PositionX == null || paragraphData.PositionY == null)
@ -802,12 +810,6 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
ExtractPositionOffset(outerXml, paragraphData);
}
// 注意:不使用 distL, distT 等距离属性
// 这些属性不是文本框在页面上的坐标,而是:
// - 要么是文本框的内边距(文本到边框的距离)
// - 要么是文本框到页面边距的距离(而不是绝对坐标)
// 我们目前使用写死定位方式,不需要依赖这些值
_logger.LogDebug("最终提取的位置 - X: {X}, Y: {Y}", paragraphData.PositionX, paragraphData.PositionY);
}
catch (Exception ex)
@ -1063,6 +1065,14 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
var selectedFont = CertificateFontHelper.GetSelectedFont();
_logger.LogInformation($"使用字体: {selectedFont ?? "默认字体"}");
// 记录提取到的位置信息,便于调试
_logger.LogInformation($"提取到 {documentData.Paragraphs.Count} 个文本框:");
for (int i = 0; i < documentData.Paragraphs.Count; i++)
{
var p = documentData.Paragraphs[i];
_logger.LogInformation($" [{i}] 文本: {p.Text}, X: {p.PositionX}, Y: {p.PositionY}, 字号: {p.FontSize}");
}
var questDocument = QuestPDF.Fluent.Document.Create(container =>
{
container.Page(page =>
@ -1084,62 +1094,52 @@ public class CertificateGenerateService : IDynamicApiController, ITransient
.Image(documentData.BackgroundImage)
.FitArea();
}
// 最简单且工作的方案:使用正常的 Column 布局
// 但通过跟踪和补偿累积高度来定位所有文本
float cumulativeHeight = 0;
// 使用 Column 来放置所有内容
page.Content().Column(column =>
{
column.Spacing(0);
double? firstParagraphY = null;
double? firstParagraphOriginalY = null;
// 根据文本框数量确定X轴偏移量
float xOffset = documentData.Paragraphs.Count == 1 ? 140 : 100;
// 根据段落信息进行定位
for (int i = 0; i < documentData.Paragraphs.Count; i++)
foreach (var paragraph in documentData.Paragraphs)
{
var paragraph = documentData.Paragraphs[i];
var fontSize = paragraph.FontSize ?? 14;
var isBold = paragraph.IsBold;
// 计算位置
float x = (float)(paragraph.PositionX ?? 50) + xOffset;
float y;
if (i == 0)
{
firstParagraphY = paragraph.PositionY ?? 50;
firstParagraphOriginalY = firstParagraphY.Value;
y = (float)firstParagraphY + 70;
}
else
{
y = (float)(firstParagraphOriginalY + 70) + (i * 45);
}
// 创建文本元素
var textElement = column.Item()
// X偏移+80保持不变
// Y坐标减去之前所有文本占用的高度进行补偿
float x = (float)(paragraph.PositionX ?? 50) + 90;
float y = (float)(paragraph.PositionY ?? 50) + 75 - cumulativeHeight;
_logger.LogInformation($"放置文本框: 文本={paragraph.Text}, X={x}, Y={y}, cumulativeHeight={cumulativeHeight}");
// 正常添加文本,不限制高度
var element = column.Item()
.TranslateX(x)
.TranslateY(y)
.Text(paragraph.Text)
.FontSize((float)fontSize)
.FontColor(QuestPDF.Helpers.Colors.Black)
.FontColor(Colors.Black)
.AlignLeft();
// 应用字体
if (!string.IsNullOrEmpty(selectedFont))
{
textElement = textElement.FontFamily(selectedFont);
element = element.FontFamily(selectedFont);
}
if (isBold)
{
textElement = textElement.Bold();
element = element.Bold();
}
// 估算当前文本行的高度并累积
// 通常一行文本高度大约是 font-size * 1.5
cumulativeHeight += (float)(fontSize * 1.5f);
}
});
// 添加二维码到右下角
if (documentData.QRCodeImage != null)
{

View File

@ -287,7 +287,6 @@ public class PointsRecordService : BaseService<PointsRecord>, ITransient
Action = e.EventName,
Item = pr.Name,
ProjectId = pr.Id,
ProjectType = pr.ProjectType,
Group = er.GroupName,
Points = p.Points,
Remark = p.Remark,