Colorized capture of console screen in HTML and RTF.

Colorized capture of console screen in HTML and RTF.

  • Comments 7

In the previous post we demonstrated how we can use console host API to capture screen buffer contents as text. But what if we want some colors. Would not it be nice to publish colorized copy of console in HTML or insert it as part of Microsoft Word document. For this to work we need to add some modifications to original script. Colors of each character are available as properties of System.Management.Automation.Host.BufferCell object:

PS E:\MyScripts> $bufferWidth = $host.ui.rawui.BufferSize.Width                                                         
PS E:\MyScripts> $bufferHeight = $host.ui.rawui.CursorPosition.Y
PS E:\MyScripts> $rec = new-object System.Management.Automation.Host.Rectangle 0,0,($bufferWidth - 1),$bufferHeight
PS E:\MyScripts> $buffer = $host.ui.rawui.GetBufferContents($rec)
PS E:\MyScripts> $buffer[1,1]

Character ForegroundColor BackgroundColor BufferCellType
--------- --------------- --------------- --------------
o DarkYellow DarkMagenta Complete


PS E:\MyScripts>

All we need to do is to iterate through the screen buffer array, keeping track of the cell colors and generate HTML spans or RTF blocks with varying color attributes as soon as they change.

Why implementing both formats? While HTML is sufficient for Web applications, we will get bad results if we will try to use it in word processing publications. MS Word is much better at pasting and rendering RTF, rather than HTML. The e-mail editor in Microsoft Outlook also produce better results with RTF. By implementing console capture in both formats we will cover much bigger range of applications.

Usage of the scripts is fairly easy. The following example demonstrates how to put both scripts to a quick test:

Windows PowerShell V2 (Community Technology Preview - Features Subject to Change)                                       
Copyright (C) 2008 Microsoft Corporation. All rights reserved.

PS C:\Users\Vladimir> cd E:\MyScripts
PS E:\MyScripts> $htmlFileName = "$env:temp\ConsoleBuffer.html"
PS E:\MyScripts> .\Get-ConsoleAsHtml | out-file $htmlFileName -encoding UTF8
PS E:\MyScripts> $null = [System.Diagnostics.Process]::Start("$htmlFileName")
PS E:\MyScripts>
PS E:\MyScripts>
PS E:\MyScripts> $rtfFileName = "$env:temp\test.rtf"
PS E:\MyScripts> .\Get-ConsoleAsRTF | out-file $rtfFileName -encoding ascii
PS E:\MyScripts> $null = [System.Diagnostics.Process]::Start("$rtfFileName")
PS E:\MyScripts>

Needless to say, the scripts can be further modified to include configurable parameters such as font name and size. The purpose here is to demonstrate the basic techniques for automated generation of simple HTML and RTF documents.

Hope you will find it useful,
Vladimir Averkin
Windows PowerShell Team 

############################################################################################################
# Get-ConsoleAsHtml.ps1
#
# The script captures console screen buffer up to the current cursor position and returns it in HTML format.
#
# Returns: UTF8-encoded string.
#
# Example:
#
# $htmlFileName = "$env:temp\ConsoleBuffer.html"
# .\Get-ConsoleAsHtml | out-file $htmlFileName -encoding UTF8
# $null = [System.Diagnostics.Process]::Start("$htmlFileName")
#

# Check the host name and exit if the host is not the Windows PowerShell console host.
if ($host.Name -ne 'ConsoleHost')
{
  write-host -ForegroundColor Red "This script runs only in the console host. You cannot run this script in $($host.Name)."
  exit -1
}

# The Windows PowerShell console host redefines DarkYellow and DarkMagenta colors and uses them as defaults.
# The redefined colors do not correspond to the color names used in HTML, so they need to be mapped to digital color codes.
#
function Normalize-HtmlColor ($color)
{
  if ($color -eq "DarkYellow") { $color = "#eeedf0" }
  if ($color -eq "DarkMagenta") { $color = "#012456" }
  return $color
}

# Create an HTML span from text using the named console colors.
#
function Make-HtmlSpan ($text, $forecolor = "DarkYellow", $backcolor = "DarkMagenta")
{
  $forecolor = Normalize-HtmlColor $forecolor
  $backcolor = Normalize-HtmlColor $backcolor

  # You can also add font-weight:bold tag here if you want a bold font in output.
  return "<span style='font-family:Courier New;color:$forecolor;background:$backcolor'>$text</span>"
}

# Generate an HTML span and append it to HTML string builder
#
function Append-HtmlSpan
{
  $spanText = $spanBuilder.ToString()
  $spanHtml = Make-HtmlSpan $spanText $currentForegroundColor $currentBackgroundColor
  $null = $htmlBuilder.Append($spanHtml)
}

# Append line break to HTML builder
#
function Append-HtmlBreak
{
  $null = $htmlBuilder.Append("<br>")
}

# Initialize the HTML string builder.
$htmlBuilder = new-object system.text.stringbuilder
$null = $htmlBuilder.Append("<pre style='MARGIN: 0in 10pt 0in;line-height:normal';font-size:10pt>")

