插入带有终端颜色转义序列的文本

插入带有终端颜色转义序列的文本

我有一个在 unix shell 中生成的日志文件,其中包含一堆 shell/终端颜色转义序列。例如,echo -e '\e[1;34mBLUE TEXT\e[0m'以蓝色输出“蓝色文本”等等。如何将此日志文件插入我的 LaTeX 文档,并确保生成的 PDF 中的文本与终端中的文本完全相同(保留颜色)?

需要澄清的是:echo上面是如何生成彩色输出的示例。我需要插入的是结果输出,而不是命令echo本身,因此\e实际上是带有十六进制代码的文字转义字节1B

示例输出:

https://www.filedropper.com/typescript

它的实际外观的图片(或者你可以cat在 unix 终端中看到它):

在此处输入图片描述

答案1

这是您所要求的近似值。

TeX 不提供可靠地输入二进制文件的方法,但对于 pdfTeX,有一个\pdffiledump读取二进制文件的原语,将其转义为十六进制数字序列。我们首先需要预处理此字符串以获取字符序列,每个字符的 catcode 为 12 ( \term@preprocess)。

然后我们解析这个输入字符串 ( \term@process),在标记列表中构建当前输出行\term@line(出于技术原因,以相反的顺序构建)。根据当前输入字符,输出标记列表中的元素将被附加、删除或打印,并开始新的标记列表。

