Skip to content

What is the best way to preview images, PDFs, etc. currently? #1140

@wideweide

Description

@wideweide

My system environment:

Operating System: Pop!_OS 24.04 LTS
Kernel: Linux 6.18.7-76061807-generic
Architecture: x86-64
Session type: wayland
Desktop: COSMIC
Wayland display: wayland-1

Initially, I tried installing from source using git clone https://github.com/jstkdng/ueberzugpp.git, but the configuration failed. I even tried Yazi, which is supposed to work out of the box, but previews still didn't work. I suspect it might be poor support for Wayland or the COSMIC desktop.

Then I tried chafa, but gave up on the mosaic-style images.

Finally, I managed to get image previews working by using the foot terminal, which supports the Sixel protocol. However, there are two issues:

  1. I haven't been able to figure out how to maximize the image within the preview pane while maintaining the correct aspect ratio.
  2. Some images fail to render when their width exceeds a certain threshold.
fileviewer *.jpg,*.jpeg,*.png,*.gif,*.bmp,*.tiff,*.webp
    \ ~/.config/vifm/scripts/img_preview.sh "%c" %pw %ph %pd
img_preview.sh
#!/bin/bash
# 图片预览脚本 - 优先 img2sixel (保持比例),回退到 chafa
# 支持 Sixel 缓存以减少闪烁

img="$1"
width_chars="$2"
height_chars="$3"

# 缓存目录(请确保可写)
CACHE_DIR="$HOME/.cache/vifm/sixel_cache"
mkdir -p "$CACHE_DIR"

# 日志文件
LOG="$HOME/.config/vifm/scripts/img_preview.log"

# 强制使用符号模式(创建该文件可禁用所有 Sixel 尝试)
FORCE_SYMBOLS_FILE="$HOME/.config/vifm/force_preview_symbols"

mkdir -p "$(dirname "$LOG")"

echo "=== $(date) ===" >> "$LOG"
echo "File: $img" >> "$LOG"
echo "Chars: ${width_chars}x${height_chars}" >> "$LOG"

# 估算像素尺寸(用于 Sixel 工具)
font_width=32
font_height=64
pixel_width=$((width_chars * font_width))
pixel_height=$((height_chars * font_height))

# 限制最大像素尺寸(img2sixel 和 chafa 使用不同的限制)
MAX_WIDTH_IMG2SIXEL=940
MAX_HEIGHT_IMG2SIXEL=1600
MAX_WIDTH_CHAFA=150        # chafa 使用较小尺寸提升速度
MAX_HEIGHT_CHAFA=300

# 获取图片原始尺寸(需要 ImageMagick)
if command -v identify >/dev/null 2>&1; then
    orig_size=$(identify -format "%wx%h" "$img" 2>/dev/null)
    echo "Original size: $orig_size" >> "$LOG"
    orig_width=$(echo "$orig_size" | cut -d'x' -f1)
    orig_height=$(echo "$orig_size" | cut -d'x' -f2)
fi

# 辅助函数:计算保持比例的缩放尺寸
calculate_fit_size() {
    local w="$1" h="$2" max_w="$3" max_h="$4"
    local ratio_w=$(echo "scale=4; $max_w / $w" | bc)
    local ratio_h=$(echo "scale=4; $max_h / $h" | bc)
    local ratio=$(echo "scale=4; if ($ratio_w < $ratio_h) $ratio_w else $ratio_h" | bc)
    local new_w=$(echo "$w * $ratio" | bc | cut -d. -f1)
    local new_h=$(echo "$h * $ratio" | bc | cut -d. -f1)
    # 确保至少为1
    [ -z "$new_w" ] && new_w=1
    [ -z "$new_h" ] && new_h=1
    echo "${new_w}x${new_h}"
}

# 生成缓存键:基于图片路径、最终输出尺寸、工具名
get_cache_key() {
    local tool="$1"
    local out_w="$2"
    local out_h="$3"
    echo "$tool|$img|${out_w}x${out_h}" | md5sum | cut -d' ' -f1
}

# 尝试从缓存输出
try_cache() {
    local cache_file="$1"
    if [ -f "$cache_file" ] && [ -s "$cache_file" ]; then
        cat "$cache_file"
        echo "Used cache: $cache_file" >> "$LOG"
        return 0
    fi
    return 1
}

