Files
XplorePlane/XP.ReportEngine/Services/ITextPdfRenderer.cs
T

787 lines
31 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Media.Imaging;
using iText.IO.Font;
using iText.Kernel.Colors;
using iText.Kernel.Font;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas.Draw;
using iText.Layout;
using iText.Layout.Borders;
using iText.Layout.Element;
using iText.Layout.Properties;
using XP.Common.Localization.Enums;
using XP.Common.Localization.Interfaces;
using XP.Common.Logging.Interfaces;
using XP.ReportEngine.Interfaces;
using XP.ReportEngine.Models;
namespace XP.ReportEngine.Services
{
/// <summary>
/// iText 7 PDF 渲染器实现 | iText 7 PDF renderer implementation
/// </summary>
public class ITextPdfRenderer : IPdfRenderer
{
private readonly ILoggerService _logger;
private readonly ILocalizationService _localizationService;
/// <summary>
/// mm 到 points 的转换系数 | mm to points conversion factor
/// </summary>
private const float MmToPoints = 2.83465f;
/// <summary>
/// A4 页面宽度(mm| A4 page width in mm
/// </summary>
private const float A4WidthMm = 210f;
/// <summary>
/// A4 页面高度(mm| A4 page height in mm
/// </summary>
private const float A4HeightMm = 297f;
/// <summary>
/// 表头背景色 | Table header background color
/// </summary>
private const string HeaderBackgroundColor = "#E0E0E0";
/// <summary>
/// 表格奇数行背景色 | Table odd row background color
/// </summary>
private const string OddRowBackgroundColor = "#FFFFFF";
/// <summary>
/// 表格偶数行背景色 | Table even row background color
/// </summary>
private const string EvenRowBackgroundColor = "#F5F5F5";
private PdfFont _cjkFont;
private PdfFont _westernFont;
private bool _fontsInitialized;
private readonly object _fontLock = new();
public ITextPdfRenderer(ILoggerService logger, ILocalizationService localizationService)
{
_logger = logger?.ForModule<ITextPdfRenderer>() ?? throw new ArgumentNullException(nameof(logger));
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
// 字体延迟加载,不在构造函数中阻塞 | Fonts loaded lazily, not blocking in constructor
}
#region 7.1 PDF | Basic PDF document creation
/// <summary>
/// 将排版结果渲染为 PDF 内存流 | Render layout result to PDF memory stream
/// </summary>
/// <param name="pages">排版后的页面列表 | Laid-out pages</param>
/// <param name="options">生成选项 | Generation options</param>
/// <returns>PDF 内存流 | PDF memory stream</returns>
public MemoryStream Render(List<LayoutPage> pages, ReportGenerationOptions options)
{
_logger.Info("开始 PDF 渲染,共 {PageCount} 页 | Starting PDF rendering, {PageCount} pages", pages?.Count ?? 0);
var memoryStream = new MemoryStream();
try
{
var writer = new PdfWriter(memoryStream, new WriterProperties().SetFullCompressionMode(true));
// 防止 PdfWriter 关闭时关闭底层流 | Prevent PdfWriter from closing the underlying stream
writer.SetCloseStream(false);
var pdfDocument = new PdfDocument(writer);
// 设置 A4 页面尺寸 | Set A4 page size
pdfDocument.SetDefaultPageSize(PageSize.A4);
var document = new Document(pdfDocument);
// 设置默认边距(使用默认 20mm| Set default margins (20mm default)
float marginTop = 20f * MmToPoints;
float marginBottom = 20f * MmToPoints;
float marginLeft = 20f * MmToPoints;
float marginRight = 20f * MmToPoints;
document.SetMargins(marginTop, marginRight, marginBottom, marginLeft);
if (pages != null && pages.Count > 0)
{
for (int i = 0; i < pages.Count; i++)
{
if (i > 0)
{
// 添加新页面 | Add new page
document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
RenderPage(document, pages[i]);
}
}
// 仅关闭 Document(它会级联关闭 PdfDocument 和 PdfWriter
// Only close Document (it cascades to PdfDocument and PdfWriter)
document.Close();
// 重置流位置以便后续读取 | Reset stream position for subsequent reading
memoryStream.Position = 0;
_logger.Info("PDF 渲染完成 | PDF rendering completed");
}
catch (Exception ex)
{
_logger.Error(ex, "PDF 渲染过程中发生错误 | Error occurred during PDF rendering: {Message}", ex.Message);
throw;
}
return memoryStream;
}
/// <summary>
/// 渲染单个页面 | Render a single page
/// </summary>
private void RenderPage(Document document, LayoutPage page)
{
if (page?.Elements == null) return;
foreach (var element in page.Elements)
{
try
{
RenderElement(document, element);
}
catch (Exception ex)
{
_logger.Warn("渲染元素失败,跳过该元素 | Failed to render element, skipping: {Message}", ex.Message);
}
}
}
/// <summary>
/// 根据元素类型分发渲染 | Dispatch rendering based on element type
/// </summary>
private void RenderElement(Document document, LayoutElement element)
{
if (element?.Source == null) return;
var elementType = element.Source.Type?.ToLowerInvariant();
switch (elementType)
{
case "text":
RenderTextElement(document, element);
break;
case "image":
RenderImageElement(document, element);
break;
case "table":
RenderTableElement(document, element);
break;
case "divider":
RenderDividerElement(document, element);
break;
default:
_logger.Warn("未知的元素类型:{Type},跳过渲染 | Unknown element type: {Type}, skipping", elementType);
break;
}
}
#endregion
#region 7.2 | Text element rendering
/// <summary>
/// 渲染文本元素 | Render text element
/// </summary>
private void RenderTextElement(Document document, LayoutElement element)
{
var content = element.ResolvedContent ?? string.Empty;
var style = element.ResolvedStyle ?? new StyleDefinition();
var paragraph = new Paragraph(content);
// 应用字体 | Apply font
var font = GetFontForCurrentLanguage();
paragraph.SetFont(font);
// 应用字体大小 | Apply font size
paragraph.SetFontSize(style.Size);
// 应用粗体 | Apply bold
if (style.Bold)
{
paragraph.SetBold();
}
// 应用斜体 | Apply italic
if (style.Italic)
{
paragraph.SetItalic();
}
// 应用字体颜色 | Apply font color
var color = ParseColor(style.Color);
if (color != null)
{
paragraph.SetFontColor(color);
}
// 应用对齐方式 | Apply text alignment
paragraph.SetTextAlignment(ParseTextAlignment(style.Align));
// 应用背景色 | Apply background color
if (!string.IsNullOrEmpty(style.BackgroundColor))
{
var bgColor = ParseColor(style.BackgroundColor);
if (bgColor != null)
{
paragraph.SetBackgroundColor(bgColor);
}
}
// 设置固定位置(如果有坐标信息)| Set fixed position if coordinates available
if (element.Width > 0)
{
paragraph.SetWidth(element.Width * MmToPoints);
}
document.Add(paragraph);
}
#endregion
#region 7.3 | Font management
/// <summary>
/// 确保字体已初始化(线程安全的延迟加载)| Ensure fonts are initialized (thread-safe lazy loading)
/// </summary>
private void EnsureFontsInitialized()
{
if (_fontsInitialized) return;
lock (_fontLock)
{
if (_fontsInitialized) return;
InitializeFonts();
_fontsInitialized = true;
}
}
/// <summary>
/// 初始化字体(从系统字体目录加载)| Initialize fonts (load from system fonts directory)
/// 使用 Windows 系统自带字体,确保 Telerik RadPdfViewer 兼容性
/// Uses Windows built-in fonts to ensure Telerik RadPdfViewer compatibility
/// </summary>
private void InitializeFonts()
{
var fontsDir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Fonts");
// 加载微软雅黑(支持简体中文、繁体中文)| Load Microsoft YaHei (supports Simplified & Traditional Chinese)
try
{
var msyhPath = System.IO.Path.Combine(fontsDir, "msyh.ttc");
_cjkFont = PdfFontFactory.CreateFont(msyhPath + ",0", PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
_logger.Info("中文字体加载成功(微软雅黑)| Chinese font loaded successfully (Microsoft YaHei)");
}
catch (Exception ex)
{
_logger.Warn("微软雅黑加载失败,尝试后备字体 | Microsoft YaHei load failed, trying fallback: {Message}", ex.Message);
try
{
// 后备:宋体 | Fallback: SimSun
var simsunPath = System.IO.Path.Combine(fontsDir, "simsun.ttc");
_cjkFont = PdfFontFactory.CreateFont(simsunPath + ",0", PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
_logger.Info("中文后备字体加载成功(宋体)| Chinese fallback font loaded successfully (SimSun)");
}
catch (Exception ex2)
{
_logger.Warn("宋体加载失败 | SimSun load failed: {Message}", ex2.Message);
_cjkFont = null;
}
}
// 加载 Arial(西文字体)| Load Arial (Western font)
try
{
var arialPath = System.IO.Path.Combine(fontsDir, "arial.ttf");
_westernFont = PdfFontFactory.CreateFont(arialPath, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
_logger.Info("西文字体加载成功(Arial| Western font loaded successfully (Arial)");
}
catch (Exception ex)
{
_logger.Warn("Arial 加载失败,使用 Helvetica 后备 | Arial load failed, using Helvetica fallback: {Message}", ex.Message);
_westernFont = null;
}
// 如果系统字体都不可用,使用 iText 内置 Helvetica | If system fonts unavailable, use built-in Helvetica
if (_cjkFont == null && _westernFont == null)
{
_logger.Warn("所有系统字体不可用,使用 Helvetica 后备字体 | All system fonts unavailable, using Helvetica fallback");
}
}
/// <summary>
/// 根据当前语言获取合适的字体 | Get appropriate font based on current language
/// zh-CN / zh-TW → 微软雅黑(支持简繁体);en-US → Arial
/// </summary>
/// <returns>PDF 字体 | PDF font</returns>
private PdfFont GetFontForCurrentLanguage()
{
// 延迟初始化字体 | Lazy initialize fonts
EnsureFontsInitialized();
var language = _localizationService.CurrentLanguage;
PdfFont selectedFont;
switch (language)
{
case SupportedLanguage.ZhCN:
case SupportedLanguage.ZhTW:
selectedFont = _cjkFont;
break;
case SupportedLanguage.EnUS:
default:
selectedFont = _westernFont ?? _cjkFont; // 西文优先,中文后备(微软雅黑也支持西文)| Western preferred, CJK fallback (YaHei supports Western too)
break;
}
// 后备字体逻辑 | Fallback font logic
if (selectedFont != null)
{
return selectedFont;
}
// 尝试使用其他字体 | Try other fonts
if (_cjkFont != null) return _cjkFont;
if (_westernFont != null) return _westernFont;
// 最终后备:使用 iText 内置 Helvetica | Final fallback: use built-in Helvetica
return PdfFontFactory.CreateFont(iText.IO.Font.Constants.StandardFonts.HELVETICA);
}
#endregion
#region 7.4 | Image embedding rendering
/// <summary>
/// 渲染图像元素 | Render image element
/// </summary>
private void RenderImageElement(Document document, LayoutElement element)
{
var imageData = element.ResolvedImage;
// 如果图像数据缺失,渲染占位矩形 | If image data is missing, render placeholder
if (imageData == null)
{
_logger.Warn("图像数据为空,渲染占位矩形 | Image data is null, rendering placeholder");
RenderImagePlaceholder(document, element);
return;
}
try
{
byte[] imageBytes = GetImageBytes(imageData);
if (imageBytes == null || imageBytes.Length == 0)
{
_logger.Warn("图像字节数据为空,渲染占位矩形 | Image byte data is empty, rendering placeholder");
RenderImagePlaceholder(document, element);
return;
}
// 创建 iText 图像对象 | Create iText image object
var iTextImageData = iText.IO.Image.ImageDataFactory.Create(imageBytes);
var image = new Image(iTextImageData);
// 计算目标区域尺寸(mm → points| Calculate target area size (mm → points)
float targetWidthPt = element.Width * MmToPoints;
float targetHeightPt = element.Height * MmToPoints;
// 等比缩放以适应目标区域 | Scale proportionally to fit target area
if (targetWidthPt > 0 && targetHeightPt > 0)
{
float imageWidth = image.GetImageWidth();
float imageHeight = image.GetImageHeight();
float scaleX = targetWidthPt / imageWidth;
float scaleY = targetHeightPt / imageHeight;
float scale = Math.Min(scaleX, scaleY);
image.SetWidth(imageWidth * scale);
image.SetHeight(imageHeight * scale);
}
else if (targetWidthPt > 0)
{
image.SetWidth(targetWidthPt);
image.ScaleToFit(targetWidthPt, float.MaxValue);
}
// 应用边框 | Apply border
if (element.Source?.Border == true)
{
image.SetBorder(new SolidBorder(ColorConstants.BLACK, 1f));
}
document.Add(image);
}
catch (Exception ex)
{
_logger.Warn("图像渲染失败,渲染占位矩形 | Image rendering failed, rendering placeholder: {Message}", ex.Message);
RenderImagePlaceholder(document, element);
}
}
/// <summary>
/// 从 ImageData 获取字节数组 | Get byte array from ImageData
/// </summary>
private byte[] GetImageBytes(ImageData imageData)
{
switch (imageData.SourceType)
{
case ImageSourceType.Bytes:
return imageData.Bytes;
case ImageSourceType.FilePath:
if (!string.IsNullOrEmpty(imageData.FilePath) && File.Exists(imageData.FilePath))
{
return File.ReadAllBytes(imageData.FilePath);
}
_logger.Warn("图像文件不存在:{Path} | Image file not found: {Path}", imageData.FilePath);
return null;
case ImageSourceType.BitmapSource:
if (imageData.BitmapSource is BitmapSource bitmapSource)
{
return ConvertBitmapSourceToBytes(bitmapSource);
}
_logger.Warn("BitmapSource 对象无效 | BitmapSource object is invalid");
return null;
default:
_logger.Warn("未知的图像来源类型:{Type} | Unknown image source type: {Type}", imageData.SourceType);
return null;
}
}
#endregion
#region 7.5 BitmapSource byte[] | BitmapSource to byte[] conversion
/// <summary>
/// 将 BitmapSource 转换为 PNG 编码的字节数组 | Convert BitmapSource to PNG-encoded byte array
/// </summary>
/// <param name="bitmapSource">WPF BitmapSource 对象 | WPF BitmapSource object</param>
/// <returns>PNG 编码的字节数组 | PNG-encoded byte array</returns>
public static byte[] ConvertBitmapSourceToBytes(BitmapSource bitmapSource)
{
if (bitmapSource == null)
{
return null;
}
using (var memoryStream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memoryStream);
return memoryStream.ToArray();
}
}
#endregion
#region 7.6 | Image placeholder rendering
/// <summary>
/// 渲染图像缺失时的占位矩形(带"无图像 | No Image"文本标签)
/// Render placeholder rectangle when image is missing (with "无图像 | No Image" text label)
/// </summary>
private void RenderImagePlaceholder(Document document, LayoutElement element)
{
float widthPt = element.Width > 0 ? element.Width * MmToPoints : 100f * MmToPoints;
float heightPt = element.Height > 0 ? element.Height * MmToPoints : 60f * MmToPoints;
// 使用表格模拟占位矩形(带边框和居中文本)| Use table to simulate placeholder rectangle
var table = new Table(1);
table.SetWidth(widthPt);
var cell = new Cell();
cell.SetHeight(heightPt);
cell.SetBorder(new SolidBorder(ColorConstants.GRAY, 1f));
cell.SetBackgroundColor(new DeviceRgb(245, 245, 245));
// 居中显示"无图像 | No Image"文本 | Center "无图像 | No Image" text
var placeholderText = new Paragraph("无图像 | No Image");
var font = GetFontForCurrentLanguage();
placeholderText.SetFont(font);
placeholderText.SetFontSize(10f);
placeholderText.SetFontColor(ColorConstants.GRAY);
placeholderText.SetTextAlignment(TextAlignment.CENTER);
cell.SetVerticalAlignment(VerticalAlignment.MIDDLE);
cell.Add(placeholderText);
table.AddCell(cell);
document.Add(table);
}
#endregion
#region 7.7 | Table rendering
/// <summary>
/// 渲染表格元素 | Render table element
/// </summary>
private void RenderTableElement(Document document, LayoutElement element)
{
var columns = element.Source?.Columns;
var tableData = element.ResolvedTableData;
if (columns == null || columns.Count == 0)
{
_logger.Warn("表格列定义为空,跳过渲染 | Table column definitions are empty, skipping");
return;
}
// 计算列宽(mm → points| Calculate column widths (mm → points)
var columnWidths = new float[columns.Count];
for (int i = 0; i < columns.Count; i++)
{
columnWidths[i] = columns[i].Width > 0 ? columns[i].Width * MmToPoints : 30f * MmToPoints;
}
var table = new Table(columnWidths);
table.SetWidth(UnitValue.CreatePercentValue(100));
var font = GetFontForCurrentLanguage();
// 渲染表头行 | Render header row
var headerBgColor = ParseColor(HeaderBackgroundColor);
foreach (var column in columns)
{
var headerCell = new Cell();
var headerParagraph = new Paragraph(column.Header ?? string.Empty);
headerParagraph.SetFont(font);
headerParagraph.SetFontSize(10f);
headerParagraph.SetBold();
headerParagraph.SetTextAlignment(ParseTextAlignment(column.Align));
headerCell.Add(headerParagraph);
headerCell.SetBackgroundColor(headerBgColor);
headerCell.SetBorder(new SolidBorder(ColorConstants.LIGHT_GRAY, 0.5f));
table.AddHeaderCell(headerCell);
}
// 渲染数据行(交替背景色)| Render data rows (alternating background colors)
if (tableData != null)
{
for (int rowIndex = 0; rowIndex < tableData.Count; rowIndex++)
{
var rowData = tableData[rowIndex];
var rowBgColor = rowIndex % 2 == 0
? ParseColor(OddRowBackgroundColor)
: ParseColor(EvenRowBackgroundColor);
foreach (var column in columns)
{
var dataCell = new Cell();
// 从行数据中获取字段值 | Get field value from row data
string cellValue = string.Empty;
if (rowData != null && !string.IsNullOrEmpty(column.Field) && rowData.ContainsKey(column.Field))
{
cellValue = rowData[column.Field]?.ToString() ?? string.Empty;
}
var cellParagraph = new Paragraph(cellValue);
cellParagraph.SetFont(font);
cellParagraph.SetFontSize(9f);
cellParagraph.SetTextAlignment(ParseTextAlignment(column.Align));
dataCell.Add(cellParagraph);
dataCell.SetBackgroundColor(rowBgColor);
dataCell.SetBorder(new SolidBorder(ColorConstants.LIGHT_GRAY, 0.5f));
table.AddCell(dataCell);
}
}
}
document.Add(table);
}
#endregion
#region 7.8 线 | Divider rendering
/// <summary>
/// 渲染分隔线元素 | Render divider element
/// </summary>
private void RenderDividerElement(Document document, LayoutElement element)
{
var style = element.ResolvedStyle;
// 确定分隔线颜色(从样式或默认灰色)| Determine divider color (from style or default gray)
Color lineColor = ColorConstants.GRAY;
if (style != null && !string.IsNullOrEmpty(style.Color))
{
var parsedColor = ParseColor(style.Color);
if (parsedColor != null)
{
lineColor = parsedColor;
}
}
// 使用 LineSeparator 渲染水平分隔线 | Use LineSeparator to render horizontal divider
var lineSeparator = new LineSeparator(new SolidLine(1f));
lineSeparator.SetStrokeColor(lineColor);
// 设置宽度为可用区域全宽 | Set width to full available area
if (element.Width > 0)
{
lineSeparator.SetWidth(element.Width * MmToPoints);
}
// 添加上下间距 | Add vertical spacing
lineSeparator.SetMarginTop(5f);
lineSeparator.SetMarginBottom(5f);
document.Add(lineSeparator);
}
#endregion
#region 7.9 PDF | PDF save to file
/// <summary>
/// 将 PDF 内存流保存到文件 | Save PDF memory stream to file
/// </summary>
/// <param name="pdfStream">PDF 内存流 | PDF memory stream</param>
/// <param name="filePath">输出文件路径 | Output file path</param>
/// <returns>保存结果(成功/失败)| Save result (success/failure)</returns>
public ReportResult SaveToFile(MemoryStream pdfStream, string filePath)
{
if (pdfStream == null)
{
return ReportResult.Failure("PDF 流为空,无法保存 | PDF stream is null, cannot save");
}
if (string.IsNullOrWhiteSpace(filePath))
{
return ReportResult.Failure("输出文件路径为空 | Output file path is empty");
}
try
{
_logger.Info("开始保存 PDF 到文件:{FilePath} | Saving PDF to file: {FilePath}", filePath);
// 确保目标目录存在 | Ensure target directory exists
var directory = System.IO.Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 保存流位置并重置 | Save stream position and reset
long originalPosition = pdfStream.Position;
pdfStream.Position = 0;
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
pdfStream.CopyTo(fileStream);
}
// 恢复流位置 | Restore stream position
pdfStream.Position = originalPosition;
_logger.Info("PDF 文件保存成功:{FilePath} | PDF file saved successfully: {FilePath}", filePath);
return ReportResult.Success(pdfStream);
}
catch (UnauthorizedAccessException ex)
{
_logger.Error(ex, "PDF 保存失败:无写入权限 | PDF save failed: no write permission: {Path}", filePath);
return ReportResult.Failure($"无法写入文件,权限不足:{filePath} | Cannot write file, insufficient permissions: {filePath}", ex);
}
catch (DirectoryNotFoundException ex)
{
_logger.Error(ex, "PDF 保存失败:目录不存在 | PDF save failed: directory not found: {Path}", filePath);
return ReportResult.Failure($"目标目录不存在:{filePath} | Target directory not found: {filePath}", ex);
}
catch (IOException ex)
{
_logger.Error(ex, "PDF 保存失败:IO 错误 | PDF save failed: IO error: {Path}", filePath);
return ReportResult.Failure($"文件保存 IO 错误:{ex.Message} | File save IO error: {ex.Message}", ex);
}
catch (Exception ex)
{
_logger.Error(ex, "PDF 保存失败:未知错误 | PDF save failed: unknown error: {Path}", filePath);
return ReportResult.Failure($"文件保存过程中发生错误:{ex.Message} | Error occurred during file save: {ex.Message}", ex);
}
}
#endregion
#region | Helper methods
/// <summary>
/// 解析十六进制颜色字符串为 iText Color 对象 | Parse hex color string to iText Color object
/// 支持格式:#RRGGBB 或 #RGB | Supports formats: #RRGGBB or #RGB
/// </summary>
/// <param name="hexColor">十六进制颜色字符串 | Hex color string</param>
/// <returns>iText Color 对象,解析失败返回黑色 | iText Color object, returns black on failure</returns>
private Color ParseColor(string hexColor)
{
if (string.IsNullOrEmpty(hexColor))
{
return ColorConstants.BLACK;
}
try
{
var hex = hexColor.TrimStart('#');
if (hex.Length == 3)
{
// 扩展 #RGB 为 #RRGGBB | Expand #RGB to #RRGGBB
hex = $"{hex[0]}{hex[0]}{hex[1]}{hex[1]}{hex[2]}{hex[2]}";
}
if (hex.Length == 6)
{
int r = Convert.ToInt32(hex.Substring(0, 2), 16);
int g = Convert.ToInt32(hex.Substring(2, 2), 16);
int b = Convert.ToInt32(hex.Substring(4, 2), 16);
return new DeviceRgb(r, g, b);
}
}
catch (Exception ex)
{
_logger.Warn("颜色解析失败:{Color},使用默认黑色 | Color parsing failed: {Color}, using default black: {Message}", hexColor, ex.Message);
}
return ColorConstants.BLACK;
}
/// <summary>
/// 解析对齐方式字符串为 iText TextAlignment | Parse alignment string to iText TextAlignment
/// </summary>
/// <param name="align">对齐方式字符串(left/center/right| Alignment string</param>
/// <returns>iText TextAlignment 枚举值 | iText TextAlignment enum value</returns>
private TextAlignment ParseTextAlignment(string align)
{
switch (align?.ToLowerInvariant())
{
case "center":
return TextAlignment.CENTER;
case "right":
return TextAlignment.RIGHT;
case "justify":
return TextAlignment.JUSTIFIED;
case "left":
default:
return TextAlignment.LEFT;
}
}
#endregion
}
}