每当出现转义字符 ( \x1B) 时,下一个字符决定转义序列的类型。如果是[,后面跟着一系列参数编号,以及指定终端输出类型的最后一个字符。然后,我们根据此类型和给定的参数编号来修改当前显示属性的全局变量 ( \term@process@termout)。每当向当前输出行添加新字符时,都会考虑后者。

完整代码:

\documentclass{article}

\usepackage{xcolor}
\usepackage{pdftexcmds}
\usepackage{mdframed}
\usepackage[utf8]{inputenc}
\usepackage{textcomp}

\makeatletter
\endlinechar=-1


% Manipulation of current output line

\newif\ifterm@cr@

\def\term@line{}

\def\term@line@push#1{
    \ifterm@cr@
        \def\term@line{}
    \fi
    \xdef\term@line{
        {{\noexpand\strut
          \noexpand\color{\term@fgcolor\ifterm@intense@!75!white\fi}
          \unexpanded{#1}}}
        \unexpanded\expandafter{\term@line}
    }
    \term@cr@false
}

\def\term@line@pop{
    \xdef\term@line{\expandafter\unexpanded\expandafter\expandafter\expandafter
        {\expandafter\@gobble\term@line}}
    \term@cr@false
}

\def\term@line@print{
    \leavevmode
    \expandafter\term@line@reverse\expandafter\@sep\term@line{}\@end
}
\def\term@line@reverse#1\@sep#2#3\@end{
    \if@empty{#3}{
        #2#1
    }{
        \term@line@reverse#2#1\@sep#3\@end
    }
}


% Display attributes

\def\term@fgcolor{}
\def\term@default@fgcolor{lightgray}
\newif\ifterm@intense@


% Input parsing

\newcount\term@param@a
\newcount\term@param@b

\begingroup
\count0=0\relax
\loop
    \lccode`\*=\count0\relax
    \lowercase{
        \expandafter\gdef\csname term@preprocess@char@\number\count0\endcsname{*}
    }
    \advance\count0 by 1\relax
\ifnum\count0<256\relax
\repeat
\endgroup

\def\term@preprocess#1#2{
    \if@empty{#1}{}{
        \csname term@preprocess@char@\number"#1#2\endcsname
        \term@preprocess
    }
}

\def\term@process#1{
    % End of input
    \if@char@eq#1\relax{
        \@@par
        \condition@true{}
    }{}
    % Escape sequence
    \if@char@eq#1\term@escape@char{
        \condition@true\term@process@esc
    }{}
    % Newline
    \if@char@eq#1\term@lf@char{
        \term@line@print
        \@@par
        \condition@true\term@process
    }{}
    % Carriage return
    \if@char@eq#1\term@cr@char{
        \term@cr@true
        \condition@true\term@process
    }{}
    \if@char@eq#1\term@backspace@char{
        \term@line@pop
        \condition@true\term@process
    }{}
    \if@char@eq#1\term@space@char{
        \term@line@push{\ }
        \condition@true\term@process
    }{}
    \if@num@range{`#1}{194}{223}{
        \condition@true{\term@process@unicode@ii#1}
    }{}
    \if@num@range{`#1}{224}{239}{
        \condition@true{\term@process@unicode@iii#1}
    }{}
    \if@num@range{`#1}{240}{244}{
        \condition@true{\term@process@unicode@iv#1}
    }{}
    \condition@false{
        \term@line@push{#1}
        \term@process
    }
}

\def\term@process@esc#1{
    \if@char@eq#1[{
        \term@process@csi
    }{
        \GenericWarning{}{Warning: Ignored unknown escape sequence of type `#1'}
        \term@process
    }
}

\def\term@process@csi#1{
    \term@param@b=-1\relax
    % Is private sequence?
    \if@char@eq#1?{
        \afterassignment\term@process@csi@
        \term@param@a=0
    }{
        \afterassignment\term@process@csi@
        \term@param@a=0#1
    }
}

\def\term@process@csi@#1{
    % Check for second parameter
    \if@char@eq#1;{
        \afterassignment\term@process@termout
        \term@param@b=0
    }{
        \term@process@termout #1
    }
}

\def\term@process@termout#1{
    % SGR parameter
    \if@char@eq#1m{
        \term@process@sgr\term@param@a
        \ifnum\term@param@b<0\else
            \term@process@sgr\term@param@b
        \fi
        \condition@true{}
    }{}
    \condition@false{
%        \GenericWarning{}{Warning: Ignored unknown terminal output sequence of type `#1'}
    }
    \term@process
}

\def\term@process@sgr#1{
    % Reset
    \if@num@eq{#1}{0}{
        \let\term@fgcolor=\term@default@fgcolor
        \term@intense@false
        \condition@true{}
    }{}

    % Bold/intense
    \if@num@eq{#1}{1}{
        \term@intense@true
        \condition@true{}
    }{}

    % Standard colors
    \if@num@eq{#1}{30}{
        \def\term@fgcolor{black}
        \condition@true{}
    }{}
    \if@num@eq{#1}{31}{
        \def\term@fgcolor{red}
        \condition@true{}
    }{}
    \if@num@eq{#1}{32}{
        \def\term@fgcolor{green}
        \condition@true{}
    }{}
    \if@num@eq{#1}{33}{
        \def\term@fgcolor{yellow}
        \condition@true{}
    }{}
    \if@num@eq{#1}{34}{
        \def\term@fgcolor{blue}
        \condition@true{}
    }{}
    \if@num@eq{#1}{35}{
        \def\term@fgcolor{magenta}
        \condition@true{}
    }{}
    \if@num@eq{#1}{36}{
        \def\term@fgcolor{cyan}
        \condition@true{}
    }{}
    \if@num@eq{#1}{37}{
        \def\term@fgcolor{white}
        \condition@true{}
    }{}
    \condition@false{}
}

% n-byte Unicode sequences

\def\term@process@unicode@ii#1#2{
    \scantokens{\csname term@line@push\endcsname{#1#2}}
    \term@process
}

\def\term@process@unicode@iii#1#2#3{
    \scantokens{\csname term@line@push\endcsname{#1#2#3}}
    \term@process
}

\def\term@process@unicode@iv#1#2#3#4{
    \scantokens{\csname term@line@push\endcsname{#1#2#3#4}}
    \term@process
}


% Helper macros

\def\if@empty#1{
    \if\relax\detokenize{#1}\relax
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}

\def\if@char@eq#1#2{
    \if#1#2%
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}

\def\if@num@eq#1#2{
    \ifnum#1=#2 %
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
}

\def\if@num@range#1#2#3{
    \ifnum#1<#2 %
        \expandafter\@firstoftwo
    \else
        \expandafter\@secondoftwo
    \fi
    {
        \@secondoftwo
    }{
        \ifnum#1>#3 %
            \expandafter\@secondoftwo
        \else
            \expandafter\@firstoftwo
        \fi
    }
}

\def\condition@true#1#2\condition@false#3{#1}
\def\condition@false#1{#1}

\def\@temp#1#2{
    \begingroup
    \lccode`\*=`#2
    \lowercase{\global\let#1=*}
    \endgroup
}
\@temp\term@backspace@char \^^H
\@temp\term@lf@char        \^^J
\@temp\term@cr@char        \^^M
\@temp\term@escape@char    \^^[
\@temp\term@space@char     \ %


% User macros

% Print terminal session stored in #1
\newcommand\terminalinput[1]{
    \begin{mdframed}[
        backgroundcolor=black,
        innerleftmargin=0pt,
        innerrightmargin=0pt,
        innertopmargin=0pt,
        innerbottommargin=0pt
    ]
    \begingroup
    \parindent=0pt
    \frenchspacing
    \ttfamily
    \fboxsep=0pt
    \term@process@sgr{0}

    \xdef\@temp{\pdf@filedump{0}{\pdf@filesize{#1}}{#1}}
    \expandafter\xdef\expandafter\term@input\expandafter{
        \expandafter\term@preprocess\@temp{}{}
    }
    \expandafter\term@process\term@input\relax
    \endgroup
    \end{mdframed}
}

\endlinechar=`\^^M
\makeatother

\DeclareUnicodeCharacter{279C}{\textrightarrow}

\begin{document}
\terminalinput{typescript.bin}
\end{document}

输出

在此处输入图片描述

目前的实施还存在几个问题:

  • 该代码仅适用于pdflatexlualatex编译器,因为它分别依赖于\pdffiledump原语或其在 Lua 代码中的重新实现。
  • 目前不支持 Unicode 字符,因为文件被解码为单字节流。可打印 ASCII 范围之外的字符目前将被忽略。
    编辑:现在支持 Unicode 字符。每当遇到 UTF-8 2/3/4 字节序列时,字节都会通过 重新映射到其标准 catcode,\scantokens以便inputenc可以正常完成其工作。可以通过 添加新的字符映射\DeclareUnicodeCharacter
  • “终端窗口”目前只是一个横跨整个文本宽度的黑框。
  • 当前实现仅涵盖所有可用转义序列/终端输出序列的一小部分。最值得注意的是,背景颜色根本没有处理,只有粗体/浓色的处理(通过附加!75!white到当前颜色),只有八种标准颜色(代码 30-37)被实现等等。
  • 光标定位也处理不正确,例如回车符(\0x0D)清除整行而不是仅将光标移动到行首。
  • 在终端输出序列中,仅支持一个或两个参数数量,但应该支持任意数量或参数。
  • 代码尚未经过很好的测试,实际上仅使用给定的示例文件进行测试。;-)

答案2

比如说,像这样。

\documentclass{article}
\usepackage{fancyvrb}
\usepackage{color}

\def\defaultcode{[0}
\def\bluecode{[1;34}
\def\redcode{[1;31}
\makeatletter
\def\e#1m{%                                                                                                                                                                   
\def\colcode{#1}%                                                                                                                                                             
\ifx\colcode\defaultcode\color{black}%
\else\ifx\colcode\bluecode\color{blue}%
\else\ifx\colcode\redcode\color{red}%
\fi\fi\fi}
\makeatother

\begin{document}
\begin{Verbatim}[commandchars=\\\{\}]
echo -e '\e[1;34mBLUE TEXT\e[0m'
echo -e '\e[1;31mRED TEXT\e[0m'
echo -e 'DEFAULT COLOURED TEXT'
\end{Verbatim}
\end{document}

在此处输入图片描述

控制代码以 开头\e并以 结尾m。因此,我们选取​​介于 之间的所有内容,然后将其与一些预定义代码进行比较。添加其他代码很容易,例如,绿色将由 来支持

\def\greencode{[1;32}

进而

\else\ifx\colcode\greencode\color{green}

\fi最后再添加一个。之后,你只需要一个允许宏的逐字环境。在这里我使用了fancyvrb 包。请注意,您需要更改颜色

\ifx\colcode\defaultcode\color{black}

如果您的默认颜色不是黑色。

相关内容