# Grab the console screen buffer contents using the Host console API.
$bufferWidth = $host.ui.rawui.BufferSize.Width
$bufferHeight = $host.ui.rawui.CursorPosition.Y
$rec = new-object System.Management.Automation.Host.Rectangle 0,0,($bufferWidth - 1),$bufferHeight
$buffer = $host.ui.rawui.GetBufferContents($rec)

# Iterate through the lines in the console buffer.
for($i = 0; $i -lt $bufferHeight; $i++)
{
  $spanBuilder = new-object system.text.stringbuilder

  # Track the colors to identify spans of text with the same formatting.
  $currentForegroundColor = $buffer[$i, 0].Foregroundcolor
  $currentBackgroundColor = $buffer[$i, 0].Backgroundcolor

  for($j = 0; $j -lt $bufferWidth; $j++)
  {
    $cell = $buffer[$i,$j]

    # If the colors change, generate an HTML span and append it to the HTML string builder.
    if (($cell.ForegroundColor -ne $currentForegroundColor) -or ($cell.BackgroundColor -ne $currentBackgroundColor))
    {
      Append-HtmlSpan

      # Reset the span builder and colors.
      $spanBuilder = new-object system.text.stringbuilder
      $currentForegroundColor = $cell.Foregroundcolor
      $currentBackgroundColor = $cell.Backgroundcolor
    }

    # Substitute characters which have special meaning in HTML.
    switch ($cell.Character)
    {
      '>' { $htmlChar = '&gt;' }
      '<' { $htmlChar = '&lt;' }
      '&' { $htmlChar = '&amp;' }
      default
      {
        $htmlChar = $cell.Character
      }
    }

    $null = $spanBuilder.Append($htmlChar)
  }

  Append-HtmlSpan
  Append-HtmlBreak
}

# Append HTML ending tag.
$null = $htmlBuilder.Append("</pre>")

return $htmlBuilder.ToString()

###########################################################################################################
# Get-ConsoleAsRtf.ps1
#
# The script captures console screen buffer up to the current cursor position and returns it in RTF format.
#
# Returns: ASCII-encoded string.
#
# Example:
#
# $rtfFileName = "$env:temp\ConsoleBuffer.rtf"
# .\Get-ConsoleAsRtf | out-file $rtfFileName -encoding ascii
# $null = [System.Diagnostics.Process]::Start("$rtfFileName")
#

# Check the host name and exit if the host is not the Windows PowerShell console host.
if ($host.Name -ne 'ConsoleHost')
{
  write-host -ForegroundColor Red "This script runs only in the console host. You cannot run this script in $($host.Name)."
  exit -1
}

# Maps console color name to RTF color index.
# The index of \cf is referencing the color definition in RTF color table.
#
function Get-RtfColorIndex ([string]$color)
{
  switch ($color)
  {
    'Black' { $index = 17 }
    'DarkBlue' { $index = 2 }
    'DarkGreen' { $index = 3 }
    'DarkCyan' { $index = 4 }
    'DarkRed' { $index = 5 }
    'DarkMagenta' { $index = 6 }
    'DarkYellow' { $index = 7 }
    'Gray' { $index = 8 }
    'DarkGray' { $index = 9 }
    'Blue' { $index = 10 }
    'Green' { $index = 11 }
    'Cyan' { $index = 12 }
    'Red' { $index = 13 }
    'Magenta' { $index = 14 }
    'Yellow' { $index = 15 }
    'White' { $index = 16 }
    default
    {
      $index = 0
    }
  }
  return $index
}

# Create RTF block from text using named console colors.
#
function Append-RtfBlock ($text)
{
  $foreColorIndex = Get-RtfColorIndex $currentForegroundColor
  $null = $rtfBuilder.Append("{\cf$foreColorIndex")

  # You can also add \ab* tag here if you want a bold font in the output.

  $backColorIndex = Get-RtfColorIndex $currentBackgroundColor
  $null = $rtfBuilder.Append("\chshdng0\chcbpat$backColorIndex")

  $text = $blockBuilder.ToString()
  $null = $rtfBuilder.Append(" $text}")
}

# Append line break to RTF builder
#
function Append-Break
{
  $backColorIndex = Get-RtfColorIndex $currentBackgroundColor
  $null = $rtfBuilder.Append("\shading0\cbpat$backColorIndex\par`r`n")
}

# Initialize the RTF string builder.
$rtfBuilder = new-object system.text.stringbuilder

# Set the desired font
$fontName = 'Lucida Console'
# Append RTF header
$null = $rtfBuilder.Append("{\rtf1\fbidis\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 $fontName;}}")
$null = $rtfBuilder.Append("`r`n")
# Append RTF color table which will contain all Powershell console colors.
$null = $rtfBuilder.Append('{\colortbl;red0\green0\blue128;\red0\green128\blue0;\red0\green128\blue128;\red128\green0\blue0;\red1\green36\blue86;\red238\green237\blue240;\red192\green192\blue192;\red128\green128\blue128;\red0\green0\blue255;\red0\green255\blue0;\red0\green255\blue255;\red255\green0\blue0;\red255\green0\blue255;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue0;}')
$null = $rtfBuilder.Append("`r`n")
# Append RTF document settings.
$null = $rtfBuilder.Append('\viewkind4\uc1\pard\ltrpar\f0\fs23 ')
 
