ansigo.go (6389B)
1 // AnsiGo 1.01 2 // Copyright (c) 2012-2019, Frederic Cambus 3 // https://github.com/fcambus/ansigo 4 // 5 // Created: 2012-02-14 6 // Last Updated: 2019-06-11 7 // 8 // AnsiGo is released under the BSD 2-Clause license. 9 // See LICENSE file for details. 10 package main 11 12 import ( 13 "fmt" 14 "image" 15 "image/draw" 16 "image/png" 17 "io/ioutil" 18 "os" 19 "strconv" 20 "strings" 21 ) 22 23 func main() { 24 fmt.Println("-------------------------------------------------------------------------------\n AnsiGo 1.01 (c) by Frederic CAMBUS 2012-2019\n-------------------------------------------------------------------------------\n") 25 26 // Check input parameters and show usage 27 if len(os.Args) != 2 { 28 fmt.Println("USAGE: ansigo inputfile\n") 29 fmt.Println("EXAMPLES: ansigo ansi.ans\n") 30 os.Exit(1) 31 } 32 33 input := os.Args[1] 34 output := input + ".png" 35 36 fmt.Println("Input File:", input) 37 fmt.Println("Output File:", output) 38 39 var ansi Ansi 40 ansi.SetPalette() 41 ansi.SetFont() 42 43 // Load input file 44 data, err := ioutil.ReadFile(input) 45 if err != nil { 46 fmt.Println("\nERROR: Can't open or read", input, "\n") 47 os.Exit(1) 48 } 49 50 // Process ANSI 51 for i := 0; i < len(data); i++ { 52 ansi.character = data[i] 53 54 // 80th column wrapping 55 if ansi.positionX == 80 { 56 ansi.positionY++ 57 ansi.positionX = 0 58 } 59 60 // CR (Carriage Return) 61 if ansi.character == '\r' { 62 continue 63 } 64 65 // LF (Line Feed) 66 if ansi.character == '\n' { 67 ansi.positionY++ 68 ansi.positionX = 0 69 continue 70 } 71 72 // HT (Horizontal Tabulation) 73 if ansi.character == '\t' { 74 ansi.positionX += 8 75 continue 76 } 77 78 // SUB (Substitute) 79 if ansi.character == '\x1a' { 80 break 81 } 82 83 // ANSI Sequence: ESC (Escape) + [ 84 if ansi.character == '\x1b' && data[i+1] == '[' { 85 ansiSequence := []byte{} 86 87 sequence: 88 for j := 0; j < 12; j++ { 89 ansiSequenceCharacter := data[i+2+j] 90 91 switch ansiSequenceCharacter { 92 case 'H', 'f': // Cursor Position 93 ansiSequenceValues := strings.SplitN(string(ansiSequence), ";", -1) 94 95 valueY, _ := strconv.Atoi(ansiSequenceValues[0]) 96 ansi.positionY = valueY - 1 97 98 valueX, _ := strconv.Atoi(ansiSequenceValues[1]) 99 ansi.positionX = valueX - 1 100 101 i += j + 2 102 break sequence 103 104 case 'A': // Cursor Up 105 valueY, _ := strconv.Atoi(string(ansiSequence)) 106 if valueY == 0 { 107 valueY++ 108 } 109 110 ansi.positionY = ansi.positionY - valueY 111 112 i += j + 2 113 break sequence 114 115 case 'B': // Cursor Down 116 valueY, _ := strconv.Atoi(string(ansiSequence)) 117 if valueY == 0 { 118 valueY++ 119 } 120 121 ansi.positionY = ansi.positionY + valueY 122 123 i += j + 2 124 break sequence 125 126 case 'C': // Cursor Forward 127 valueX, _ := strconv.Atoi(string(ansiSequence)) 128 if valueX == 0 { 129 valueX++ 130 } 131 132 ansi.positionX = ansi.positionX + valueX 133 if ansi.positionX > 80 { 134 ansi.positionX = 80 135 } 136 137 i += j + 2 138 break sequence 139 140 case 'D': // Cursor Backward 141 valueX, _ := strconv.Atoi(string(ansiSequence)) 142 if valueX == 0 { 143 valueX++ 144 } 145 146 ansi.positionX = ansi.positionX - valueX 147 if ansi.positionX < 0 { 148 ansi.positionX = 0 149 } 150 151 i += j + 2 152 break sequence 153 154 case 's': // Save Cursor Position 155 ansi.savedPositionY = ansi.positionY 156 ansi.savedPositionX = ansi.positionX 157 158 i += j + 2 159 break sequence 160 161 case 'u': // Restore Cursor Position 162 ansi.positionY = ansi.savedPositionY 163 ansi.positionX = ansi.savedPositionX 164 165 i += j + 2 166 break sequence 167 168 case 'J': // Erase Display 169 value, _ := strconv.Atoi(string(ansiSequence)) 170 171 if value == 2 { 172 ansi.buffer = nil 173 174 ansi.positionX = 0 175 ansi.positionY = 0 176 ansi.sizeX = 0 177 ansi.sizeY = 0 178 } 179 180 i += j + 2 181 break sequence 182 183 case 'm': // Set Graphic Rendition 184 ansiSequenceValues := strings.SplitN(string(ansiSequence), ";", -1) 185 186 for j := 0; j < len(ansiSequenceValues); j++ { 187 valueColor, _ := strconv.Atoi(ansiSequenceValues[j]) 188 189 switch valueColor { 190 case 0: 191 ansi.colorBackground = 0 192 ansi.colorForeground = 7 193 ansi.bold = false 194 195 case 1: 196 ansi.colorForeground += 8 197 ansi.bold = true 198 199 case 5: 200 ansi.colorBackground += 8 201 202 case 30, 31, 32, 33, 34, 35, 36, 37: 203 ansi.colorForeground = valueColor - 30 204 if ansi.bold { 205 ansi.colorForeground += 8 206 } 207 208 case 40, 41, 42, 43, 44, 45, 46, 47: 209 ansi.colorBackground = valueColor - 40 210 } 211 } 212 213 i += j + 2 214 break sequence 215 216 case 'h', 'l': // Skipping Set Mode And Reset Mode Sequences 217 i += j + 2 218 break sequence 219 } 220 221 ansiSequence = append(ansiSequence, ansiSequenceCharacter) 222 } 223 } else { 224 // Record Number Of Columns And Lines Used 225 if ansi.positionX > ansi.sizeX { 226 ansi.sizeX = ansi.positionX 227 } 228 if ansi.positionY > ansi.sizeY { 229 ansi.sizeY = ansi.positionY 230 } 231 232 // Write Current Character Info In A Temporary Array 233 ansi.buffer = append(ansi.buffer, Character{ansi.colorBackground, ansi.colorForeground, ansi.positionX, ansi.positionY, ansi.character}) 234 235 ansi.positionX++ 236 } 237 } 238 239 // Allocate Image Buffer Memory 240 canvasSize := image.Rect(0, 0, 640, (ansi.sizeY+1)*16) 241 canvas := image.NewRGBA(canvasSize) 242 243 // Draw The Canvas Background 244 draw.Draw(canvas, canvas.Bounds(), &image.Uniform{ansi.palette[0]}, image.ZP, draw.Src) 245 246 // Render ANSI 247 for i := 0; i < len(ansi.buffer); i++ { 248 character := ansi.buffer[i] 249 250 // Set Background 251 draw.Draw(canvas, image.Rect(character.positionX*8, character.positionY*16, character.positionX*8+8, character.positionY*16+16), &image.Uniform{ansi.palette[character.colorBackground]}, image.ZP, draw.Src) 252 253 // Draw Character 254 for line := 0; line < 16; line++ { 255 for column := 0; column < 8; column++ { 256 if (ansi.font[line+(int(character.code)*16)] & (0x80 >> uint(column))) != 0 { 257 canvas.Set(character.positionX*8+column, line+character.positionY*16, ansi.palette[character.colorForeground]) 258 } 259 } 260 } 261 } 262 263 // Create Output File 264 outputFile, err := os.Create(output) 265 if err != nil { 266 fmt.Println("ERROR: Can't create output file.") 267 os.Exit(1) 268 } 269 270 // Encode PNG image 271 if err = png.Encode(outputFile, canvas); err != nil { 272 fmt.Println("ERROR: Can't encode PNG file.") 273 os.Exit(1) 274 } 275 276 outputFile.Close() 277 278 fmt.Println("\nSuccessfully created file", output, "\n") 279 }