Added --width, --height and --font flags

This commit is contained in:
Zoraiz 2021-08-06 20:10:26 +05:00
parent 367f986d8a
commit d11ab9f718
13 changed files with 230 additions and 67 deletions

View File

@ -42,6 +42,7 @@ archives:
files:
- LICENSE.txt
- README.md
replacements:
linux: Linux
@ -50,7 +51,7 @@ archives:
amd64: amd64_64bit
arm64: arm64_64bit
386: i386_32bit
arm: arm_32bit
arm: armv6_32bit
checksum:
name_template: 'sha256_checksums.txt'
@ -111,4 +112,4 @@ nfpms:
bindir: /usr/bin
rpm:
compression: lzma
compression: lzma

View File

@ -1,4 +1,3 @@
# ascii-image-converter
[![release version](https://img.shields.io/github/v/release/TheZoraiz/ascii-image-converter?label=Latest%20Version)](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
@ -102,7 +101,7 @@ Now, open a terminal in the same directory and execute this command:
```
sudo cp ascii-image-converter /usr/local/bin/
```
Now you can use ascii-image-converter in the terminal. Execute "ascii-image-converter -h" for more details.
Now you can use ascii-image-converter in the terminal. Execute `ascii-image-converter -h` for more details.
### Windows
@ -115,7 +114,7 @@ Download the archive for your Windows architecture [here](https://github.com/The
* In the Edit Environment Variable window, click "New" and then paste the path of the folder that you copied initially.
* Click "Ok" on all open windows.
Now, restart any open command prompt and execute "ascii-image-converter -h" for more details.
Now, restart any open command prompt and execute `ascii-image-converter -h` for more details.
<br>
@ -185,6 +184,36 @@ ascii-image-converter [image paths/urls] -d 60,30
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/dimensions.gif">
</p>
#### --width OR -W
> **Note:** Don't immediately append another flag with -W
Set width of ascii art. Height is calculated according to aspect ratio.
```
ascii-image-converter [image paths/urls] -W <width>
# Or
ascii-image-converter [image paths/urls] --width <width>
```
Example:
```
ascii-image-converter [image paths/urls] -W 60
```
#### --height OR -H
> **Note:** Don't immediately append another flag with -H
Set height of ascii art. Width is calculated according to aspect ratio.
```
ascii-image-converter [image paths/urls] -H <height>
# Or
ascii-image-converter [image paths/urls] --height <height>
```
Example:
```
ascii-image-converter [image paths/urls] -H 60
```
#### --map OR -m
> **Note:** Don't immediately append another flag with -m
@ -304,6 +333,16 @@ Saves the passed GIF as an ascii art GIF with the name `<image-name>-ascii-art.g
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/save.gif">
</p>
#### --font
> **Note:** This flag will be ignored if `--save-img` or `--save-gif` flags are not set
This flag takes path to a font .ttf file that will be used to set font in saved .png or .gif files if `--save-img` or `--save-gif` flags are set.
```
ascii-image-converter [image paths/urls] -s . --font /path/to/font-file.ttf
```
#### --formats
Display supported input formats.

Binary file not shown.

Binary file not shown.

View File

@ -97,13 +97,13 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
var imgSet [][]imgManip.AsciiPixel
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, flipX, flipY, full)
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full)
if err != nil {
fmt.Println(err)
fmt.Println("Error:", err)
os.Exit(0)
}
asciiCharSet := imgManip.ConvertToAscii(imgSet, negative, colored, complex, customMap)
asciiCharSet := imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap)
gifFramesSlice[i].asciiCharSet = asciiCharSet
gifFramesSlice[i].delay = originalGif.Delay[i]
@ -182,7 +182,7 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
colored || grayscale,
)
if err != nil {
fmt.Println(err)
fmt.Println("Error:", err)
os.Exit(0)
}

View File

@ -26,9 +26,7 @@ import (
imgManip "github.com/TheZoraiz/ascii-image-converter/image_manipulation"
)
/*
This function decodes the passed image and returns as an ascii art string, optionaly saving it as a .txt and/or .png file
*/
// This function decodes the passed image and returns an ascii art string, optionaly saving it as a .txt and/or .png file
func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byte, localImg *os.File) (string, error) {
var (
@ -45,12 +43,12 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
}
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, flipX, flipY, full)
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full)
if err != nil {
return "", err
}
asciiSet := imgManip.ConvertToAscii(imgSet, negative, colored, complex, customMap)
asciiSet := imgManip.ConvertToAsciiChars(imgSet, negative, colored, complex, customMap)
// Save ascii art as .png image before printing it, if --save-img flag is passed
if saveImagePath != "" {

View File

@ -33,25 +33,70 @@ import (
_ "golang.org/x/image/webp"
"github.com/asaskevich/govalidator"
"github.com/golang/freetype/truetype"
)
type Flags struct {
Dimensions []int
Complex bool
SaveTxtPath string
// Set dimensions of ascii art. Accepts a slice of 2 integers
// e.g. []int{60,30}.
// This overrides Flags.Width and Flags.Height
Dimensions []int
// Set width of ascii art while calculating height from aspect ratio.
// Setting this along with Flags.Height will throw an error
Width int
// Set height of ascii art while calculating width from aspect ratio.
// Setting this along with Flags.Width will throw an error
Height int
// Use set of 69 characters instead of the default 10
Complex bool
// Path to save ascii art .txt file
SaveTxtPath string
// Path to save ascii art .png file
SaveImagePath string
SaveGifPath string
Negative bool
Colored bool
Grayscale bool
CustomMap string
FlipX bool
FlipY bool
Full bool
// Path to save ascii art .gif file, if gif is passed
SaveGifPath string
// Invert ascii art character mapping as well as colors
Negative bool
// Keep colors from the original image. This uses the True color codes for
// the terminal and will work on saved .png and .gif files as well.
// This overrides Flags.Grayscale
Colored bool
// Keep grayscale colors from the original image. This uses the True color
// codes for the terminal and will work on saved .png and .gif files as well
Grayscale bool
// Pass custom ascii art characters as a string.
// This overrides Flags.Complex
CustomMap string
// Flip ascii art horizontally
FlipX bool
// Flip ascii art vertically
FlipY bool
// Use terminal width to calculate ascii art size while keeping aspect ratio.
// This overrides Flags.Dimensions, Flags.Width and Flags.Height
Full bool
// File path to a font .ttf file to use when saving ascii art gif or png file.
// This will be ignored if Flags.SaveImagePath or Flags.SaveImagePath are not set
FontFilePath string
}
var (
dimensions []int
width int
height int
complex bool
saveTxtPath string
saveImagePath string
@ -63,6 +108,7 @@ var (
flipX bool
flipY bool
full bool
fontPath string
)
// Return default configuration for flags.
@ -71,6 +117,8 @@ func DefaultFlags() Flags {
return Flags{
Complex: false,
Dimensions: nil,
Width: 0,
Height: 0,
SaveTxtPath: "",
SaveImagePath: "",
SaveGifPath: "",
@ -81,30 +129,14 @@ func DefaultFlags() Flags {
FlipX: false,
FlipY: false,
Full: false,
FontFilePath: "",
}
}
/*
Convert takes an image or gif path/url as its first argument
and a Flags literal as the second argument, with which it alters
Convert() takes an image or gif path/url as its first argument
and a aic_package.Flags literal as the second argument, with which it alters
the returned ascii art string.
The "flags" argument should be declared as follows before passing:
flags := aic_package.Flags{
Complex: bool, // Pass true for using complex character set
Dimensions: []int, // Pass 2 integer dimensions. Pass nil to ignore
SaveTxtPath: string, // System path to save the ascii art string as a .txt file. Pass "" to ignore
SavefilePath: string, // System path to save the ascii art string as a .png file. Pass "" to ignore
SaveGifPath : string, // System path to save the ascii art gif as a .gif file. Pass "" to ignore
Negative: bool, // Pass true for negative color-depth ascii art
Colored: bool, // Pass true for returning colored ascii string
Grayscale: bool // Pass true for returning grayscale ascii string
CustomMap: string, // Custom map of ascii chars e.g. " .-+#@" . Nullifies "complex" flag. Pass "" to ignore.
FlipX: bool, // Pass true to return horizontally flipped ascii art
FlipY: bool, // Pass true to return vertically flipped ascii art
Full: bool, // Pass true to use full terminal as ascii height
}
*/
func Convert(filePath string, flags Flags) (string, error) {
@ -113,6 +145,8 @@ func Convert(filePath string, flags Flags) (string, error) {
} else {
dimensions = flags.Dimensions
}
width = flags.Width
height = flags.Height
complex = flags.Complex
saveTxtPath = flags.SaveTxtPath
saveImagePath = flags.SaveImagePath
@ -124,6 +158,7 @@ func Convert(filePath string, flags Flags) (string, error) {
flipX = flags.FlipX
flipY = flags.FlipY
full = flags.Full
fontPath = flags.FontFilePath
// Declared at the start since some variables are initially used in conditional blocks
var (
@ -151,7 +186,7 @@ func Convert(filePath string, flags Flags) (string, error) {
defer retrievedImage.Body.Close()
urlImgName = path.Base(filePath)
fmt.Printf(" \r") // To erase "Fetching image from url..." text from console
fmt.Printf(" \r") // To erase "Fetching image from url..." text from terminal
} else {
@ -163,6 +198,19 @@ func Convert(filePath string, flags Flags) (string, error) {
}
// If path to font file is provided, use it
if fontPath != "" {
fontFile, err := ioutil.ReadFile(fontPath)
if err != nil {
return "", fmt.Errorf("unable to open font file: %v", err)
}
// tempFont is globally declared in aic_package/create_ascii_image.go
if tempFont, err = truetype.Parse(fontFile); err != nil {
return "", fmt.Errorf("unable to parse font file: %v", err)
}
}
if path.Ext(filePath) == ".gif" {
return pathIsGif(filePath, urlImgName, pathIsURl, urlImgBytes, localFile)
} else {

View File

@ -90,9 +90,9 @@ func createGifFrameToSave(asciiArt [][]imgManip.AsciiChar, img image.Image, colo
return nil, err
}
// Font size increased during assignment to become more visible. This will not affect image drawing
robotoBoldFontFace := truetype.NewFace(tempFont, &truetype.Options{Size: fontSize * 1.5})
fontFace := truetype.NewFace(tempFont, &truetype.Options{Size: fontSize * 1.5})
dc.SetFontFace(robotoBoldFontFace)
dc.SetFontFace(fontFace)
// Font color of text on picture is white by default
dc.SetColor(color.White)

View File

@ -27,14 +27,13 @@ import (
"github.com/golang/freetype/truetype"
)
//go:embed RobotoMono-Bold.ttf
//go:embed Hack-Regular.ttf
var embeddedFontFile []byte
var tempFont *truetype.Font
// Load embedded font
func init() {
// Error not handled because the same font file will always be used
tempFont, _ = truetype.Parse(embeddedFontFile)
}
@ -76,8 +75,8 @@ func createImageToSave(asciiArt [][]imgManip.AsciiChar, colored bool, saveImageP
dc.DrawImage(tempImg, 0, 0)
robotoBoldFontFace := truetype.NewFace(tempFont, &truetype.Options{Size: constant * 1.5})
dc.SetFontFace(robotoBoldFontFace)
fontFace := truetype.NewFace(tempFont, &truetype.Options{Size: constant * 1.5})
dc.SetFontFace(fontFace)
// Font color of text on picture is white by default
dc.SetColor(color.White)

View File

@ -33,6 +33,8 @@ var (
cfgFile string
complex bool
dimensions []int
width int
height int
saveTxtPath string
saveImagePath string
saveGifPath string
@ -44,12 +46,13 @@ var (
flipX bool
flipY bool
full bool
fontFile string
// Root commands
rootCmd = &cobra.Command{
Use: "ascii-image-converter [image paths/urls]",
Short: "Converts images and gifs into ascii art",
Version: "1.4.2",
Version: "1.5.0",
Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",
// Not RunE since help text is getting larger and seeing it for every error impacts user experience
@ -62,6 +65,8 @@ var (
flags := aic_package.Flags{
Complex: complex,
Dimensions: dimensions,
Width: width,
Height: height,
SaveTxtPath: saveTxtPath,
SaveImagePath: saveImagePath,
SaveGifPath: saveGifPath,
@ -72,6 +77,7 @@ var (
FlipX: flipX,
FlipY: flipY,
Full: full,
FontFilePath: fontFile,
}
for _, imagePath := range args {
@ -168,6 +174,40 @@ func checkInputAndFlags(args []string) bool {
}
}
if width != 0 || height != 0 {
if width != 0 && height != 0 {
fmt.Printf("Error: both --width and --height can't be set. Use --dimensions instead\n\n")
return true
} else {
defaultTermWidth, _, err := winsize.GetTerminalSize()
if err != nil {
fmt.Printf("Error: %v\n\n", err)
return true
}
// Check if set width exceeds terminal
defaultTermWidth -= 1
if width > defaultTermWidth {
fmt.Printf("Error: set width must be lower than terminal width\n\n")
return true
}
if width < 0 {
fmt.Printf("Error: invalid value for width\n\n")
return true
}
if height < 0 {
fmt.Printf("Error: invalid value for height\n\n")
return true
}
}
}
return false
}
@ -188,17 +228,20 @@ func init() {
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with original colors\n(Can work with the --negative flag)\n(Overrides --grayscale flag)\n")
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 60,30 (defaults to terminal height)\n")
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 60,30 (defaults to terminal height)\n(Overrides --width and --height flags)\n")
rootCmd.PersistentFlags().IntVarP(&width, "width", "W", 0, "Set width for ascii art in CHARACTER length\nHeight is kept to aspect ratio\ne.g. -W 60\n")
rootCmd.PersistentFlags().IntVarP(&height, "height", "H", 0, "Set height for ascii art in CHARACTER length\nWidth is kept to aspect ratio\ne.g. -H 60\n")
rootCmd.PersistentFlags().StringVarP(&customMap, "map", "m", "", "Give custom ascii characters to map against\nOrdered from darkest to lightest\ne.g. -m \" .-+#@\" (Quotation marks excluded from map)\n(Overrides --complex flag)\n")
rootCmd.PersistentFlags().BoolVarP(&grayscale, "grayscale", "g", false, "Display grayscale ascii art\n(Can work with --negative flag)\n")
rootCmd.PersistentFlags().BoolVarP(&complex, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
rootCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Use largest dimensions for ascii art that\nfill the terminal width\n(Overrides --dimensions flag)\n")
rootCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Use largest dimensions for ascii art\nthat fill the terminal width\n(Overrides --dimensions, --width and --height flags)\n")
rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors\n")
rootCmd.PersistentFlags().BoolVarP(&flipX, "flipX", "x", false, "Flip ascii art horizontally\n")
rootCmd.PersistentFlags().BoolVarP(&flipY, "flipY", "y", false, "Flip ascii art vertically\n")
rootCmd.PersistentFlags().StringVarP(&saveImagePath, "save-img", "s", "", "Save ascii art as a .png file\nFormat: <image-name>-ascii-art.png\nImage will be saved in passed path\n(pass . for current directory)\n")
rootCmd.PersistentFlags().StringVar(&saveTxtPath, "save-txt", "", "Save ascii art as a .txt file\nFormat: <image-name>-ascii-art.txt\nFile will be saved in passed path\n(pass . for current directory)\n")
rootCmd.PersistentFlags().StringVar(&saveGifPath, "save-gif", "", "(Experimental)\nIf input is a gif, save it as a .gif file\nFormat: <image-name>-ascii-art.gif\nGif will be saved in passed path\n(pass . for current directory)\n")
rootCmd.PersistentFlags().StringVar(&fontFile, "font", "", "Set font for --save-img and --save-gif flags\nPass file path to font .ttf file\ne.g. --font ./RobotoMono-Regular.ttf\n(Defaults to embedded Hack-Regular)\n")
rootCmd.PersistentFlags().BoolVar(&formatsTrue, "formats", false, "Display supported input formats\n")
rootCmd.PersistentFlags().BoolP("help", "h", false, "Help for "+rootCmd.Name()+"\n")

View File

@ -124,7 +124,7 @@ type AsciiChar struct {
//
// If complex parameter is true, values are compared to 69 levels of color density in ASCII characters.
// Otherwise, values are compared to 10 levels of color density in ASCII characters.
func ConvertToAscii(imgSet [][]AsciiPixel, negative, colored, complex bool, customMap string) [][]AsciiChar {
func ConvertToAsciiChars(imgSet [][]AsciiPixel, negative, colored, complex bool, customMap string) [][]AsciiChar {
height := len(imgSet)
width := len(imgSet[0])

View File

@ -37,7 +37,7 @@ type AsciiPixel struct {
//
// The returned 2D AsciiPixel slice contains each corresponding pixel's values. Grayscale value
// ranges from 0 to 65535, while RGB values are separate.
func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY, full bool) ([][]AsciiPixel, error) {
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full bool) ([][]AsciiPixel, error) {
var asciiWidth, asciiHeight int
var smallImg image.Image
@ -59,9 +59,50 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY, full
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
} else if len(dimensions) == 0 {
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
// If either width or height is set and dimensions aren't given
// Following code in this condition calculates aspect ratio according to terminal height
if width > terminalWidth-1 {
return nil, fmt.Errorf("set width must be lower than terminal width")
}
if width != 0 && height == 0 {
// If width is set and height is not set, use width to calculate aspect ratio
asciiWidth = width
smallImg = resize.Resize(uint(asciiWidth), 0, img, resize.Lanczos3)
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
asciiHeight = int(0.5 * float32(asciiHeight))
if asciiHeight == 0 {
asciiHeight = 1
}
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
} else if height != 0 && width == 0 {
// If height is set and width is not set, use height to calculate aspect ratio
asciiHeight = height
smallImg = resize.Resize(0, uint(asciiHeight), img, resize.Lanczos3)
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
asciiWidth = int(2 * float32(asciiWidth))
if asciiWidth > terminalWidth-1 {
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
}
smallImg = resize.Resize(uint(asciiWidth), uint(asciiHeight), img, resize.Lanczos3)
} else {
return nil, fmt.Errorf("both width and height can't be set. Use dimensions instead")
}
} else if len(dimensions) == 0 {
// This condition calculates aspect ratio according to terminal height
asciiHeight = terminalHeight - 1
@ -95,13 +136,7 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY, full
//
// If there are passed dimensions, check whether the width exceeds terminal width
if len(dimensions) > 0 && !full {
defaultTermWidth, _, err := winsize.GetTerminalSize()
if err != nil {
return nil, err
}
defaultTermWidth -= 1
if dimensions[0] > defaultTermWidth {
if dimensions[0] > terminalWidth-1 {
return nil, fmt.Errorf("set width must be lower than terminal width")
}
}
@ -123,8 +158,8 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, flipX, flipY, full
oldPixel := smallImg.At(x, y)
grayPixel := color.GrayModel.Convert(oldPixel)
// We only need Red from Red, Green, Blue (RGB) for grayscaleValue in AsciiPixel since they have the same value for grayscale images
r1, g1, b1, _ := grayPixel.RGBA()
// We only need Red from Red, Green, Blue (RGB) for charDepth in AsciiPixel since they have the same value for grayscale images
charDepth := r1
r1 = uint32(r1 / 257)
g1 = uint32(g1 / 257)

View File

@ -1,6 +1,6 @@
name: ascii-image-converter
base: core18
version: "1.4.2"
version: "1.5.0"
summary: Convert images and gifs into ascii art
description: |
This tool converts images and gifs into ascii format and prints them onto the terminal window.