[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Lua
. . -> [Subject]  ANSI to Style Table

ANSI to Style Table

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by Falanyx   (4 posts)  [Biography] bio
Date Wed 06 Jan 2016 04:12 AM (UTC)
Message
Hello,

I'm currently receiving a string over Telnet subnegotiation that includes ANSI colour codes. While I doubt it the more I look, I was wondering if there was any way to take such a string and convert it to a style table (as received by Lua functions as arg#4 when called by a trigger).

Thank you,
Falanyx.
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Wed 06 Jan 2016 04:53 AM (UTC)
Message
Can you give an example of such a string? Anything is possible, that doesn't sound too hard.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Falanyx   (4 posts)  [Biography] bio
Date Reply #2 on Wed 06 Jan 2016 07:42 AM (UTC)
Message
Here's an example string (which is surrounded by a reasonable amount of JSON in reality):

"\u001b[0;1;33mYou tell Drunken Master Kalas Malarious, Kitten Avenger, \"// Test string.\"\u001b[0;37m"


I've given a bit more thought on how to do it, and I know it's possible to Simulate the string and use a trigger, but I'm trying to avoid the use of a trigger altogether (in what is, frankly, probably a misguided attempt to be lazy or clever depending on point of view).
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #3 on Wed 06 Jan 2016 10:15 AM (UTC)
Message
See: http://www.gammon.com.au/scripts/doc.php?function=AnsiNote

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Falanyx   (4 posts)  [Biography] bio
Date Reply #4 on Wed 06 Jan 2016 04:03 PM (UTC)
Message
I probably should have been about 20 times more clear with what I was trying to do, so my apologies for that. I'm actually needing the style table for insertion into a chat miniwindow (based off the one posted here back in 2010/2011 by... I think it was Fiendish, but I could be 100% wrong).
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #5 on Wed 06 Jan 2016 08:40 PM (UTC)

Amended on Thu 07 Jan 2016 08:59 PM (UTC) by Nick Gammon

Message
You can use regexps to break up the string into ANSI sequences, along these lines:


test = "\027[0;1;33mYou tell foo, \"// Test string.\"\027[0;37mbar"

for sequence, text in string.gmatch (test, "\027%[([0-9;]+)m([^\027]+)") do
    
  for ansi in string.gmatch (sequence, "%d+") do
    print ("ANSI code: ", ansi)
  end -- for

  print ("Text: ", text)

end  -- for


Output:


ANSI code:  0
ANSI code:  1
ANSI code:  33
Text:  You tell foo, "// Test string."
ANSI code:  0
ANSI code:  37
Text:  bar


I seem to recall doing this a while back but don't remember where.

You can now take those sequences and do something meaningful with them, to apply to those parts of text.

From the MUSHclient source, these are what the codes do:


// ANSI Colour Codes

#define ANSI_RESET             0 
#define ANSI_BOLD              1
#define ANSI_BLINK             3 
#define ANSI_UNDERLINE         4 
#define ANSI_SLOW_BLINK        5
#define ANSI_FAST_BLINK        6 
#define ANSI_INVERSE           7 

#define ANSI_CANCEL_BOLD      22 
#define ANSI_CANCEL_BLINK     23 
#define ANSI_CANCEL_UNDERLINE 24 
#define ANSI_CANCEL_SLOW_BLINK  25
#define ANSI_CANCEL_INVERSE   27 

#define ANSI_TEXT_BLACK       30 
#define ANSI_TEXT_RED         31 
#define ANSI_TEXT_GREEN       32 
#define ANSI_TEXT_YELLOW      33 
#define ANSI_TEXT_BLUE        34 
#define ANSI_TEXT_MAGENTA     35 
#define ANSI_TEXT_CYAN        36 
#define ANSI_TEXT_WHITE       37 
#define ANSI_TEXT_256_COLOUR  38
         
#define ANSI_SET_FOREGROUND_DEFAULT 39
                     
#define ANSI_BACK_BLACK       40 
#define ANSI_BACK_RED         41 
#define ANSI_BACK_GREEN       42 
#define ANSI_BACK_YELLOW      43 
#define ANSI_BACK_BLUE        44 
#define ANSI_BACK_MAGENTA     45 
#define ANSI_BACK_CYAN        46 
#define ANSI_BACK_WHITE       47 
#define ANSI_BACK_256_COLOUR  48

#define ANSI_SET_BACKGROUND_DEFAULT 49


So in other words, my first test sequence does:


  • 0 - reset to standard (ie. not bold, not underline, etc.)
  • 1 - set bold text
  • 33 - text to yellow (foreground colour)


The second sequence does:


  • 0 - reset to standard (ie. not bold, not underline, etc.)
  • 37 - text to white (foreground colour)


My first regexp looks for ESC [ <some numbers or ";"> m. Thus it would fail to find the very first words on the line (if any) if they are not preceded by an escape sequence. You could inject a dummy one (eg. ESC[0m ) or handle that as a special case of a style run that does not change colours from the previous one.

This should give you enough information to get started.

[EDIT] Bear in mind the second regexp in my example returns strings. You need to use tonumber on them if you want to compare them directly to numbers.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Fiendish   USA  (2,514 posts)  [Biography] bio   Global Moderator
Date Reply #6 on Fri 17 Jun 2016 07:28 AM (UTC)

Amended on Fri 17 Jun 2016 05:17 PM (UTC) by Fiendish

Message
I just did something similar.
Not to styles, to Aardwolf color codes, but the process should be basically the same.
Anyway I came up with this:

-- Aardwolf bold colors
local ansi_digit_to_bold_atcode = {
   [30] = "@D",
   [31] = "@R",
   [32] = "@G",
   [33] = "@Y",
   [34] = "@B",
   [35] = "@M",
   [36] = "@C",
   [37] = "@W"
}

-- Tries to convert ANSI sequences to Aardwolf color codes
function AnsiToColours(ansi, default_foreground_code)
   if not default_foreground_code then
      default_foreground_code = "@w"
   end

   local ansi_capture = "\027%[([%d;]+)m"

   -- this stuff goes outside because ANSI is a state machine (lolsigh)
   local bold = false
   local color = ""
   local xstage = 0

   ansi = ansi:gsub("@","@@"):gsub(ansi_capture, function(a)
      for c in a:gmatch("%d+") do
         local nc = tonumber(c)
         if nc == 38 then
            xstage = 1
         elseif nc == 5 and xstage == 1 then
            xstage = 2
         elseif xstage == 2 then -- xterm 256 color
            if bold and ansi_digit_to_bold_atcode[nc+30] then
               color = ansi_digit_to_bold_atcode[nc+30]
            else
               color = string.format("@x%03d", nc)
            end
            xstage = 0
         elseif nc == 1 then
            bold = true
            xstage = 0
         elseif nc == 0 then
            bold = false
            -- not actually sure if we should set color here or not
            color = default_foreground_code
         elseif nc <= 37 and nc >= 30 then -- regular color
            if bold and ansi_digit_to_bold_atcode[nc] then
               color = ansi_digit_to_bold_atcode[nc]
            else
               color = string.format("@x%03d", nc-30)
            end
            xstage = 0
         end
      end
      return color
   end)
   
   return ansi
end


Aardwolf supports 256 xterm colors, so I use those preferentially for anything that isn't explicitly bold.

https://github.com/fiendish/aardwolfclientpackage
[Go to top] top

Posted by Fiendish   USA  (2,514 posts)  [Biography] bio   Global Moderator
Date Reply #7 on Fri 17 Jun 2016 08:25 AM (UTC)
Message
I then have my own Colors to Styles function written a long time ago.
It relies on having defined a table called atcode_to_color_value which, as the name suggests, translates Aardwolf's color codes into MUSHclient color numbers.

-- Converts text with Aardwolf normal and xterm 256 color codes into MUSHclient style runs
function ColorsToStyles (Text, default_foreground_code, default_background_code)
   if not default_foreground_code then
      default_foreground_code = "@w"
   end
   default_foreground = atcode_to_color_value[default_foreground_code]
  
   if not default_background_code then
      default_background_code = "@k"
   end
   default_background = atcode_to_color_value[default_background_code]
    
   if Text:match ("@") then
      astyles = {}

      -- make sure we start with a color
      if Text:sub(1, 1) ~= "@" then
         Text = default_foreground_code .. Text
      end -- if

      Text = Text:gsub ("@%-", "~") -- fix tildes
      Text = Text:gsub ("@@", "\0") -- change @@ to 0x00
      Text = Text:gsub ("@x([^%d])","%1") -- strip invalid xterm codes (non-number)
      Text = Text:gsub ("@x[3-9]%d%d","") -- strip invalid xterm codes (300+)
      Text = Text:gsub ("@x2[6-9]%d","") -- strip invalid xterm codes (260+)
      Text = Text:gsub ("@x25[6-9]","") -- strip invalid xterm codes (256+)
      Text = Text:gsub ("@[^xrgybmcwDRGYBMCWd]", "")  -- strip hidden garbage

      for color, text in Text:gmatch ("@(%a)([^@]+)") do
         text = text:gsub ("%z", "@") -- put any @ characters back

         if color == "x" then -- xterm 256 colors
            code,text = text:match("(%d%d?%d?)(.*)")
            color = color..code
         end

         if #text > 0 then
            table.insert (astyles, { text = text, 
               bold = (color == color:upper()),
               length = #text, 
               textcolour = atcode_to_color_value[color] or default_foreground,
               backcolour = default_background })
         end -- if some text
      end -- for each color run.

      return astyles
   end -- if any color codes at all

   -- No color codes, create a single style.
   return { { text = Text, 
      length = #Text, 
      textcolour = default_foreground,
      backcolour = default_background } }
end  -- function ColorsToStyles


If you want to go from ANSI to Styles, you could chain these two functions together.

https://github.com/fiendish/aardwolfclientpackage
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


21,241 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]