Skip to content

Commit

Permalink
Merge pull request #13 from wader/invert
Browse files Browse the repository at this point in the history
Add invert support
  • Loading branch information
wader authored Dec 5, 2023
2 parents f2ebd0d + e91aa06 commit 0e37c47
Show file tree
Hide file tree
Showing 15 changed files with 3,304 additions and 3,219 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ WRITE_ACTUAL=1 go test ./...

Visual inspect outputs in browser:
```
for i in ansisvg/testdata/*.ansi.svg; do echo "$i<br><img src=\"$i\"/><br>" ; done > all.html
for i in ansitosvg/testdata/*.ansi.svg; do echo "$i<br><img src=\"$i\"/><br>" ; done > all.html
open all.html
```

Using [ffcat](https://github.com/wader/ffcat):
```
for i in ansisvg/testdata/*.ansi; do echo $i ; cat $i | go run main.go | ffcat ; done
for i in ansitosvg/testdata/*.ansi; do echo $i ; cat $i | go run main.go | ffcat ; done
```

## Licenses and thanks
Expand Down
76 changes: 44 additions & 32 deletions ansidecoder/ansidecoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,30 @@ const (
StateOSCSeenESC // Operating System Command ESC ] ... ESC
)

type codeRanges [][2]int
type codeRanges [2]int

func (cr codeRanges) Is(c int) bool {
for _, r := range cr {
if c >= r[0] && c <= r[1] {
return true
}
if c >= cr[0] && c <= cr[1] {
return true
}
return false
}

var sgrReset = codeRanges{{0, 0}}
var sgrIncreseIntensity = codeRanges{{1, 1}}
var sgrNormal = codeRanges{{22, 22}}
var sgrForeground = codeRanges{{30, 37}, {90, 97}}
var sgrForegroundRGB = codeRanges{{38, 38}}
var sgrForegroundDefault = codeRanges{{39, 39}}
var sgrBackground = codeRanges{{40, 47}, {100, 107}}
var sgrBackgroundRGB = codeRanges{{48, 48}}
var sgrBackgroundDefault = codeRanges{{49, 49}}
var sgrUnderlineOn = codeRanges{{4, 4}}
var sgrUnderlineOff = codeRanges{{24, 24}}
var sgrReset = codeRanges{0, 0}
var sgrIncreaseIntensity = codeRanges{1, 1}
var sgrNormal = codeRanges{22, 22}
var sgrForeground = codeRanges{30, 37}
var sgrForegroundBright = codeRanges{90, 97}
var sgrForegroundRGB = codeRanges{38, 38}
var sgrForegroundDefault = codeRanges{39, 39}
var sgrBackground = codeRanges{40, 47}
var sgrBackgroundBright = codeRanges{100, 107}
var sgrBackgroundRGB = codeRanges{48, 48}
var sgrBackgroundDefault = codeRanges{49, 49}
var sgrUnderlineOn = codeRanges{4, 4}
var sgrUnderlineOff = codeRanges{24, 24}
var sgrInvertOn = codeRanges{7, 7}
var sgrInvertOff = codeRanges{27, 27}

const ESCRune = rune('\x1b')
const BELRune = rune('\x07')
Expand All @@ -58,7 +60,7 @@ func (c Color) String() string {
if len(c.RGB) != 0 {
return fmt.Sprintf("#%.2x%.2x%.2x", c.RGB[0], c.RGB[1], c.RGB[2])
}
if c.N != 0 {
if c.N != -1 {
return fmt.Sprintf("%d", c.N)
}
return ""
Expand All @@ -72,6 +74,7 @@ type Decoder struct {
Background Color
Underline bool
Intensity bool
Invert bool

MaxX int
MaxY int
Expand All @@ -87,27 +90,27 @@ type Decoder struct {
// NewDecoder returns new ANSI decoder that is a io.RuneReader. See ReadRune for details.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{
readBuf: bufio.NewReader(r),
paramsBuf: &bytes.Buffer{},
Foreground: Color{N: -1},
Background: Color{N: -1},
readBuf: bufio.NewReader(r),
paramsBuf: &bytes.Buffer{},
}
}

func intsToColor(fo int, bo int, cs []int) (Color, int) {
if len(cs) == 0 {
return Color{}, 0
return Color{N: -1}, 0
}
switch {
case cs[0] == 2 && len(cs) >= 4: // 2;r;g;b
return Color{RGB: append([]int{}, cs[1:4]...)}, 4
case cs[0] == 5 && len(cs) >= 2: // 5;n
n := cs[1]
switch {
case n >= 0 && n <= 7:
case n >= 0 && n <= 15:
// 0- 7: standard colors (as in ESC [ 30–37 m)
return Color{N: fo + n}, 2
case n >= 7 && n <= 15:
// 8- 15: high intensity colors (as in ESC [ 90–97 m)
return Color{N: bo + n - 8}, 2
return Color{N: n}, 2
case n >= 16 && n <= 231:
// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
// TODO: not tested
Expand All @@ -128,7 +131,7 @@ func intsToColor(fo int, bo int, cs []int) (Color, int) {
return Color{RGB: []int{g, g, g}}, 2
}
}
return Color{}, 0
return Color{N: -1}, 0
}

// ReadRune returns next rune. The decoder struct has state for last returned rune, .X, .Y, .Foreground etc.
Expand Down Expand Up @@ -202,32 +205,41 @@ func (d *Decoder) ReadRune() (r rune, size int, err error) {
var ns int
switch {
case sgrReset.Is(n):
d.Foreground = Color{N: 0}
d.Background = Color{N: 0}
d.Foreground = Color{N: -1}
d.Background = Color{N: -1}
d.Underline = false
d.Intensity = false
case sgrIncreseIntensity.Is(n):
d.Invert = false
case sgrIncreaseIntensity.Is(n):
d.Intensity = true
case sgrNormal.Is(n):
d.Intensity = false
case sgrForeground.Is(n):
d.Foreground = Color{N: n}
d.Foreground = Color{N: n - 30}
case sgrForegroundBright.Is(n):
d.Foreground = Color{N: n - 90 + 8}
case sgrForegroundRGB.Is(n):
d.Foreground, ns = intsToColor(30, 90, pn[i+1:])
i += ns
case sgrForegroundDefault.Is(n):
d.Foreground = Color{N: 0}
d.Foreground = Color{N: -1}
case sgrBackground.Is(n):
d.Background = Color{N: n}
d.Background = Color{N: n - 40}
case sgrBackgroundBright.Is(n):
d.Background = Color{N: n - 100 + 8}
case sgrBackgroundRGB.Is(n):
d.Background, ns = intsToColor(40, 100, pn[i+1:])
i += ns
case sgrBackgroundDefault.Is(n):
d.Background = Color{N: 0}
d.Background = Color{N: -1}
case sgrUnderlineOn.Is(n):
d.Underline = true
case sgrUnderlineOff.Is(n):
d.Underline = false
case sgrInvertOn.Is(n):
d.Invert = true
case sgrInvertOff.Is(n):
d.Invert = false
}
}
}
Expand Down
66 changes: 34 additions & 32 deletions ansitosvg/ansisvg.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func Convert(r io.Reader, w io.Writer, opts Options) error {
r = ' '
n = 8 - (ad.X % 8)
}

for i := 0; i < n; i++ {
line.Chars = append(line.Chars, svgscreen.Char{
Char: string([]rune{r}),
Expand All @@ -72,6 +73,7 @@ func Convert(r io.Reader, w io.Writer, opts Options) error {
Background: ad.Background.String(),
Underline: ad.Underline,
Intensity: ad.Intensity,
Invert: ad.Invert,
})
}
}
Expand All @@ -94,41 +96,41 @@ func Convert(r io.Reader, w io.Writer, opts Options) error {
Transparent: opts.Transparent,
ForegroundColor: c.Foreground,
ForegroundColors: map[string]string{
"30": c.ANSIBlack,
"31": c.ANSIRed,
"32": c.ANSIGreen,
"33": c.ANSIYellow,
"34": c.ANSIBlue,
"35": c.ANSIMagenta,
"36": c.ANSICyan,
"37": c.ANSIWhite,
"90": c.ANSIBrightBlack,
"91": c.ANSIBrightRed,
"92": c.ANSIBrightGreen,
"93": c.ANSIBrightYellow,
"94": c.ANSIBrightBlue,
"95": c.ANSIBrightMagenta,
"96": c.ANSIBrightCyan,
"97": c.ANSIBrightWhite,
"0": c.ANSIBlack,
"1": c.ANSIRed,
"2": c.ANSIGreen,
"3": c.ANSIYellow,
"4": c.ANSIBlue,
"5": c.ANSIMagenta,
"6": c.ANSICyan,
"7": c.ANSIWhite,
"8": c.ANSIBrightBlack,
"9": c.ANSIBrightRed,
"10": c.ANSIBrightGreen,
"11": c.ANSIBrightYellow,
"12": c.ANSIBrightBlue,
"13": c.ANSIBrightMagenta,
"14": c.ANSIBrightCyan,
"15": c.ANSIBrightWhite,
},
BackgroundColor: c.Background,
BackgroundColors: map[string]string{
"40": c.ANSIBlack,
"41": c.ANSIRed,
"42": c.ANSIGreen,
"43": c.ANSIYellow,
"44": c.ANSIBlue,
"45": c.ANSIMagenta,
"46": c.ANSICyan,
"47": c.ANSIWhite,
"100": c.ANSIBrightBlack,
"101": c.ANSIBrightRed,
"102": c.ANSIBrightYellow,
"103": c.ANSIBrightYellow,
"104": c.ANSIBrightBlue,
"105": c.ANSIBrightMagenta,
"106": c.ANSIBrightCyan,
"107": c.ANSIBrightWhite,
"0": c.ANSIBlack,
"1": c.ANSIRed,
"2": c.ANSIGreen,
"3": c.ANSIYellow,
"4": c.ANSIBlue,
"5": c.ANSIMagenta,
"6": c.ANSICyan,
"7": c.ANSIWhite,
"8": c.ANSIBrightBlack,
"9": c.ANSIBrightRed,
"10": c.ANSIBrightYellow,
"11": c.ANSIBrightYellow,
"12": c.ANSIBrightBlue,
"13": c.ANSIBrightMagenta,
"14": c.ANSIBrightCyan,
"15": c.ANSIBrightWhite,
},
FontName: opts.FontName,
FontSize: opts.FontSize,
Expand Down
Loading

0 comments on commit 0e37c47

Please sign in to comment.