Code cleaning, update Readme

This commit is contained in:
probandula
2016-10-26 18:32:11 +02:00
parent 3f7f13785f
commit eef61fca27
9 changed files with 1165 additions and 66 deletions

View File

@@ -1,8 +1,5 @@
# FIGlet for Go
**Currently in Development.
Sould work but will be improved (add demos, better font adding, maybe better performance, more default fonts)**
A port of [figlet](http://www.figlet.org/) to golang and fork of [getwe/figlet4go](https://github.com/getwe/figlet4go).
![screenshot](./screenshot/figlet4go.png)
@@ -14,21 +11,70 @@ go get -u github.com/probandula/figlet4go
```
## Usage
### Basic
You have to create a renderer (`ascii`) and let it render the desired string through the `Render` method. After that you can simply print the returned string.
```go
// Create the renderer
ascii := figlet4go.NewAsciiRender()
// Optional: Add color to the letters (https://github.com/fatih/color needed)
// Render and print the string
renderStr, _ := ascii.Render("Hello World")
fmt.Print(renderStr)
```
## Default font
The default font is built into the `bindata.go` file with the tool [go-bindata](https://github.com/jteeuwen/go-bindata).
### Colored
The colors given in the `[]color.Attribute` slice are repeating if the string is longer than the slice. You have to call the `RenderOpts` instead of the `Render` method to give the Renderer the Options.
```go
ascii := figlet4go.NewAsciiRender()
// Adding the colors to RenderOptions
options := figlet4go.NewRenderOptions()
options.FontColor = []color.Attribute{
color.FgGreen,
color.FgYellow,
color.FgCyan,
}
renderStr, _ := ascii.RenderOpts("Hello Colors", options)
fmt.Print(renderStr)
```
### Other font
If you want to use another font, you have to specify the name of the font as in this example.
Is the font you want to use not [included](#builtin) you have to load the font manually with the `LoadFont` method. This method will walk the path recursively and load all `.flf` files
```go
ascii := figlet4go.NewAsciiRender()
options := figlet4go.NewRenderOptions()
options.FontName = "larry3d"
// If 'larry3d' wouldn't be included you would have to load your .flf files like that:
ascii.LoadFont("/path/to/fonts/")
renderStr, _ := ascii.RenderOpts("Hello Colors", options)
fmt.Print(renderStr)
```
## Fonts
### Bulitin
The builtin fonts are built into the `bindata.go` file with the tool [go-bindata](https://github.com/jteeuwen/go-bindata).
The bash script for building the default font is stored in `tools/` (`go-bindata` must be installed).
The default font is `standard`. All builtin fonts are listed here:
| Font name | Source |
| --- | --- |
| standard | http://www.figlet.org/fontdb_example.cgi?font=standard.flf |
| larry3d | http://www.figlet.org/fontdb_example.cgi?font=larry3d.flf |
### Other fonts
Other fonts can mainly be found on [figlet](http://www.figlet.org). You have to load them as in [this example](#other-fonts).
## Use the demo
There are [demo](https://github.com/probandula/figlet4go/blob/master/demo) programs for trying out the library.
To run them, `cd` into the `demo/` directory and run `go run [filename]` on any program you want to run.
## Todo
- [ ] automatic the perfect char margin
- [ ] Linebreak possible?

924
assets/larry3d.flf Normal file
View File

@@ -0,0 +1,924 @@
flf2a$ 9 6 30 1 5
larry3d.flf by Larry Gelberg (larryg@avs.com)
(stolen liberally from Juan Car's puffy.flf)
tweaked by Glenn Chappell <ggc@uiuc.edu>
Version 1.2 2/24/94
$$$ @
$$$ @
$$$ @
$$$ @
$$$ @
$$$ @
$$$ @
$$$ @
$$$@@
__ @
/\ \ @
\ \ \ @
\ \ \ @
\ \_\ @
\/\_\@
\/_/@
@
@@
__ __ @
/\ \\ \ @
\ \_\\_\ @
\/_//_/$ @
$ $ @
$ $@
@
@
@@
__ __ @
_\ \\ \__ @
/\__ _ _\ @
\/_L\ \\ \L_ @
/\_ _ _\@
\/_/\_\\_\/@
\/_//_/ @
@
@@
__ @
/\ \_ @
\/'__`\ @
/\ \_\_\ @
\ \____ \ @
\/\ \_\ \@
\ `\_ _/@
`\_/\_\@
\/_/@@
__ __ @
/\_\ / / @
\/_/ / / @
/ / @
/ / __ @
/_/ /\_\@
/_/ \/_/@
@
@@
____ @
/| _ \ @
|/\ | @
\// __`\/\ @
/| \L> <_@
| \_____/\/@
\/____/\/ @
@
@@
__ @
/\ \ @
\ \/$ @
\/ $ @
$ $ @
$ $@
@
@
@@
_ @
/' \ @
/\ ,/' @
\ \ \ @
\ \ `\ @
\ `\__\@
`\/_/ @
@
@@
__ @
/\ `\ @
\`\ \ @
`\`\ \ @
`\/' \@
/\__/@
\/_/ @
@
@@
__ @
_\ \ _ @
/\_` ' \ @
\/_> <_ @
/\_, ,_\@
\/_/\_\/@
\/_/ @
@
@@
__ @
/\ \ @
\_\ \___ @
/\___ __\@
\/__/\ \_/@
\ \_\ @
\/_/ @
@
@@
@
@
@
@
__ @
/\ \@
\ \/@
\/ @
@@
@
@
@
_______ @
/\______\@
\/______/@
@
@
@@
@
@
@
@
__ @
/\_\@
\/_/@
@
@@
__@
/ /@
/ / @
/ / @
/ / @
/_/ @
/_/ @
@
@@
__ @
/'__`\ @
/\ \/\ \ @
\ \ \ \ \ @
\ \ \_\ \@
\ \____/@
\/___/ @
@
@@
_ @
/' \ @
/\_, \ @
\/_/\ \ @
\ \ \ @
\ \_\@
\/_/@
@
@@
___ @
/'___`\ @
/\_\ /\ \ @
\/_/// /__ @
// /_\ \@
/\______/@
\/_____/ @
@
@@
__ @
/'__`\ @
/\_\L\ \ @
\/_/_\_<_ @
/\ \L\ \@
\ \____/@
\/___/ @
@
@@
__ __ @
/\ \\ \ @
\ \ \\ \ @
\ \ \\ \_ @
\ \__ ,__\@
\/_/\_\_/@
\/_/ @
@
@@
______ @
/\ ___\ @
\ \ \__/ @
\ \___``\ @
\/\ \L\ \@
\ \____/@
\/___/ @
@
@@
____ @
/'___\ @
/\ \__/ @
\ \ _``\ @
\ \ \L\ \@
\ \____/@
\/___/ @
@
@@
________ @
/\_____ \@
\/___//'/'@
/' /' @
/' /' @
/\_/ @
\// @
@
@@
__ @
/'_ `\ @
/\ \L\ \ @
\/_> _ <_ @
/\ \L\ \@
\ \____/@
\/___/ @
@
@@
__ @
/'_ `\ @
/\ \L\ \ @
\ \___, \ @
\/__,/\ \ @
\ \_\@
\/_/@
@
@@
@
@
__ @
/\_\ @
\/_/_ @
/\_\@
\/_/@
@
@@
@
@
__ @
/\_\ @
\/_/_ @
/\ \@
\ \/@
\/ @
@@
___ @
/ / @
/ / @
/< < @
\ `\ `\ @
`\ `\_|@
`\// @
@
@@
@
_______ @
/\______\ @
\/______/_ @
/\______\@
\/______/@
@
@
@@
__ @
/\ `\ @
\ `\ `\ @
`\ > >@
/ / @
/\_/ @
\// @
@
@@
_ @
/'_`\ @
/\_\/\`\@
\/_//'/'@
/\_\ @
\/\_\@
\/_/@
@
@@
@
__ @
/'_`\_ @
/'/'_` \ @
/\ \ \L\ \ @
\ \ `\__,_\@
\ `\_____\@
`\/_____/@
@@
______ @
/\ _ \ @
\ \ \L\ \ @
\ \ __ \ @
\ \ \/\ \ @
\ \_\ \_\@
\/_/\/_/@
@
@@
____ @
/\ _`\ @
\ \ \L\ \ @
\ \ _ <' @
\ \ \L\ \@
\ \____/@
\/___/ @
@
@@
____ @
/\ _`\ @
\ \ \/\_\ @
\ \ \/_/_ @
\ \ \L\ \@
\ \____/@
\/___/ @
@
@@
____ @
/\ _`\ @
\ \ \/\ \ @
\ \ \ \ \ @
\ \ \_\ \@
\ \____/@
\/___/ @
@
@@
____ @
/\ _`\ @
\ \ \L\_\ @
\ \ _\L @
\ \ \L\ \@
\ \____/@
\/___/ @
@
@@
____ @
/\ _`\ @
\ \ \L\_\@
\ \ _\/@
\ \ \/ @
\ \_\ @
\/_/ @
@
@@
____ @
/\ _`\ @
\ \ \L\_\ @
\ \ \L_L @
\ \ \/, \@
\ \____/@
\/___/ @
@
@@
__ __ @
/\ \/\ \ @
\ \ \_\ \ @
\ \ _ \ @
\ \ \ \ \ @
\ \_\ \_\@
\/_/\/_/@
@
@@
______ @
/\__ _\ @
\/_/\ \/ @
\ \ \ @
\_\ \__ @
/\_____\@
\/_____/@
@
@@
_____ @
/\___ \ @
\/__/\ \ @
_\ \ \ @
/\ \_\ \@
\ \____/@
\/___/ @
@
@@
__ __ @
/\ \/\ \ @
\ \ \/'/' @
\ \ , < @
\ \ \\`\ @
\ \_\ \_\@
\/_/\/_/@
@
@@
__ @
/\ \ @
\ \ \ @
\ \ \ __ @
\ \ \L\ \@
\ \____/@
\/___/ @
@
@@
@
/'\_/`\ @
/\ \ @
\ \ \__\ \ @
\ \ \_/\ \ @
\ \_\\ \_\@
\/_/ \/_/@
@
@@
__ __ @
/\ \/\ \ @
\ \ `\\ \ @
\ \ , ` \ @
\ \ \`\ \ @
\ \_\ \_\@
\/_/\/_/@
@
@@
_____ @
/\ __`\ @
\ \ \/\ \ @
\ \ \ \ \ @
\ \ \_\ \ @
\ \_____\@
\/_____/@
@
@@
____ @
/\ _`\ @
\ \ \L\ \@
\ \ ,__/@
\ \ \/ @
\ \_\ @
\/_/ @
@
@@
_____ @
/\ __`\ @
\ \ \/\ \ @
\ \ \ \ \ @
\ \ \\'\\ @
\ \___\_\@
\/__//_/@
@
@@
____ @
/\ _`\ @
\ \ \L\ \ @
\ \ , / @
\ \ \\ \ @
\ \_\ \_\@
\/_/\/ /@
@
@@
____ @
/\ _`\ @
\ \,\L\_\ @
\/_\__ \ @
/\ \L\ \ @
\ `\____\@
\/_____/@
@
@@
______ @
/\__ _\ @
\/_/\ \/ @
\ \ \ @
\ \ \ @
\ \_\@
\/_/@
@
@@
__ __ @
/\ \/\ \ @
\ \ \ \ \ @
\ \ \ \ \ @
\ \ \_\ \ @
\ \_____\@
\/_____/@
@
@@
__ __ @
/\ \/\ \ @
\ \ \ \ \ @
\ \ \ \ \ @
\ \ \_/ \@
\ `\___/@
`\/__/ @
@
@@
__ __ @
/\ \ __/\ \ @
\ \ \/\ \ \ \ @
\ \ \ \ \ \ \ @
\ \ \_/ \_\ \@
\ `\___x___/@
'\/__//__/ @
@
@@
__ __ @
/\ \ /\ \ @
\ `\`\/'/' @
`\/ > < @
\/'/\`\ @
/\_\\ \_\@
\/_/ \/_/@
@
@@
__ __ @
/\ \ /\ \@
\ `\`\\/'/@
`\ `\ /' @
`\ \ \ @
\ \_\@
\/_/@
@
@@
________ @
/\_____ \ @
\/____//'/' @
//'/' @
//'/'___ @
/\_______\@
\/_______/@
@
@@
____ @
/\ _\ @
\ \ \/ @
\ \ \ @
\ \ \_ @
\ \___\@
\/___/@
@
@@
__ @
/\ `\ @
\`\ `\ @
`\`\ `\ @
`\`\ `\ @
`\`\__\@
`\/__/@
@
@@
____ @
/\__ \ @
\/_/\ \ @
\ \ \ @
\_\ \ @
/\___\@
\/___/@
@
@@
__ @
/ `\ @
/\_/\_\ @
\//\// $ @
$ $ @
$ $@
@
@
@@
@
@
@
@
@
$ $ @
$_______ @
/\______\@
\/______/@@
__ @
/\ \ @
\ \\$ @
\// $ @
$ $ @
$ $@
@
@
@@
@
@
__ @
/'__`\ @
/\ \L\.\_ @
\ \__/.\_\@
\/__/\/_/@
@
@@
__ @
/\ \ @
\ \ \____ @
\ \ '__`\ @
\ \ \L\ \@
\ \_,__/@
\/___/ @
@
@@
@
@
___ @
/'___\ @
/\ \__/ @
\ \____\@
\/____/@
@
@@
__ @
/\ \ @
\_\ \ @
/'_` \ @
/\ \L\ \ @
\ \___,_\@
\/__,_ /@
@
@@
@
@
__ @
/'__`\ @
/\ __/ @
\ \____\@
\/____/@
@
@@
___ @
/'___\ @
/\ \__/ @
\ \ ,__\@
\ \ \_/@
\ \_\ @
\/_/ @
@
@@
@
@
__ @
/'_ `\ @
/\ \L\ \ @
\ \____ \ @
\/___L\ \@
/\____/@
\_/__/ @@
__ @
/\ \ @
\ \ \___ @
\ \ _ `\ @
\ \ \ \ \ @
\ \_\ \_\@
\/_/\/_/@
@
@@
@
__ @
/\_\ @
\/\ \ @
\ \ \ @
\ \_\@
\/_/@
@
@@
@
__ @
/\_\ @
\/\ \ @
\ \ \ @
_\ \ \ @
/\ \_\ \@
\ \____/@
\/___/ @@
__ @
/\ \ @
\ \ \/'\ @
\ \ , < @
\ \ \\`\ @
\ \_\ \_\@
\/_/\/_/@
@
@@
___ @
/\_ \ @
\//\ \ @
\ \ \ @
\_\ \_ @
/\____\@
\/____/@
@
@@
@
@
___ ___ @
/' __` __`\ @
/\ \/\ \/\ \ @
\ \_\ \_\ \_\@
\/_/\/_/\/_/@
@
@@
@
@
___ @
/' _ `\ @
/\ \/\ \ @
\ \_\ \_\@
\/_/\/_/@
@
@@
@
@
___ @
/ __`\ @
/\ \L\ \@
\ \____/@
\/___/ @
@
@@
@
@
_____ @
/\ '__`\ @
\ \ \L\ \@
\ \ ,__/@
\ \ \/ @
\ \_\ @
\/_/ @@
@
@
__ @
/'__`\ @
/\ \L\ \ @
\ \___, \ @
\/___/\ \ @
\ \_\@
\/_/@@
@
@
_ __ @
/\`'__\@
\ \ \/ @
\ \_\ @
\/_/ @
@
@@
@
@
____ @
/',__\ @
/\__, `\@
\/\____/@
\/___/ @
@
@@
__ @
/\ \__ @
\ \ ,_\ @
\ \ \/ @
\ \ \_ @
\ \__\@
\/__/@
@
@@
@
@
__ __ @
/\ \/\ \ @
\ \ \_\ \@
\ \____/@
\/___/ @
@
@@
@
@
__ __ @
/\ \/\ \ @
\ \ \_/ |@
\ \___/ @
\/__/ @
@
@@
@
@
__ __ __ @
/\ \/\ \/\ \ @
\ \ \_/ \_/ \@
\ \___x___/'@
\/__//__/ @
@
@@
@
@
__ _ @
/\ \/'\ @
\/> </ @
/\_/\_\@
\//\/_/@
@
@@
@
@
__ __ @
/\ \/\ \ @
\ \ \_\ \ @
\/`____ \ @
`/___/> \@
/\___/@
\/__/ @@
@
@
____ @
/\_ ,`\ @
\/_/ /_ @
/\____\@
\/____/@
@
@@
_ @
/' \@
\ ,/'@
<' \ @
< \ `\ @
\`\__\@
\/__/@
@
@@
__ @
/\ \ @
\ \ \ @
\ \ \ @
\ \ \ @
\ \ \ @
\ \ \ @
\ \_\@
\/_/@@
__ @
/\ `\ @
\`\ \ @
\ \ `>@
//' \ @
/\__/' @
\/_/ @
@
@@
_ _ @
/' \/' \ @
/\_/\__//$ @
\//\/__/ $ @
$ $@
@
@
@
@@
__ __ @
/\_\/\_\ @
\/\ _ \ @
\ \ \L\ \ @
\ \ __ \ @
\ \_\/\_\@
\/_/\/_/@
@
@@
__ __ @
/\_\/\_\ @
\/\ __ \ @
\ \ \/\ \ @
\ \ \_\ \ @
\ \_____\@
\/_____/@
@
@@
__ __ @
/\_\/\_\ @
\/\ \/\ \ @
\ \ \ \ \ @
\ \ \_\ \ @
\ \_____\@
\/_____/@
@
@@
__ __ @
/\_\/\_\ @
\/_/\/_/_ @
/'_` \ @
/\ \L\ \ @
\ `\__,_\@
`\/_,__/@
@
@@
__ __ @
/\_\/\_\ @
\/_/\/_/ @
/'_`\ @
/\ \L\ \@
\ `\___/@
`\/__/ @
@
@@
__ __ @
/\_\ \_\ @
\/_/\/_/_ @
/\ \/\ \ @
\ \ \_\ \@
\ `\___/@
`\/__/ @
@
@@
______ @
/\ __ \ @
\ \ \/\ \ @
\ \ \<_<_ @
\ \ \ \ \@
\ \ \\_/@
\ \_\/ @
\/_/ @
@@

File diff suppressed because one or more lines are too long

44
char.go Normal file
View File

@@ -0,0 +1,44 @@
package figlet4go
import (
"errors"
"strings"
"github.com/fatih/color"
)
type AsciiChar struct {
// Slice with the lines of the Char
Lines []string
// Color of the char
Color color.Attribute
}
func NewAsciiChar(font *font, char rune) (*AsciiChar, error) {
// If not ascii, throw an error
if char < 0 || char > 127 {
return nil, errors.New("Not Ascii")
}
height := font.height
beginRow := (int(char) - 32) * height
lines := make([]string, height)
for i := 0; i < height; i++ {
row := font.fontSlice[beginRow + i]
row = strings.Replace(row, "@", "", -1)
row = strings.Replace(row, font.hardblank, " ", -1)
lines[i] = row
}
return &AsciiChar{Lines: lines}, nil
}
// Return a line of the char as string with color if set
func (char *AsciiChar) GetLine(index int) string {
if char.Color != 0 {
colorFunc := color.New(char.Color).SprintFunc()
return colorFunc(char.Lines[index])
}
return char.Lines[index]
}

View File

@@ -1 +1,32 @@
// Print a colored string with the 'standard' figlet font
package main
import (
"fmt"
"github.com/probandula/figlet4go"
// This package is used to define the colors
"github.com/fatih/color"
)
const str string = "Colored"
func main() {
ascii := figlet4go.NewAsciiRender()
// Add the colors to the options
options := figlet4go.NewRenderOptions()
options.FontColor = []color.Attribute{
color.FgGreen,
color.FgYellow,
color.FgCyan,
}
// Use the RenderOpts instead of the Render method
renderStr, err := ascii.RenderOpts(str, options)
if err != nil {
panic(err)
}
fmt.Print(renderStr)
}

View File

@@ -15,7 +15,10 @@ func main() {
ascii := figlet4go.NewAsciiRender()
// Render the string
renderStr, _ := ascii.Render(str)
renderStr, err := ascii.Render(str)
if err != nil {
panic(err)
}
// Print the string
fmt.Print(renderStr)

View File

@@ -1,5 +1,27 @@
// Print a string with another font
package main
func main() {
import (
"fmt"
"github.com/probandula/figlet4go"
)
const str string = "otherfont"
func main() {
ascii := figlet4go.NewAsciiRender()
// Add the font name to the options
options := figlet4go.NewRenderOptions()
// This font is included so you don't have to specify a path with 'ascii.LoadFont(/path/to/fonts/)'
options.FontName = "larry3d"
// Use the RenderOpts instead of the Render method
renderStr, err := ascii.RenderOpts(str, options)
if err != nil {
panic(err)
}
fmt.Print(renderStr)
}

25
font.go
View File

@@ -57,16 +57,26 @@ func (this *fontManager) loadFont(fontPath string) error {
// Load the default font
func (this *fontManager) loadBuildInFont() error {
fontStr, err := Asset("standard.flf")
if err != nil {
panic(err)
// Default fonts to load
defaultFonts := []string{
"standard",
"larry3d",
}
font, err := this.parseFontContent(string(fontStr))
if err != nil {
return err
// Load each default font
for _, name := range defaultFonts {
fontStr, err := Asset(name + ".flf")
if err != nil {
return err
}
font, err := this.parseFontContent(string(fontStr))
if err != nil {
return err
}
this.fontLib[name] = font
}
this.fontLib[defaultFontName] = font
return nil
}
@@ -126,6 +136,7 @@ func (this *fontManager) parseFontContent(cont string) (*font, error) {
}
// Get a font by name
// Better error handling. Why is there one in the return if not used?
func (this *fontManager) getFont(fontName string) (*font, error) {
font, ok := this.fontLib[fontName]
if !ok {

View File

@@ -1,10 +1,7 @@
package figlet4go
import (
"errors"
"fmt"
"github.com/fatih/color"
"strings"
)
// RenderOptions are used to set color or maybe future
@@ -43,67 +40,65 @@ func (ar *AsciiRender) LoadFont(fontPath string) error {
}
// Render a string with the default options
// Calls the RenderOpts method with a new RenderOptions object
func (ar *AsciiRender) Render(str string) (string, error) {
return ar.render(str, NewRenderOptions())
return ar.RenderOpts(str, NewRenderOptions())
}
// Render a string with special RenderOptions
func (ar *AsciiRender) RenderOpts(str string, opts *RenderOptions) (string, error) {
return ar.render(str, opts)
}
// Can be called from the user (if options wished) or the above Render method
// Contains the whole rendering logic
func (ar *AsciiRender) RenderOpts(str string, opt *RenderOptions) (string, error) {
colored := len(opt.FontColor) > 0
func (this *AsciiRender) convertChar(font *font, char rune) ([]string, error) {
if char < 0 || char > 127 {
return nil, errors.New("Not Ascii")
// Load the font
font, err := ar.fontMgr.getFont(opt.FontName)
if err != nil {
return "", err
}
height := font.height
begintRow := (int(char) - 32) * height
// Slice holding the chars
chars := []*AsciiChar{}
word := make([]string, height, height)
// Index of the current color
curColorIndex := 0
for i := 0; i < height; i++ {
row := font.fontSlice[begintRow+i]
row = strings.Replace(row, "@", "", -1)
row = strings.Replace(row, font.hardblank, " ", -1)
word[i] = row
}
return word, nil
}
func (this *AsciiRender) render(asciiStr string, opt *RenderOptions) (string, error) {
font, _ := this.fontMgr.getFont(opt.FontName)
wordlist := make([][]string, 0)
for _, char := range asciiStr {
word, err := this.convertChar(font, char)
// Foreach char create the ascii char
for _, char := range str {
// AsciiChar
asciiChar, err := NewAsciiChar(font, char)
if err != nil {
return "", err
}
wordlist = append(wordlist, word)
// Set color if given
if colored {
// Start colors from beginning if length is reached
if curColorIndex == len(opt.FontColor) {
curColorIndex = 0
}
// Assign color and increment the index
asciiChar.Color = opt.FontColor[curColorIndex]
curColorIndex++
}
// Append the char to the chars slice
chars = append(chars, asciiChar)
}
// Result which will be returned
result := ""
wordColorFunc := make([]func(a ...interface{}) string, len(wordlist))
for i, _ := range wordColorFunc {
if i < len(opt.FontColor) {
wordColorFunc[i] = color.New(opt.FontColor[i]).SprintFunc()
} else {
wordColorFunc[i] = fmt.Sprint
}
}
for i := 0; i < font.height; i++ {
for j := 0; j < len(wordlist); j++ {
result += wordColorFunc[j]((wordlist[j][i]))
// Foreach line of the font height
for curLine := 0; curLine < font.height; curLine++ {
// Add the current line of the char to the result
for i, _ := range chars {
result += chars[i].GetLine(curLine)
}
// A new line at the end
result += "\n"
}
return result, nil
}