由于国产化系统逐步的推广应用,需要将在window系统中实现的功能;迁移到国产系统(UOS(统信OS)、麒麟操作系统等)中运行。
在windows环境中主要采用Framework4.0开发的后台运行程序。主体思路采用将Windows程序功能迁移成Linux系统后台服务运行。
特记录在适配过程中相关问题记录
二、问题记录1、System.Draw.Common包使用问题:
由于后台服务需要生成pdf报告功能;需要用到gdi相关类型:如Font、Pen、Image、Bitmap等类型;在跨平台的处理中微软提供了:System.Draw.Common包在跨平台中使用。
注意点:
a)System.Draw.Common在linux环境中依赖gdip包;需要在环境中安装gdip包:
//安装libgdiplus: apt-get install -y libgdiplus
b)在.NET6以后System.Draw.Common被归为 Windows 特定的库。 在为非 Windows 操作系统编译时,平台分析器会在编译时发出警告;运行时会出现以下异常:
System.TypeInitializationException : The type initializer for 'Gdip' threw an exception. ---- System.PlatformNotSupportedException : System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information.
更改原因:
由于
System.Drawing.Common
被设计为 Windows 技术的精简包装器,因此其跨平台实现欠佳。
libgdiplus
是本机端System.Drawing.Common
跨平台实现的主要提供程序。libgdiplus
实际上是对System.Drawing.Common
所依赖的 Windows 部分的重新实现。 该实现使libgdiplus
成为一个重要的组件。 它大约有 30,000 行 C 代码,大部分未经测试,而且缺少很多功能。libgdiplus
还具有许多用于图像处理和文本呈现的外部依赖项,例如cairo
、pango
和其他本机库。 这些依赖项使得维护和交付组件更具挑战性。 自从包含 Mono 跨平台实现以来,我们已将许多从未得到修复的问题重定向到libgdiplus
。 相比之下,我们采用的其他外部依赖项,例如icu
或openssl
,都是高质量的库。 使libgdiplus
的功能集和质量与 .NET 堆栈的其余部分相媲美是不可行的。通过对 NuGet 包的分析,我们观察到
System.Drawing.Common
主要用于跨平台的图像处理,例如 QR 代码生成器和文本呈现。 由于我们的跨平台图形支持不完整,我们还没有注意到大量的图形使用。System.Drawing.Common
在非 Windows 环境中的使用通常得到 SkiaSharp 和 ImageSharp 的良好支持。
System.Drawing.Common
将仅在 Windows 窗体和 GDI+ 的上下文中继续演变。
解决办法:可通过将 runtimeconfig.json 文件中的 System.Drawing.EnableUnixSupport
System.Drawing.EnableUnixSupport
设置为 true
来启用对 .NET 6 中非 Windows 平台的支持
{ "configProperties": { "System.Drawing.EnableUnixSupport": true } }
可以在项目中添加:runtimeconfig.template.json 设置以上内容;编译时会自动添加到对于配置文件中。
2、打印机信息获取:
在国产系统中打印机管理主要使用CUPS管理打印机:
a)安装CUPS:(如果未安装改服务)
sudo apt-get update && sudo apt-get install cups cups-client lpr
b) 获取打印机列表:安装后可以使用以下命令获取打印机列表
//输出打印机信息 lpstat -p //输出信息: 打印机 HP-Color-LaserJet-MFP-M281fdw 目前空闲。从 2022年03月15日 星期二 17时49分10秒 开始启用 打印机 PDF 目前空闲。从 2022年03月17日 星期四 19时09分47秒 开始启用
c)获取打印机基本信息:如纸张信息、纸张来源、双面打印等
//打印机相关信息 lpoptions -p HP-Color-LaserJet-MFP-M281fdw -l //输出信息 //纸张信息 PageSize/Media Size: *Letter Legal Executive FanFoldGermanLegal 4x6 5x8 A4 A5 A6 B5 B6 102x152mm Oficio 195x270mm 184x260mm 7.75x10.75 Postcard DoublePostcardRotated Env10 EnvMonarch EnvISOB5 EnvC5 EnvDL Custom.WIDTHxHEIGHT //双面打印 Collate/Collate: True False //双面打印设置 Duplex/Two-Sided: *None DuplexNoTumble DuplexTumble //纸张来源 InputSlot/Paper Feed: *Auto Tray1 Tray2 ManualFeed HPOption_Duplexer/Duplex Unit: True *False MediaType/Media Type: *Unspecified Plain HPEcoFFICIENT HPLaserJet90 HPColorLaserMatte105 HPPremiumChoiceMatte120 HPBrochureMatte150 HPCoverMatte200 HPMattePhoto200 HPPremiumPresentationGlossy120 HPBrochureGlossy150 HPTrifoldBrochureGlossy150 HPBrochureGlossy200 Light6074 Intermediate8595 MidWeight96110 Heavy111130 ExtraHeavy131175 HeavyGlossy111130 ExtraHeavyGlossy131175 CardGlossy176220 ColorLaserTransparency Labels Letterhead Envelope HeavyEnvelope Preprinted Prepunched Colored Bond Recycled Rough HeavyRough OpaqueFilm HPPJLColorAsGray/Print Color as Gray: True *False HPRGBEmulation/RGB Color: *DefaultSRGB PhotoSRGB Adobe VividSRGB HPRGBEmulationNone HPEdgeControl/Edge Control: HPEdgeControlOff Light *Normal Max HPGeneralHalftone/Halftone: *Smooth Detail HPTextNeutralGrays/Text Neutral Grays: *Black ProcessBlack HPGraphicsNeutralGrays/Graphics Neutral Grays: *Black ProcessBlack HPPhotoNeutralGrays/Photo Neutral Grays: Black *ProcessBlack
有了前面的命令就可以封装获取打印机名称及打印机信息的方法了:
//命令执行帮助类 public static class ShellHelper { public static string RunCommand(string? command, string? args) { if (string.IsNullOrWhiteSpace(command)) return string.Empty; var process = new Process() { StartInfo = new ProcessStartInfo { FileName = command, Arguments = args, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, } }; process.Start(); string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (string.IsNullOrEmpty(error)) { return output; } else { return error; } } }
获取打印机名称:
public static object GetAllPrinterNames(ILogger? logger = null) { object result = null; if (printerCache.TryGetValue(PrinterName, out result) && (result is PrinterSettings.StringCollection && (result as PrinterSettings.StringCollection).Count == PrinterSettings.InstalledPrinters.Count)) { result = printerCache[PrinterName]; } else { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { string cmdResult = ShellHelper.RunCommand("lpstat", "-p"); string[] strArray = cmdResult.Split('\n'); List<string> strList = new List<string>(); int index1, index2; for (int i = 0; i < strArray.Length; i++) { index1 = strArray[i].IndexOf(' '); if (index1 <= 0) continue; index2 = strArray[i].IndexOf(' ', index1 + 1); if (index2 <= 0) continue; strList.Add(strArray[i].Substring(index1 + 1, index2 - index1 - 1)); } //获取打印机信息 result = strList; } else { result = PrinterSettings.InstalledPrinters; } printerCache[PrinterName] = result; } return result; }
获取打印机信息:需注意不同打印机获取出来的设置信息不同,需要获取通用的打印机信息:纸张来源、纸张、双面打印等
public static List<PrinterInfo> GetAllPrinters(ILogger? logger = null) { List<PrinterInfo> result = null; if (printerCache.TryGetValue(PrinterInfo, out object objresult) && (objresult is List<PrinterInfo> && (objresult as List<PrinterInfo>).Count == PrinterSettings.InstalledPrinters.Count)) { result = objresult as List<PrinterInfo>; } else { var resultPrinterInfos = new List<PrinterInfo>(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { string cmdResult = ShellHelper.RunCommand("lpstat", "-p"); string[] strArray = cmdResult.Split('\n'); List<string> strList = new List<string>(); int index1, index2; for (int i = 0; i < strArray.Length; i++) { index1 = strArray[i].IndexOf(' '); if (index1 <= 0) continue; index2 = strArray[i].IndexOf(' ', index1 + 1); if (index2 <= 0) continue; strList.Add(strArray[i].Substring(index1 + 1, index2 - index1 - 1)); } //获取打印机信息 foreach (var item in strList) { cmdResult = ShellHelper.RunCommand("lpoptions", $"-p \"{item}\" -l"); string[] strArray2 = cmdResult.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); /* PageSize/Media Size: *Letter Legal Executive FanFoldGermanLegal 4x6 5x8 A4 A5 A6 B5 B6 102x152mm Oficio 195x270mm 184x260mm 7.75x10.75 Postcard DoublePostcardRotated Env10 EnvMonarch EnvISOB5 EnvC5 EnvDL Custom.WIDTHxHEIGHT Collate/Collate: True False Duplex/Two-Sided: *None DuplexNoTumble DuplexTumble InputSlot/Paper Feed: *Auto Tray1 Tray2 ManualFeed HPOption_Duplexer/Duplex Unit: True *False MediaType/Media Type: *Unspecified Plain HPEcoFFICIENT HPLaserJet90 HPColorLaserMatte105 HPPremiumChoiceMatte120 HPBrochureMatte150 HPCoverMatte200 HPMattePhoto200 HPPremiumPresentationGlossy120 HPBrochureGlossy150 HPTrifoldBrochureGlossy150 HPBrochureGlossy200 Light6074 Intermediate8595 MidWeight96110 Heavy111130 ExtraHeavy131175 HeavyGlossy111130 ExtraHeavyGlossy131175 CardGlossy176220 ColorLaserTransparency Labels Letterhead Envelope HeavyEnvelope Preprinted Prepunched Colored Bond Recycled Rough HeavyRough OpaqueFilm HPPJLColorAsGray/Print Color as Gray: True *False HPRGBEmulation/RGB Color: *DefaultSRGB PhotoSRGB Adobe VividSRGB HPRGBEmulationNone HPEdgeControl/Edge Control: HPEdgeControlOff Light *Normal Max HPGeneralHalftone/Halftone: *Smooth Detail HPTextNeutralGrays/Text Neutral Grays: *Black ProcessBlack HPGraphicsNeutralGrays/Graphics Neutral Grays: *Black ProcessBlack HPPhotoNeutralGrays/Photo Neutral Grays: Black *ProcessBlack */ PrinterInfo pinfo = new PrinterInfo(); pinfo.name = item; pinfo.color = true; pinfo.isvalid = true; foreach (var opItem in strArray2) { string[] kvArr = opItem.Split(":".ToArray(), StringSplitOptions.RemoveEmptyEntries); if (kvArr.Length == 2) { string key = kvArr.First(); string[] values = kvArr.Last().Split(" ".ToArray(), StringSplitOptions.RemoveEmptyEntries); int nIndex = key.IndexOf('/'); if (nIndex > 0) key = key.Substring(0, nIndex);//todo:判断规则可能只取/前面的部分 switch (key) { //纸张 case "PageSize": // /Media Size /Page Size List<PaperSize> psList = new List<PaperSize>(); foreach (var ps in values) { PaperSize paperSize = new PaperSize(); paperSize.PaperName = ps.Trim('*'); psList.Add(paperSize); } pinfo.paper = new PaperSizeCollection(psList.ToArray()); break; //纸张来源 case "InputSlot": // /Paper Feed /Media Source List<PaperSource> psourceList = new List<PaperSource>(); for (int i = 0; i < values.Length; i++) { string ps = values[i]; PaperSource pSource = new PaperSource(); pSource.SourceName = ps.Trim('*'); pSource.RawKind = i; psourceList.Add(pSource); } pinfo.papersource = new PaperSourceCollection(psourceList.ToArray()); break; //双面打印 //Duplex/Two-Sided: *None DuplexNoTumble DuplexTumble case "Duplex": // /Two-Sided /Double-Sided Printing for (int i = 0; i < values.Length; i++) { string ps = values[i]; if (ps.Contains("*")) { pinfo.duplex = i; break; } } pinfo.canduplex = values.Length > 1; break; } } } if (pinfo.paper == null) { pinfo.paper = new PaperSizeCollection(new PaperSize[0]); } if (pinfo.papersource == null) { pinfo.papersource = new PaperSourceCollection(new PaperSource[0]); } resultPrinterInfos.Add(pinfo); } } else { var printerNames = PrinterSettings.InstalledPrinters; if (printerNames.Count > 0) { foreach (string printName in printerNames) { try { PrinterSettings ps = new PrinterSettings(); ps.PrinterName = printName; if (ps.IsValid) { PrinterSettings.PaperSourceCollection psSources = ps.PaperSources; resultPrinterInfos.Add(new PrinterInfo( printName, ps.PaperSources, ps.PaperSizes, ps.SupportsColor, (int)ps.Duplex, ps.IsDefaultPrinter, ps.IsValid, ps.CanDuplex )); } } catch (Exception ex) { logger?.LogError(ex, $"获取打印机【{printName}】信息失败-请检查打印机是否正常访问"); } } } } result = resultPrinterInfos; printerCache[PrinterInfo] = result; } return result; }
3、打印文件:
前面解决获取打印相关信息,接下来需要解决打印文档的问题:
/// <summary> /// 打印内容 /// </summary> /// <param name="pdfBytes">打印内容byte数组</param> /// <param name="psi"></param> public static void Print(byte[] pdfBytes, PrintSettings psi) { string tmpFileName = Path.GetTempFileName(); try { File.WriteAllBytes(tmpFileName, pdfBytes); ShellHelper.RunCommand("lp", BuildLpArgs(tmpFileName, psi)); } finally { try { File.Delete(tmpFileName); } catch { } } } /// <summary> /// 构建打印参数 /// </summary> /// <param name="filePath"></param> /// <param name="psi"></param> /// <returns></returns> private static string BuildLpArgs(string filePath, PrintSettings psi) { StringBuilder str = new StringBuilder(); if (psi != null && psi.PrinterSetting != null) { //打印机名称 if (!string.IsNullOrWhiteSpace(psi.PrinterSetting.PrinterName)) { str.Append($"-d {psi.PrinterSetting.PrinterName} "); } //双面打印 if (psi.PrinterSetting.Duplex != Duplex.Default) { switch (psi.PrinterSetting.Duplex) { case Duplex.Simplex: case Duplex.Vertical: str.Append($"-o sides=two-sided-long-edge "); break; case Duplex.Horizontal: str.Append($"-o sides=two-sided-short-edge "); break; } } //打印份数 if (psi.PrinterSetting.Copies > 1) { str.Append($"-n {psi.PrinterSetting.Copies} "); } } if (psi != null && psi.PaperSetting != null) {//纸张名称 if (!string.IsNullOrWhiteSpace(psi.PaperSetting.PaperName)) { str.Append($"-o media={psi.PaperSetting.PaperName} "); /* 全部模板及尺寸如下: Letter- US Letter (8.5×11 inches, or 216x279mm) Legal- US Legal (8.5×14 inches, or 216x356mm) A4- ISO A4 (8.27×11.69 inches, or 210x297mm) COM10- US #10 Envelope (9.5×4.125 inches, or 241x105mm) DL- ISO DL Envelope (8.66×4.33 inches, or 220x110mm) Transparency- Transparency media type or source Upper- Upper paper tray Lower- Lower paper tray MultiPurpose- Multi-purpose paper tray LargeCapacity- Large capacity paper tray 也可以自定义尺寸,比如想输出照片6寸(15.2cmx10.2cm) 只需要输入指令lp -o media=Custom.15.2×10.2cm filename */ } //是否横行 if (psi.PaperSetting.Landscape == 1) { str.Append($"-o landscape "); } } str.Append(filePath); return str.ToString(); }三、参考
仅在 Windows 上支持 System.Drawing.Common
lp操作命令说明