• 【问题记录】 国产操作系统适配问题记录


    一、起因

     由于国产化系统逐步的推广应用,需要将在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 还具有许多用于图像处理和文本呈现的外部依赖项,例如 cairopango 和其他本机库。 这些依赖项使得维护和交付组件更具挑战性。 自从包含 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.EnableUnixSupportSystem.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操作命令说明

  • 相关阅读:
    Android开发环境
    安卓学习
    Shuffle'm Up POJ
    Duizi and Shunzi HDU
    Find a path HDU
    Cyclic Nacklace HDU
    Keywords Search HDU
    HDU 1495 非常可乐
    J
    Fire Game FZU
  • 原文地址:https://www.cnblogs.com/cwsheng/p/16029742.html
Copyright © 2020-2023  润新知