Documentation
¶
Overview ¶
Package shot renders a grid of terminal cells to PNG, SVG, HTML, ANSI or plain text, with an optional frame around it.
A Grid holds resolved cells: each one has its text, its colours as RGB and its attributes. Build one cell by cell with NewGrid, or read a terminal emulator's screen with FromScreen. Render then turns the grid into the bytes of one format. A render is one call from grid to bytes, with no goroutines and no timers.
PNG draws text with Frame.Font, then each face of Frame.Fallback, then the Go Mono it embeds. Box drawing, block elements, braille and powerline glyphs are drawn as shapes, so they join up whatever the font. A glyph no face has draws as a dotted box. MissingGlyphs lists those glyphs, so a caller with a font database can add fallback faces for them.
Index ¶
- Variables
- func Downscale(img *image.RGBA, maxW, maxH int) *image.RGBA
- func EncodePNG(img *image.RGBA) ([]byte, error)
- func HasPrivateUse(g *Grid) bool
- func Hex(c Color) string
- func MissingGlyphs(g *Grid, f *Frame) ([]rune, error)
- func Raster(g *Grid, f *Frame) (*image.RGBA, error)
- func Render(format Format, g *Grid, f *Frame) ([]byte, error)
- func RenderANSI(g *Grid) []byte
- func RenderHTML(g *Grid, f *Frame) []byte
- func RenderPNG(g *Grid, f *Frame) ([]byte, error)
- func RenderSVG(g *Grid, f *Frame) []byte
- func RenderText(g *Grid) []byte
- type Cell
- type Color
- type ControlsStyle
- type Font
- type Format
- type Frame
- type FrameInputs
- type FrameMode
- type FrameSpec
- type Grid
- type Image
- type Palette
- type Screen
- type Underline
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( XTermFg = RGB(0xe5, 0xe5, 0xe5) XTermBg = RGB(0x00, 0x00, 0x00) )
XTermFg and XTermBg are the documented no-theme defaults: the xterm reference palette's own.
var Formats = []string{"png", "svg", "ansi", "html", "txt"}
Formats lists every backend, default first.
Functions ¶
func Downscale ¶
Downscale shrinks img to fit maxW by maxH, keeping its proportions. An image already inside the box comes back untouched.
This is a box filter over integer source rectangles: the preview is a picture of text being looked at from across a panel, and the difference between this and a Lanczos kernel there is invisible, while the difference in what it costs is the whole reason the preview arrives at all.
func EncodePNG ¶
EncodePNG serialises a raster. BestSpeed because the picture is a screenshot on its way to a file or a terminal, and the seconds a smaller file would save are not seconds anybody spends.
func HasPrivateUse ¶
HasPrivateUse reports whether the grid holds a glyph from a private use area.
That is what a Nerd Font icon is: a codepoint no standard font carries, put there by the font the terminal draws with. A capture full of them rendered in the embedded fallback face is a capture full of tofu boxes, and this is how the caller knows to say which setting fixes it rather than leaving the user to guess that the empty boxes are about a font at all.
func Hex ¶
Hex formats c as #rrggbb. Alpha is dropped: every place this string goes (SVG, HTML, SGR) carries opacity out of band or not at all.
func MissingGlyphs ¶
MissingGlyphs returns the runes of g that no face of f can draw, in the order they first appear and without repeats: not the font, not a fallback, not the built-in Go Mono. Box drawing, block elements, braille and powerline glyphs are drawn as shapes and are never missing.
A caller with a font database uses it to fill Frame.Fallback: ask the database for a font that covers each missing rune, add the answer, and call MissingGlyphs again to skip the runes that font also covers.
Example ¶
Find the glyphs that no face of a frame has, before the render draws them as boxes. A program with a font database adds a fallback face for each.
package main
import (
"fmt"
"tuios.dev/shot"
)
func main() {
g := shot.NewGrid(8, 1, shot.RGB(0xe5, 0xe5, 0xe5), shot.RGB(0, 0, 0))
g.Cells[0][0].Cluster = "あ"
g.Cells[0][0].Width = 2
g.Cells[0][1].Width = 0
frame := &shot.Frame{}
missing, err := shot.MissingGlyphs(g, frame)
if err != nil {
panic(err)
}
for _, r := range missing {
// Ask the font database for a face that has r, then:
// frame.Fallback = append(frame.Fallback, shot.Font{Data: data})
fmt.Printf("%U\n", r)
}
}
Output:
func Raster ¶
Raster draws the capture and stops there, handing back the pixels.
It is split out of RenderPNG because the preview and the file want the same pixels at different sizes: the file wants them encoded whole, the panel wants them shrunk to the box it will draw them in. Rendering twice cost a full second on a full-screen capture and produced two pictures that had to be kept in step; rendering once and encoding twice cannot drift.
func Render ¶
Render renders the grid in the given format. The frame applies to the pixel formats and HTML. ANSI and text have no frame. A nil frame renders the bare grid.
Example ¶
Build a grid cell by cell and render it to PNG. A nil frame renders the bare grid.
package main
import (
"os"
"tuios.dev/shot"
)
func main() {
g := shot.NewGrid(20, 1, shot.RGB(0xe5, 0xe5, 0xe5), shot.RGB(0, 0, 0))
for x, r := range []rune("hello, world") {
g.Cells[0][x].Cluster = string(r)
}
g.Cells[0][0].Bold = true
png, err := shot.Render(shot.FormatPNG, g, nil)
if err != nil {
panic(err)
}
if err := os.WriteFile("hello.png", png, 0o644); err != nil {
panic(err)
}
}
Output:
func RenderANSI ¶
RenderANSI emits the grid as truecolor SGR text, one line per row, reset at every style change and at each line end so a partial paste cannot bleed style into the host.
func RenderHTML ¶
RenderHTML renders the grid and frame to a standalone HTML document.
func RenderText ¶
RenderText emits the grid as plain text, styles dropped, lines right-trimmed.
Types ¶
type Cell ¶
type Cell struct {
// Cluster is the grapheme cluster, empty for a blank cell and for the
// continuation column of a wide cluster.
Cluster string
// Width is the cluster's cell width: 1 or 2. 0 marks the continuation
// column of a wide cluster, which no backend draws.
Width uint8
FG, BG Color
// BGDefault marks a cell whose background is the grid default, so
// backends can skip its rect and let the card ground show through.
BGDefault bool
Bold, Faint, Italic, Strike bool
Underline Underline
// Link is the OSC 8 target, kept so SVG and HTML can emit real anchors.
Link string
}
Cell is one resolved grid cell. Colors are concrete RGB: theme lookup, xterm fallback, reverse video, and the cursor are all folded in before a Cell exists, so every backend renders the same truth without knowing where it came from.
type Color ¶
Color is a resolved sRGB color. Alpha is kept because a frame background can be transparent; cell colors are always opaque by the time they land here.
func XTerm256 ¶
XTerm256 is the standard 256-color mapping: xterm's basic 16, then the 6x6x6 cube, then the grayscale ramp. An index outside 0-255 gives the zero Color.
Indexes 16-255 come from the x/ansi table, whose cube and ramp are xterm's. Its first 16 entries follow the darker VGA scheme instead, so those come from xtermBasic.
type ControlsStyle ¶
type ControlsStyle int
ControlsStyle picks the window control marks in the title bar.
const ( // ControlsMacOS draws the traffic lights, and is what "auto" resolves to. // // Accent-tinted dots at the traffic lights' size, spacing and position do // not read as a quieter variant of the arrangement everyone knows; they // read as that arrangement with the colours broken. Red, amber and green are what a window control // is, everywhere, so they are not the theme's to tint. See the rule in the // corrective spec's section 4: chrome that carries a learned meaning keeps // its learned colours. ControlsMacOS ControlsStyle = iota // ControlsGlyphs draws the user's own glyph-set window marks. ControlsGlyphs // ControlsNone leaves the bar empty of controls. ControlsNone )
type Font ¶
type Font struct {
// Data is the whole font file. The renderer reads it and does not keep it
// past the render.
Data []byte
// Index selects a face when Data is a collection (.ttc, .otc). Zero is the
// first face, and the only one of a plain font file.
Index int
}
Font is one face of a font file: TrueType, OpenType, or a collection of either. The PNG backend rasterizes with it. SVG and HTML embed it only when Frame.EmbedFont allows that.
type Format ¶
type Format string
Format is an output backend.
func ParseFormat ¶
ParseFormat resolves a format name, accepting the extension spellings.
type Frame ¶
type Frame struct {
Mode FrameMode
Padding int // px around the card, at scale 1
Radius int // card corner radius, px at scale 1
Shadow bool
// Transparent drops the wash entirely: PNG alpha, SVG nothing.
Transparent bool
// WashStart and WashEnd are the diagonal gradient stops. Equal values
// give a solid fill.
WashStart, WashEnd Color
Controls ControlsStyle
// Title is the window display name for the title bar.
Title string
// Accent tints the dots and the title, derived from the theme.
Accent Color
// CloseGlyph, MinimizeGlyph, MaximizeGlyph are the glyph-set marks for
// ControlsGlyphs.
CloseGlyph, MinimizeGlyph, MaximizeGlyph string
// FontFamily is the CSS font stack for SVG and HTML.
FontFamily string
// Font is the font PNG rasterizes with, and the one SVG and HTML inline
// as an @font-face when EmbedFont is set. No Data means reference-only
// SVG and HTML and the embedded Go Mono for PNG.
Font Font
// BoldFont is the bold cut of Font. No Data double-strikes bold cells.
BoldFont Font
// Fallback lists the faces PNG looks in, in order, for a glyph Font does
// not have. Go Mono comes after them. MissingGlyphs says which runes
// still need a face.
Fallback []Font
// EmbedFont allows SVG and HTML to inline Font as an @font-face. Only
// a font the user named themselves earns that: embedding turns a kilobyte
// of SVG into megabytes, and a font tuios found by asking the terminal was
// found for the raster, not for the export.
EmbedFont bool
// Scale multiplies the PNG raster size, 1 to 4.
Scale int
// CellAspect is one cell of the terminal being pictured, width over
// height. Zero leaves the raster on the font's own cell, which is what a
// render with no terminal to ask has to do.
CellAspect float64
}
Frame is the fully resolved dressing around a grid. Every value is concrete by the time it lands here: config parsing, theme derivation, and the auto keywords are the caller's job, so the renderer has no defaults of its own to disagree with the settings panel about.
func BuildFrame ¶
func BuildFrame(spec FrameSpec, in FrameInputs) *Frame
BuildFrame resolves a spec against its inputs into a concrete Frame.
Example ¶
Dress a grid as a window, with the gradient and the controls in the palette's colours.
package main
import (
"fmt"
"tuios.dev/shot"
)
func main() {
p := shot.XTermPalette()
g := shot.NewGrid(40, 10, p.FG, p.BG)
frame := shot.BuildFrame(shot.FrameSpec{
Frame: "window",
Title: "build",
Padding: 24,
Radius: 10,
Shadow: true,
Scale: 2,
}, shot.FrameInputs{Palette: p})
svg, err := shot.Render(shot.FormatSVG, g, frame)
if err != nil {
panic(err)
}
fmt.Println(len(svg) > 0)
}
Output:
type FrameInputs ¶
type FrameInputs struct {
Palette *Palette
// Accents seed the auto wash, best first. Empty falls back to the
// palette's blue and magenta.
Accents []Color
// Close, Minimize, Maximize are the glyph-set marks for controls glyphs.
Close, Minimize, Maximize string
// CellAspect is one cell of the terminal being pictured, width over
// height. Zero when nothing asked the terminal, which is every render off a
// daemon or the CLI, and then the font's own cell stands.
CellAspect float64
}
FrameInputs is what the environment contributes: the resolved palette, accent candidates for the wash and controls, and the glyph-set window marks.
type FrameSpec ¶
type FrameSpec struct {
Frame string // window, plain, none
Background string // auto, none, hex, hex..hex
Padding int
Radius int
Shadow bool
Controls string // auto, macos, glyphs, none
Title string
FontFamily string
// Font is the font to rasterize with. No Data means Go Mono.
Font Font
// BoldFont is the bold cut of Font, when the resolver found one. Without
// it a bold cell is double-struck, which thickens a glyph without making
// it the bold letterform the terminal drew.
BoldFont Font
// Fallback is the list of faces for glyphs Font does not have.
Fallback []Font
// EmbedFont says the font may be written into SVG and HTML output as an
// @font-face. It is separate from Font because the two questions are
// different: PNG has to rasterize with a real file, and a file found for
// it by asking the terminal is not a file the user asked to have three
// megabytes of copied into every SVG they export.
EmbedFont bool
Scale int
}
FrameSpec is the raw configuration of a frame, before theme derivation. Strings use the config spellings so config and CLI hand them over as-is.
type Grid ¶
type Grid struct {
Cols, Rows int
// Cells is Rows rows of Cols cells each.
Cells [][]Cell
// FG and BG are the resolved default colors, used for the card ground
// and for any styling derived from the content.
FG, BG Color
// Images are the pictures on the screen, drawn in order: a later image
// in the same layer goes over an earlier one. See Image.
Images []Image
}
Grid is a captured screen region, fully resolved.
func FromScreen ¶
FromScreen resolves every cell of s into a new grid. p supplies the colours for the default, basic and indexed colours. A nil p is XTermPalette.
func (*Grid) ReverseCursor ¶
ReverseCursor folds the cursor into the grid by swapping the cell's colors, the way a block cursor reads.
type Image ¶ added in v0.2.0
type Image struct {
// Pixels is the decoded picture.
Pixels image.Image
// Source is the rectangle of Pixels that fills the box. An empty Source
// is all of Pixels. A Source that reaches past the edges of Pixels draws
// nothing there, which is how a picture smaller than its box keeps its
// own size: kitty draws an image at its own pixel size from the top-left
// corner of its first cell.
Source image.Rectangle
// Col and Row are the cell at the top-left corner of the box. They can
// be outside the grid.
Col, Row int
// Cols and Rows are the size of the box in cells.
Cols, Rows int
// Below draws the picture under the text and over the cell backgrounds,
// which is what kitty does with a negative z-index. Otherwise the picture
// goes over the text.
Below bool
}
Image is a picture drawn on the grid, such as one a program shows with the kitty graphics protocol or with sixel.
The picture fills a box of cells. Source is the part of Pixels that goes into the box, scaled to fit it. Part of the box outside the grid is cut off, so a picture that scrolled half off the screen draws only the half that is on it.
PNG, SVG and HTML draw images. ANSI and text output have no way to show a picture, so they leave images out.
type Palette ¶
type Palette struct {
// FG and BG are the default foreground and background.
FG, BG Color
// ANSI is the basic 16.
ANSI [16]Color
// Indexed overrides 256-color lookups (an emulator's OSC 4 state);
// nil falls through to ANSI and the xterm cube.
Indexed func(i int) (Color, bool)
}
Palette resolves preserved color kinds to concrete RGB.
func XTermPalette ¶
func XTermPalette() *Palette
XTermPalette is the guess a render falls back to when no theme is set.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mosaic draws a picture with text: each terminal cell becomes one block glyph with a foreground and a background colour, the way chafa's symbol mode does.
|
Package mosaic draws a picture with text: each terminal cell becomes one block glyph with a foreground and a background colour, the way chafa's symbol mode does. |