func countPadding(s string, count int) int {
	padLength := count - len(s)
	rCount, wordLen := runewidth.StringWidth(s), len(s)
	if rCount < wordLen {
		padLength += wordLen - rCount
	}
	return padLength
}
determines the length of the padding needed.
package align
import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"
	"github.com/mattn/go-runewidth"
)
type Justification byte
const (
	JustifyRight Justification = iota + 1
	JustifyCenter
	JustifyLeft
)
type TextQualifier struct {
	On        bool
	Qualifier string
}
type PaddingOpts struct {
	Justification  Justification
	ColumnOverride map[int]Justification  
	Pad            int                    
}
type Grower interface {
	Grow(n int)
	Reset()
}
type Padder interface {
	io.Writer
	fmt.Stringer
	WriteByte(c byte) error
	WriteString(s string) (int, error)
	Bytes() []byte
}
type PadGrower interface {
	Grower
	Padder
}
type fieldPad struct {
	bytes.Buffer
}
type Align struct {
	scanner      *bufio.Scanner
	writer       *bufio.Writer
	sep          string  
	sepOut       string
	columnCounts map[int]int
	txtq         TextQualifier
	padOpts      PaddingOpts
	filter       []int
	filterLen    int
	lines        []string
	padder       PadGrower
}
func NewAlign(in io.Reader, out io.Writer, sep string, qu TextQualifier) *Align {
	return &Align{
		scanner:      bufio.NewScanner(in),
		writer:       bufio.NewWriter(out),
		sep:          sep,
		sepOut:       sep,
		columnCounts: make(map[int]int),
		txtq:         qu,
		padOpts: PaddingOpts{
			Justification: JustifyLeft,
			Pad:           1,
		},
		padder: &fieldPad{},  
	}
}
func (a *Align) OutputSep(outsep string) {
	a.sepOut = outsep
}
func (a *Align) Align() {
	a.columnLength()
	a.export()
}
func (a *Align) columnSize(num int) int {
	if _, ok := a.columnCounts[num]; !ok {
		return -1
	}
	return a.columnCounts[num]
}
func (a *Align) UpdatePadding(p PaddingOpts) {
	a.padOpts = p
}
func (a *Align) UpdatePadder(padder PadGrower) {
	a.padder = padder
}
func fieldLen(s, sep string) int {
	return genFieldLen(s, sep, "")
}
func fieldLenEscaped(s, sep, qual string) int {
	return genFieldLen(s, sep, qual)
}
func genFieldLen(s, sep, qual string) int {
	var endIdx int
	if len(qual) > 0 && strings.HasPrefix(s, qual) {
		endIdx += len(qual)
		endIdx += strings.Index(s[endIdx:], qual) + len(qual)
		return len(s[:endIdx])
	}
	endIdx += strings.Index(s, sep)
	if endIdx == -1 {
		return len(s)
	}
	return len(s[:endIdx])
}
func (a *Align) columnLength() {
	a.lines = make([]string, 0)
	for a.scanner.Scan() {
		var columnNum int
		var temp int
		line := a.scanner.Text()
		if a.txtq.On {
			for start := 0; start < len(line); {
				temp = fieldLenEscaped(line[start:], a.sep, a.txtq.Qualifier)
				start += temp + len(a.sep)
				if temp > a.columnCounts[columnNum] {
					a.columnCounts[columnNum] = temp
				}
				columnNum++
				temp = 0
			}
		} else {
			for start := 0; start < len(line); {
				temp = fieldLen(line[start:], a.sep)
				start += temp + len(a.sep)
				if temp > a.columnCounts[columnNum] {
					a.columnCounts[columnNum] = temp
				}
				columnNum++
				temp = 0
			}
		}
		a.lines = append(a.lines, line)
	}
}
const padchar byte = ' '
func (a *Align) export() {
	if a.padOpts.Pad < 0 {
		a.padOpts.Pad = 0
	}
	surroundingPad := make([]byte, 0, a.padOpts.Pad)
	for i := 0; i < a.padOpts.Pad; i++ {
		surroundingPad = append(surroundingPad, padchar)
	}
	for _, line := range a.lines {
		words := a.splitWithQual(line, a.sep, a.txtq.Qualifier)
		var columnNum int
		var tempColumn int  
		for _, word := range words {
			if a.filterLen > 0 {
				if !contains(a.filter, columnNum+1) {
					columnNum++
					if columnNum == len(words) {
						a.writer.WriteString("\n")
					}
					continue
				}
			}
			j := a.padOpts.Justification
			if len(a.padOpts.ColumnOverride) > 0 {
				for k, v := range a.padOpts.ColumnOverride {
					if k == columnNum+1 {
						j = v
					}
				}
			}
			padLength := countPadding(word, a.columnCounts[columnNum])
			paddedWord := applyPadding(a.padder, word, string(surroundingPad), tempColumn, padLength, j)
			a.padder.Reset()  
			columnNum++
			tempColumn++
			if a.filterLen > 0 && a.filter[a.filterLen-1] == columnNum || columnNum == len(words) {
				a.writer.Write(paddedWord)
				a.writer.WriteByte('\n')
				break
			}
			a.writer.Write(paddedWord)
			a.writer.WriteString(a.sepOut)
		}
	}
	a.writer.Flush()
}
func fillWithPadding(padder Padder, length int) {
	for i := 0; i < length; i++ {
		padder.WriteByte(padchar)
	}
}
func applyPadding(padder Padder, original, surroundingPad string, columnNum, padLength int, just Justification) []byte {
	if len(surroundingPad) > 0 {
		if columnNum > 0 {
			padder.WriteString(surroundingPad)
		}
	}
	switch just {
	case JustifyLeft:
		padder.WriteString(original)
		fillWithPadding(padder, padLength)
	case JustifyRight:
		fillWithPadding(padder, padLength)
		padder.WriteString(original)
	case JustifyCenter:
		if padLength > 2 {
			fillWithPadding(padder, (padLength - (padLength / 2)))
			padder.WriteString(original)
			fillWithPadding(padder, padLength/2)
		} else {
			padder.WriteString(original)
			fillWithPadding(padder, padLength)
		}
	}
	if len(surroundingPad) > 0 {
		padder.WriteString(surroundingPad)
	}
	return padder.Bytes()
}