【問題記錄】- 國產作業系統適配問題記錄
一、起因
由於國產化系統逐步的推廣應用,需要將在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 isnot 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(); }