# Grab the console screen buffer contents using the Host console API.
$bufferWidth = $host.ui.rawui.BufferSize.Width
$bufferHeight = $host.ui.rawui.CursorPosition.Y
$rec = new-object System.Management.Automation.Host.Rectangle 0,0,($bufferWidth - 1),$bufferHeight
$buffer = $host.ui.rawui.GetBufferContents($rec)

# Iterate through the lines in the console buffer.
for($i = 0; $i -lt $bufferHeight; $i++)
{
  $blockBuilder = new-object system.text.stringbuilder

  # Track the colors to identify spans of text with the same formatting.
  $currentForegroundColor = $buffer[$i, 0].Foregroundcolor
  $currentBackgroundColor = $buffer[$i, 0].Backgroundcolor

  for($j = 0; $j -lt $bufferWidth; $j++)
  {
    $cell = $buffer[$i,$j]

    # If the colors change, generate an RTF span and append it to the RTF string builder.
    if (($cell.ForegroundColor -ne $currentForegroundColor) -or ($cell.BackgroundColor -ne $currentBackgroundColor))
    {
      Append-RtfBlock

      # Reset the block builder and colors.
      $blockBuilder = new-object system.text.stringbuilder
      $currentForegroundColor = $cell.Foregroundcolor
      $currentBackgroundColor = $cell.Backgroundcolor
    }

    # Substitute characters which have special meaning in RTF.
    switch ($cell.Character)
    {
      "`t" { $rtfChar = '\tab' }
      '\' { $rtfChar = '\\' }
      '{' { $rtfChar = '\{' }
      '}' { $rtfChar = '\}' }
      default
      {
        $rtfChar = $cell.Character
      }
    }

    $null = $blockBuilder.Append($rtfChar)
  }

  Append-RtfBlock
  Append-Break
}

# Append RTF ending brace.
$null = $rtfBuilder.Append('}')

return $rtfBuilder.ToString()

 

Leave a Comment
  • Please add 6 and 7 and type the answer here:
  • Post
  • In the previous example we learned how to create a colorized copy of console in HTML and RTF format.

  • Nice!

    If you want to make your currently hard coded Html Encoding better then you can change it this way:

    [Reflection.Assembly]::LoadWithPartialName("System.Web")

    # Substitute characters which have special meaning in HTML.

    [System.Web.HttpUtility]::HtmlEncode( $cell.Character )

  • Thanks, Stranger. That is wonderful. I will put it to my repository of PowerShell cool tricks.

  • In the previous article we demonstrated how to use Console-Copy script to make a colorized copy of console

  • $color1 = [System.Console]::ForegroundColor

    $c = ([Int64]- 400)

    $color2 = [System.ConsoleColor]::red

    function Normalize-HtmlColor ($color1,$color2)

    {

     if ($color1 -eq "Gray") { $color1 = "peachpuff" }

     if ($color2 -eq "Red") { $color2 = "#FEDBB9" }

     return $color1,$color2

    }

    $e = $color2.[Int64]$c

  • This is AWESOME, and exactly what I needed, but the color table is all sorts of wrong on the RTF script.

    Updates:

    -----------------------------------------------

    function Get-RtfColorIndex ([string]$color)

    {

    switch ($color)

    {

    'Black' { $index = 16 }

    'DarkBlue' { $index = 1 }

    'DarkGreen' { $index = 2 }

    'DarkCyan' { $index = 3 }

    'DarkRed' { $index = 4 }

    'DarkMagenta' { $index = 5 }

    'DarkYellow' { $index = 6 }

    'Gray' { $index = 7 }

    'DarkGray' { $index = 8 }

    'Blue' { $index = 9 }

    'Green' { $index = 10 }

    'Cyan' { $index = 11 }

    'Red' { $index = 12 }

    'Magenta' { $index = 13 }

    'Yellow' { $index = 14 }

    'White' { $index = 16 }

    default

    {

    $index = 0

    }

    }

    return $index

    }

    -----------------------------------------------

    # Append RTF color table which will contain all Powershell console colors.

    $null = $rtfBuilder.Append('{\colortbl;red0\green0\blue128;\red0\green128\blue0;\red0\green128\blue128;\red128\green0\blue0;\red128\green0\blue128;\red128\green128\blue0;\red192\green192\blue192;\red128\green128\blue128;\red0\green0\blue255;\red0\green255\blue0;\red0\green255\blue255;\red255\green0\blue0;\red255\green0\blue255;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue0;}')

    $null = $rtfBuilder.Append("`r`n")

    That fixes it so all the powershell colors are right.

    Thanks!

  • Hey.  I'd love to take this and be able to integrate it with MSBuild such that the colors in the console output are maintained in the generated log file.  Any idea how that might be possible?

Page 1 of 1 (7 items)