/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/out-printer/PrinterLineOutput.cs
344 строки
12 KB
Dongbo Wang
Update `Out-Printer` to remove all decorating ANSI escape sequences from PowerShell formatting (#18425)
03 ноя 2022, 01:42
Не верифицирован
03 ноя 2022, 01:42
f2e0640
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.Management.Automation; using System.Management.Automation.Internal; namespace Microsoft.PowerShell.Commands.Internal.Format { /// <summary> /// Implementation of the LineOutput interface for printer. /// </summary> internal sealed class PrinterLineOutput : LineOutput { #region LineOutput implementation /// <summary> /// Full buffering for printer. /// </summary> internal override bool RequiresBuffering { get { return true; } } /// <summary> /// Do the printing on playback. /// </summary> internal override void ExecuteBufferPlayBack(DoPlayBackCall playback) { _playbackCall = playback; DoPrint(); } /// <summary> /// The # of columns for the printer. /// </summary> /// <value></value> internal override int ColumnNumber { get { CheckStopProcessing(); return _deviceColumns; } } /// <summary> /// The # of rows for the printer. /// </summary> /// <value></value> internal override int RowNumber { get { CheckStopProcessing(); return _deviceRows; } } /// <summary> /// Write a line to the output device. /// </summary> /// <param name="s">Line to write.</param> internal override void WriteLine(string s) { CheckStopProcessing(); // Remove all ANSI escape sequences before sending out to the printer. s = new ValueStringDecorated(s).ToString(OutputRendering.PlainText); WriteRawText(s); } /// <summary> /// Write a raw text by delegating to the writer underneath, with no change to the text. /// For example, keeping VT escape sequences intact in it. /// </summary> /// <param name="s">The raw text to be written to the device.</param> internal override void WriteRawText(string s) { CheckStopProcessing(); // Delegate the action to the helper, that will properly break the string into screen lines. _writeLineHelper.WriteLine(s, ColumnNumber); } #endregion /// <summary> /// Initializes static members of the <see cref="PrinterLineOutput"/> class. /// Used for static initializations like DefaultPrintFontName. /// </summary> static PrinterLineOutput() { // This default must be loaded from a resource file as different // cultures will have different defaults and the localizer would // know the default for different cultures. s_defaultPrintFontName = OutPrinterDisplayStrings.DefaultPrintFontName; } /// <summary> /// Initializes a new instance of the <see cref="PrinterLineOutput"/> class. /// </summary> /// <param name="printerName">Name of printer, if null use default printer.</param> internal PrinterLineOutput(string printerName) { _printerName = printerName; // instantiate the helper to do the line processing when LineOutput.WriteXXX() is called WriteLineHelper.WriteCallback wl = new(this.OnWriteLine); WriteLineHelper.WriteCallback w = new(this.OnWrite); _writeLineHelper = new WriteLineHelper(true, wl, w, this.DisplayCells); } /// <summary> /// Callback to be called when IConsole.WriteLine() is called by WriteLineHelper. /// </summary> /// <param name="s">String to write.</param> private void OnWriteLine(string s) { _lines.Enqueue(s); } /// <summary> /// Callback to be called when Console.Write() is called by WriteLineHelper. /// This is called when the WriteLineHelper needs to write a line whose length /// is the same as the width of the screen buffer. /// </summary> /// <param name="s">String to write.</param> private void OnWrite(string s) { _lines.Enqueue(s); } /// <summary> /// Do the printing. /// </summary> private void DoPrint() { try { // create a new print document object and set the printer name, if available PrintDocument pd = new(); if (!string.IsNullOrEmpty(_printerName)) { pd.PrinterSettings.PrinterName = _printerName; } // set up the callback mechanism pd.PrintPage += this.pd_PrintPage; // start printing pd.Print(); } finally { // make sure we do not leak the font if (_printFont != null) { _printFont.Dispose(); _printFont = null; } } } /// <summary> /// Helper to create a font. /// If the font object exists, it does nothing. /// Else, the a new object is created and verified. /// </summary> /// <param name="g">GDI+ graphics object needed for verification.</param> private void CreateFont(Graphics g) { if (_printFont != null) return; // create the font // do we have a specified font? if (string.IsNullOrEmpty(_printFontName)) { _printFontName = s_defaultPrintFontName; } if (_printFontSize <= 0) { _printFontSize = DefaultPrintFontSize; } _printFont = new Font(_printFontName, _printFontSize); VerifyFont(g); } /// <summary> /// Internal helper to verify that the font is fixed pitch. If the test fails, /// it reverts to the default font. /// </summary> /// <param name="g">GDI+ graphics object needed for verification.</param> private void VerifyFont(Graphics g) { // check if the font is fixed pitch // HEURISTICS: // we compute the length of two strings, one made of "large" characters // one made of "narrow" ones. If they are the same length, we assume that // the font is fixed pitch. const string large = "ABCDEF"; float wLarge = g.MeasureString(large, _printFont).Width / large.Length; const string narrow = ".;'}l|"; float wNarrow = g.MeasureString(narrow, _printFont).Width / narrow.Length; if (Math.Abs((float)(wLarge - wNarrow)) < 0.001F) { // we passed the test return; } // just get back to the default, since it's not fixed pitch _printFont.Dispose(); _printFont = new Font(s_defaultPrintFontName, DefaultPrintFontSize); } /// <summary> /// Event fired for each page to print. /// </summary> /// <param name="sender">Sender, not used.</param> /// <param name="ev">Print page event.</param> private void pd_PrintPage(object sender, PrintPageEventArgs ev) { float yPos = 0; // GDI+ coordinate down the page int linesPrinted = 0; // linesPrinted float leftMargin = ev.MarginBounds.Left; float topMargin = ev.MarginBounds.Top; CreateFont(ev.Graphics); // compute the height of a line of text float lineHeight = _printFont.GetHeight(ev.Graphics); // Work out the number of lines per page // Use the MarginBounds on the event to do this float linesPerPage = ev.MarginBounds.Height / _printFont.GetHeight(ev.Graphics); if (!_printingInitialized) { // on the first page we have to initialize the metrics for LineOutput // work out the number of columns per page assuming fixed pitch font const string s = "ABCDEF"; float w = ev.Graphics.MeasureString(s, _printFont).Width / s.Length; float columnsPerPage = ev.MarginBounds.Width / w; _printingInitialized = true; _deviceRows = (int)linesPerPage; _deviceColumns = (int)columnsPerPage; // now that we initialized the column and row count for the LineOutput // interface we can tell the outputter to playback from cache to do the // proper computations of line widths // returning from this call, the string queue on this object is full of // lines of text to print _playbackCall(); } // now iterate over the file printing out each line while ((linesPrinted < linesPerPage) && (_lines.Count > 0)) { // get the string to be printed string line = _lines.Dequeue(); // compute the Y position where to draw yPos = topMargin + (linesPrinted * lineHeight); // do the actual drawing ev.Graphics.DrawString(line, _printFont, Brushes.Black, leftMargin, yPos, new StringFormat()); linesPrinted++; } // If we have more lines then print another page ev.HasMorePages = _lines.Count > 0; } /// <summary> /// Flag for one-time initialization of the interface (columns, etc.). /// </summary> private bool _printingInitialized = false; /// <summary> /// Callback to ask the outputter to playback its cache. /// </summary> private DoPlayBackCall _playbackCall; /// <summary> /// Name of the printer to print to. Null means default printer. /// </summary> private readonly string _printerName = null; /// <summary> /// Name of the font to use, if null the default is used. /// </summary> private string _printFontName = null; /// <summary> /// Font size. /// </summary> private int _printFontSize = 0; /// <summary> /// Default font, used if the printFont is not specified or if the /// printFont is not fixed pitch. /// </summary> /// <remarks> /// This default must be loaded from a resource file as different /// cultures will have different defaults and the localizer would /// know the default for different cultures. /// </remarks> private static readonly string s_defaultPrintFontName; /// <summary> /// Default size for the default font. /// </summary> private const int DefaultPrintFontSize = 8; /// <summary> /// Number of columns on the sheet. /// </summary> private int _deviceColumns = 80; // number of rows per sheet private int _deviceRows = 40; /// <summary> /// Text lines ready to print (after output cache playback). /// </summary> private readonly Queue<string> _lines = new(); /// <summary> /// Cached font object. /// </summary> private Font _printFont = null; private readonly WriteLineHelper _writeLineHelper; } }