# iv `iv` is a command-line image viewer using terminal graphics (Sixel, iTerm, Kitty).

Installing | Building | Using | Sizing | Formats | Releases

[![Releases][release-status]][Releases] [![Discord Discussion][discord-status]][discord] [releases]: https://github.com/kenshaw/iv/releases "Releases" [release-status]: https://img.shields.io/github/v/release/kenshaw/iv?display_name=tag&sort=semver "Latest Release" [discord]: https://discord.gg/WDWAgXwJqN "Discord Discussion" [discord-status]: https://img.shields.io/discord/829150509658013727.svg?label=Discord&logo=Discord&colorB=7289da&style=flat-square "Discord Discussion" ## Overview `iv` is a command-line image viewer using terminal graphics. Uses [Sixel][sixel], [iTerm Inline Images][iterm], or [Kitty][kitty] graphics protocols where available. See [Are We Sixel Yet?][arewesixelyet] for a list of terminals known to work with this package. [sixel]: https://saitoha.github.io/libsixel/ [iterm]: https://iterm2.com/documentation-images.html [kitty]: https://sw.kovidgoyal.net/kitty/graphics-protocol/ [arewesixelyet]: https://www.arewesixelyet.com ## Installing `iv` can be installed [via Release][], [via Homebrew][], [via AUR][], [via Scoop][] or [via Go][]: [via Release]: #installing-via-release [via Homebrew]: #installing-via-homebrew-macos-and-linux [via AUR]: #installing-via-aur-arch-linux [via Scoop]: #installing-via-scoop-windows [via Go]: #installing-via-go ### Installing via Release 1. [Download a release for your platform][releases] 2. Extract the `iv` or `iv.exe` file from the `.tar.bz2` or `.zip` file 3. Move the extracted executable to somewhere on your `$PATH` (Linux/macOS) or `%PATH%` (Windows) ### Installing via Homebrew (macOS and Linux) Install `iv` from the [`kenshaw/iv` tap][iv-tap] in the usual way with the [`brew` command][homebrew]: ```sh # install $ brew install kenshaw/iv/iv ``` ### Installing via AUR (Arch Linux) Install `iv` from the [Arch Linux AUR][aur] in the usual way with the [`yay` command][yay]: ```sh # install $ yay -S iv-cli ``` Alternately, build and [install using `makepkg`][arch-makepkg]: ```sh # clone package repo and make/install package $ git clone https://aur.archlinux.org/iv-cli.git && cd iv-cli $ makepkg -si ==> Making package: iv-cli 0.4.4-1 (Sat 11 Nov 2023 02:28:28 PM WIB) ==> Checking runtime dependencies... ==> Checking buildtime dependencies... ==> Retrieving sources... ... ``` ### Installing via Scoop (Windows) Install `iv` using [Scoop](https://scoop.sh): ```powershell # Optional: Needed to run a remote script the first time > Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # install scoop if not already installed > irm get.scoop.sh | iex # install iv with scoop > scoop install iv ``` ### Installing via Go Install `iv` in the usual Go fashion: ```sh # install latest iv version $ go install github.com/kenshaw/iv@latest ``` Note that this builds from source, and so needs what [Building](#building) does. ## Building `iv` is cgo code. It links libvips for the image formats and pdfs the Go decoders do not cover, and fontconfig and freetype for the document renderer, so a Go toolchain on its own is not enough. Go 1.27 or later, plus the following per platform. ### Linux ```sh $ sudo apt-get install -y build-essential pkg-config libvips-dev \ libfontconfig-dev libfreetype-dev ``` libvips must be 8.18 or later -- the bindings `iv` uses are generated against it, and Ubuntu 24.04 and earlier ship 8.15. Debian and Ubuntu build libheif without any codec, so HEIC and AVIF need the plugins on top of that: ```sh $ sudo apt-get install -y libheif-plugin-libde265 libheif-plugin-x265 \ libheif-plugin-aomdec libheif-plugin-aomenc ``` ### macOS ```sh $ brew install vips pkgconf ``` Some libvips builds put `-Xpreprocessor` in their pkg-config cflags, which cgo refuses to pass through. If the build stops on that, allow it: ```sh $ export CGO_CFLAGS_ALLOW='-Xpreprocessor' ``` ### Windows Build inside [MSYS2][], in the UCRT64 environment rather than MINGW64: the bundled mupdf calls `__intrinsic_setjmpex`, which only the UCRT runtime has. ```sh $ pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-pkgconf \ mingw-w64-ucrt-x86_64-libvips ``` ### Building and testing ```sh # build, vet, and test $ go build ./... $ go vet ./... $ go test ./... # render everything in testdata/ through the pipeline, as a smoke test -- # uses ./iv when it is there, and the iv on $PATH otherwise $ go build -o ./iv . && ./test.sh # a release build: versioned, stripped, and packed $ ./build.sh -v v0.1.0 ``` `build.sh` writes to `build////`. `-r` takes the version from the latest tag instead of `-v`, `-i` installs rather than packing, `-b` builds without packing, `-a` cross compiles to another arch, and `-s` makes a fully static linux binary. ### Optional tools Four decoders shell out. Without the command, the file fails to render with ` not in path` rather than being quietly skipped: | Command | Used for | | --------- | -------------------------------------------------- | | `soffice` | Word, Excel, PowerPoint, and OpenDocument documents | | `mmdc` | Mermaid diagrams | | `ffmpeg` | video snapshots, and the waveform on an audio card | | `binwalk` | images embedded in otherwise unrecognized files | A mermaid diagram is the exception: without `mmdc` it falls through to `blitz`, which renders the source as a document. An audio card is another -- it is drawn without a waveform rather than not at all. ### 32-bit targets On `386` and `arm`, `epub`, `xps`, `mobi`, `fb2` and `psd` do not decode: go-fitz vendors its prebuilt mupdf for 64-bit targets only, and nothing registers for those formats there. ## Using ```sh $ iv /path/to/image_or_directory # a data: URL or WIFI: code $ iv 'WIFI:S:mynetwork;T:WPA;P:secret;;' # a remote image $ iv https://example.com/image.png # a specific page of a pdf, epub, or comic archive $ iv -p 4 /path/to/document.pdf # convert instead of displaying -- the encoder follows the output extension $ iv --out out.webp /path/to/image.heic # the registered decoders, encoders, and file extensions $ iv --list # all command line options $ iv --help ``` ## Sizing Drawing to a terminal, `iv` fits an image to it. Every measure is in pixels. The display size (`-W`/`-H`) is a ceiling and the minimum size (`-w`/`-h`, 64 by default) a floor, and **between the two an image is shown at its own size** -- so an ordinary image is never resampled, and only one too big to fit or too small to make out is touched at all. An image above the ceiling is shrunk to it; one below the floor is grown up to it, and no further. Leave `-W`/`-H` at 0 and the ceiling comes from the terminal itself, which is the usual case. `iv` asks the kernel for the window size and uses the pixel geometry the terminal reports there; a terminal that reports only a character grid has its pixel size estimated from it instead. Two rows are kept back for the file name and the prompt that follow the image. A file written with `--out` gets no ceiling from the terminal -- there is no terminal to fit, and a conversion that quietly downscaled to whatever window happened to be open would be a surprising thing for a conversion to do. An explicit `-W`/`-H` still applies. `-m`/`--mode` changes how the two are used: | Mode | What it does | | ---------- | ---------------------------------------------------------------- | | `best-fit` | the default, described above | | `none` | no scaling at all; every image is shown at its own size | | `width` | `best-fit` against the width alone, however tall the result runs | | `height` | `best-fit` against the height alone | | `shrink` | `best-fit` without the floor: a small image is left small | | `stretch` | fill `-W` x `-H` exactly, disregarding the aspect ratio | Where the floor and the ceiling disagree -- a minimum larger than the room there is to show it in -- the ceiling wins. An image that clears the floor in one dimension is not grown to clear it in the other, so a 1000x2 banner stays 1000x2 rather than becoming 32000 wide. An image small enough to be icon art is magnified by a whole number of pixels and not resampled, so a 24x24 favicon is its own pixels drawn three times larger rather than blurred up to 72. A vector -- svg, lottie, pdf -- has no pixels of its own to preserve, so it is rasterized at the size it is displayed at instead of being resampled to it, and comes out sharp at any size. ## Formats `iv --list` prints what the binary in front of you actually has. Everything below is in a default build; the decoders marked *needs* are only as good as the command they shell out to, and the libvips formats depend on how libvips itself was compiled. Anything holding more than one image is shown one at a time, and `-p N` picks which: a pdf or epub page, an icon size, a comic archive page, a lottie frame. An animated `gif` or `webp` is the exception, and always shows its first frame. ### Images | Format | Extensions | Decoder | | ----------------------------------------- | ------------------------------------- | ------------ | | Portable Network Graphics, including APNG | `png` | `png` | | JPEG | `jpg` `jpeg` `jpe` `jif` `jfif` `jfi` | `jpeg` | | GIF, first frame of an animation | `gif` | `gif` | | WebP, lossy and lossless | `webp` | `nativewebp` | | TIFF | `tif` `tiff` | `tiff` | | Windows Bitmap | `bmp` `dib` | `bmp` | | Netpbm, raw and plain | `pbm` `pgm` `ppm` `pnm` `pam` | `netpbm` | | Windows icons and cursors | `ico` `cur` | `ico` | | Apple Icon Image | `icns` | `icns` | | HEIC/HEIF and AVIF | `heic` `heif` `avif` | `vips` | | JPEG 2000 and JPEG XL | `jp2` `jpf` `j2k` `jxl` `jxs` | `vips` | | OpenEXR, Radiance HDR, PFM | `exr` `hdr` `pfm` `rad` | `vips` | | FITS, MATLAB, NIfTI, native vips | `fits` `mat` `nii` `v` | `vips` | ### Vector graphics and diagrams | Format | Extensions | Decoder | | --------------------------------- | --------------------- | ---------- | | SVG, plain and gzipped | `svg` `svgz` | `resvg` | | Lottie animations, and dotLottie | `json` `lot` `lottie` | `lottie` | | Graphviz graph description | `gv` `dot` | `graphviz` | | Mermaid diagrams *(needs `mmdc`)* | `mmd` `mermaid` | `mermaid` | A `.json` is only taken as a lottie when the document itself says so, so an ordinary json file is left alone. ### Documents | Format | Extensions | Decoder | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------- | | PDF, including password protected | `pdf` | `vips-pdf` | | EPUB, XPS, MOBI, FictionBook, Photoshop | `epub` `xps` `oxps` `mobi` `fb2` `psd` | `fitz` | | Markdown, HTML, and plain text | `md` `markdown` `mkd` `mdown` `html` `htm` `xhtml` `txt` | `blitz` | | Comic book archives | `cbz` `cbr` `cbt` `cb7` | `archives` | | Word, Excel, PowerPoint *(needs `soffice`)* | `doc` `docx` `dot` `dotx` `xls` `xlsx` `xlt` `xltx` `ppt` `pptx` `pot` `potx` `pps` `ppsx` | `libreoffice` | | OpenDocument *(needs `soffice`)* | `odt` `ods` `odp` `odg` `odf` `odc` `ott` `ots` `otp` `otg` | `libreoffice` | | RTF, Publisher, Visio, WordPerfect, csv, tsv *(needs `soffice`)* | `rtf` `pub` `vsd` `wpd` `csv` `tsv` | `libreoffice` | ### Video and audio | Format | Extensions | Decoder | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -------- | | Video, as a single frame *(needs `ffmpeg`)* | `mp4` `m4v` `mkv` `mov` `avi` `webm` `mpeg` `mpeg2` `mpg` `mpg2` `flv` `asf` `wmv` `3gp` `3g2` `mj2` `ogv` | `ffmpeg` | | Audio, as a card of its cover art and tags | `mp3` `m4a` `m4b` `m4p` `flac` `ogg` `oga` `dsf` `aac` | `tag` | The audio card draws a waveform when `ffmpeg` is there, and is drawn without one when it is not. `-t` picks the moment a video is snapshotted at. ### Fonts and executables | Format | Extensions | Decoder | | ---------------------- | --------------------------------------------------- | --------- | | Font specimen previews | `ttf` `ttc` `otf` `woff` `woff2` `sfnt` `eot` `pfb` | `fontimg` | | Icons in a Windows PE | `exe` `dll` `mui` | `winres` | ### Arguments that are not files | Argument | Example | Decoder | | --------------------------- | ----------------------------------- | ----------- | | `data:` URLs | `data:image/png;base64,iVBOR...` | `data` | | `WIFI:` codes, as a QR code | `WIFI:S:mynetwork;T:WPA;P:secret;;` | `qr` | | `http://` and `https://` | a page, or the image it names | `blitz-url` | ### Anything else A file nothing above recognizes is handed to `binwalk`, which renders an image found inside it -- the Affinity formats (`afdesign`, `afphoto`, `afpub`) among them. *Needs `binwalk`.* ### Output Without `--out`, `iv` draws to the terminal with Kitty, iTerm, or Sixel graphics. With it, the encoder follows the output extension, and `--encoder` overrides that: | Extension | Encoder | | --------- | ------------ | | `png` | `png` | | `jpg` | `jpeg` | | `webp` | `nativewebp` | | `avif` | `vips-avif` | | `gif` | `vips-gif` | | `heic` | `vips-heif` | | `jp2` | `vips-jp2k` | | `jxl` | `vips-jxl` | | `tiff` | `vips-tiff` | libvips writes several of these itself, which `--encoder` reaches: `vips-webp` instead of `nativewebp`, or `vips-tiff` for a file the Go encoder has no compression for. [homebrew]: https://brew.sh/ [msys2]: https://www.msys2.org/ [iv-tap]: https://github.com/kenshaw/homebrew-iv [aur]: https://aur.archlinux.org/packages/iv-cli [arch-makepkg]: https://wiki.archlinux.org/title/makepkg [yay]: https://github.com/Jguer/yay