# ----- 优先尝试 img2sixel(保持比例)-----
if command -v img2sixel >/dev/null 2>&1; then
    echo "Trying img2sixel..." >> "$LOG"
    # 计算适应预览区域的缩放尺寸
    if [ -n "$orig_width" ] && [ -n "$orig_height" ]; then
        fit_size=$(calculate_fit_size "$orig_width" "$orig_height" "$pixel_width" "$pixel_height")
        fit_width=$(echo "$fit_size" | cut -d'x' -f1)
        fit_height=$(echo "$fit_size" | cut -d'x' -f2)
        echo "Fit size: ${fit_width}x${fit_height}" >> "$LOG"
    else
        # 无法获取原图尺寸,直接使用预览区域(会拉伸)
        fit_width="$pixel_width"
        fit_height="$pixel_height"
    fi

    # 限制最大尺寸
    [ "$fit_width" -gt "$MAX_WIDTH_IMG2SIXEL" ] && fit_width="$MAX_WIDTH_IMG2SIXEL"
    [ "$fit_height" -gt "$MAX_HEIGHT_IMG2SIXEL" ] && fit_height="$MAX_HEIGHT_IMG2SIXEL"

    # 生成缓存键并检查缓存
    cache_key=$(get_cache_key "img2sixel" "$fit_width" "$fit_height")
    cache_file="$CACHE_DIR/$cache_key.sixel"
    if try_cache "$cache_file"; then
        exit 0
    fi

    # 生成新的 Sixel
    tmpfile=$(mktemp)
    img2sixel --width "$fit_width" --height "$fit_height" "$img" > "$tmpfile" 2>&1
    exit_code=$?
    if [ $exit_code -eq 0 ] && [ -s "$tmpfile" ]; then
        if grep -q $'\x1bP' "$tmpfile" && grep -q $'\x1b\\\\' "$tmpfile"; then
            # 输出并缓存
            cat "$tmpfile"
            cp "$tmpfile" "$cache_file"
            echo "img2sixel succeeded (sixel, fit) and cached" >> "$LOG"
            rm -f "$tmpfile"
            exit 0
        else
            echo "img2sixel output missing sixel sequences" >> "$LOG"
        fi
    else
        echo "img2sixel failed (exit $exit_code, size $(stat -c%s "$tmpfile" 2>/dev/null))" >> "$LOG"
    fi
    rm -f "$tmpfile"
fi

# ----- 最后尝试 chafa sixel(保持比例,添加 --no-clear)-----
if command -v chafa >/dev/null 2>&1; then
    echo "Trying chafa sixel..." >> "$LOG"
    # 计算适应预览区域的缩放尺寸(使用字符尺寸估算)
    # 注意:chafa 的 --size 参数是像素,这里我们限制最大尺寸
    chafa_pixel_width=$((width_chars * 8))   # 假设字体宽8像素,可根据实际调整
    chafa_pixel_height=$((height_chars * 16)) # 假设字体高16像素

    # 限制最大尺寸(使用更保守的数值以提升性能)
    [ "$chafa_pixel_width" -gt "$MAX_WIDTH_CHAFA" ] && chafa_pixel_width="$MAX_WIDTH_CHAFA"
    [ "$chafa_pixel_height" -gt "$MAX_HEIGHT_CHAFA" ] && chafa_pixel_height="$MAX_HEIGHT_CHAFA"

    echo "Chafa pixel size: ${chafa_pixel_width}x${chafa_pixel_height}" >> "$LOG"

    # 生成缓存键并检查缓存
    cache_key=$(get_cache_key "chafa_sixel" "$chafa_pixel_width" "$chafa_pixel_height")
    cache_file="$CACHE_DIR/$cache_key.sixel"
    if try_cache "$cache_file"; then
        exit 0
    fi

    # 生成新的 Sixel(添加 --no-clear 禁止清屏)
    tmpfile=$(mktemp)
    chafa --format=sixel \
          --size="${chafa_pixel_width}x${chafa_pixel_height}" \
          "$img" > "$tmpfile" 2>/dev/null
    exit_code=$?
    if [ $exit_code -eq 0 ] && [ -s "$tmpfile" ]; then
        if grep -q $'\x1bP' "$tmpfile" && grep -q $'\x1b\\\\' "$tmpfile"; then
            cat "$tmpfile"
            cp "$tmpfile" "$cache_file"
            echo "chafa sixel succeeded and cached" >> "$LOG"
            rm -f "$tmpfile"
            exit 0
        else
            echo "chafa sixel missing start/end sequences" >> "$LOG"
        fi
    else
        echo "chafa sixel failed (exit $exit_code, size $(stat -c%s "$tmpfile" 2>/dev/null))" >> "$LOG"
    fi
    rm -f "$tmpfile"
fi

# ----- 最终回退:chafa 符号模式 -----
echo "Falling back to symbols mode" >> "$LOG"

echo "安装依赖:"
echo "  sudo apt -y install foot,libsixel-bin"
exit 0

After all this tinkering, I discovered that there are three plugins that can achieve image preview functionality. For those who have used them, which one would you recommend?

  • vifmimg (image preview) - Repository (uses Überzug to display images)
  • sixel image preview - Repository (for Sixel-capable terminals)
  • thu.sh (image preview) - Repository (Sixel or Kitty)

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Usage.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions