Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

adding text decorations to console output

I have a c# .net 3.5 application that writes text to the console using a StreamWriter. Is there a way I can add text decorations like underline and strikethrough to the text that is printed to the console? Possibly using ANSI escape sequences?

TextWriter writer = new StreamWriter(Console.OpenStandardOutput());
writer.WriteLine("some underlined text");

Thanks, PaulH

like image 589
PaulH Avatar asked Mar 08 '11 20:03

PaulH


4 Answers

I found this question and thought I would add to the answers for pre-windows 10 terminal using kernel32 functions,

using System;
using System.Runtime.InteropServices;

namespace color_console
{
    class Class1
    {
        static void Main(string[] args)
        {
            Class1 c = new Class1();
            c.change();
        }

        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, CharacterAttributes wAttributes);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll")]
        public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput,
                ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);


        void change()
        {
            const int STD_OUTPUT_HANDLE = -11;
            IntPtr hOut;
            hOut = GetStdHandle(STD_OUTPUT_HANDLE);

            CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo = new CONSOLE_SCREEN_BUFFER_INFO();
            GetConsoleScreenBufferInfo(hOut, ref ConsoleInfo);
            CharacterAttributes originalAttributes = (CharacterAttributes)ConsoleInfo.wAttributes;

            //write some text
            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_BLUE);
            Console.WriteLine("Blue text");

            SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_RED);
            Console.WriteLine("Red background");

            SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN);
            Console.WriteLine("Green background");

            SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN | CharacterAttributes.BACKGROUND_RED);
            Console.WriteLine("Yellow background");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED | CharacterAttributes.COMMON_LVB_UNDERSCORE);
            Console.WriteLine("Red underlined text");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
                                   | CharacterAttributes.FOREGROUND_BLUE);
            Console.WriteLine("Purple text");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
                                    | CharacterAttributes.FOREGROUND_BLUE
                                    | CharacterAttributes.FOREGROUND_INTENSITY);
            Console.WriteLine("Purple text intense");
           
            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
                                    | CharacterAttributes.FOREGROUND_BLUE
                                    | CharacterAttributes.COMMON_LVB_REVERSE_VIDEO);
            Console.WriteLine("Aqua reversed text ");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
                                    | CharacterAttributes.FOREGROUND_BLUE
                                    | CharacterAttributes.COMMON_LVB_REVERSE_VIDEO
                                    | CharacterAttributes.FOREGROUND_INTENSITY);
            Console.WriteLine("Aqua reversed intense text ");

            SetConsoleTextAttribute(hOut, CharacterAttributes.COMMON_LVB_GRID_LVERTICAL
                                    | CharacterAttributes.FOREGROUND_GREEN);
            Console.WriteLine("What does this do");

            SetConsoleTextAttribute(hOut, originalAttributes);
            Console.WriteLine("Back to the shire");
        }

        public enum CharacterAttributes
        {
            FOREGROUND_BLUE =   0x0001,
            FOREGROUND_GREEN =  0x0002,
            FOREGROUND_RED =    0x0004,
            FOREGROUND_INTENSITY = 0x0008,
            BACKGROUND_BLUE = 0x0010,
            BACKGROUND_GREEN = 0x0020,
            BACKGROUND_RED = 0x0040,
            BACKGROUND_INTENSITY = 0x0080,
            COMMON_LVB_LEADING_BYTE = 0x0100,
            COMMON_LVB_TRAILING_BYTE = 0x0200,
            COMMON_LVB_GRID_HORIZONTAL = 0x0400,
            COMMON_LVB_GRID_LVERTICAL = 0x0800,
            COMMON_LVB_GRID_RVERTICAL = 0x1000,
            COMMON_LVB_REVERSE_VIDEO = 0x4000,
            COMMON_LVB_UNDERSCORE = 0x8000
        }

 

        [StructLayout(LayoutKind.Sequential)]
        public struct CONSOLE_SCREEN_BUFFER_INFO
        {
            public COORD dwSize;
            public COORD dwCursorPosition;
            public int wAttributes;
            public SMALL_RECT srWindow;
            public COORD dwMaximumWindowSize;
        }

 

        // Standard structures used for interop with kernel32
        [StructLayout(LayoutKind.Sequential)]
        public struct COORD
        {
            public short x;
            public short y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SMALL_RECT

        {
            public short Left;
            public short Top;
            public short Right;
            public short Bottom;
        }
    }
}

Output on my PC

like image 123
David Straight Avatar answered Oct 19 '22 22:10

David Straight


I use this code. It's a fixed version of Vladimir Reshetnikov's answer, using the correct escape code for the reset.

    private static void WriteUnderline(string s)
    {
        var handle = GetStdHandle(STD_OUTPUT_HANDLE);
        uint mode;
        GetConsoleMode(handle, out mode);
        mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(handle, mode);
        Console.WriteLine($"\x1B[4m{s}\x1B[24m");
    }

This will do underlined text, and has the benefit of not resetting any colors you have set.

like image 26
Kieran Foot Avatar answered Oct 19 '22 23:10

Kieran Foot


The Windows console does not support ANSI escape sequences. To my knowledge, the only way to change the attributes of an output character is to call SetConsoleTextAttribute before writing the character. Or, in .NET, modify the Console.ForegroundColor or Console.BackgroundColor attributes.

It might be possible to set those properties to custom values (i.e. values not defined by ConsoleColor) with a type cast. But I don't know what good that would do you.

I don't know that I've ever seen strikethrough text on a Windows console, and it's been years since I saw underline. I suppose it's possible, but I don't know how it's done.

like image 34
Jim Mischel Avatar answered Oct 20 '22 00:10

Jim Mischel


Short answer, no; the console doesn't allow the use of underlined characters in output.

Longer answer: The screen buffer used by the console is little more than a byte array. Each cursor position is one byte or one character. To create an underline, you either need two characters overlapping (which isn't possible in the console), or you need access to a codepage that uses the upper 128 character values as underlined or strikethrough versions of the lower 128 (I don't know of one).

You can work around this if you are willing to go "double-spaced" for lines that have underlines. Character code 0x00AF (decimal 175) is a "text art" character representing a border across the top of the character space. If you use those in the line underneath your text, presto, underlines.

like image 38
KeithS Avatar answered Oct 19 '22 22:10

KeithS