CTeX-org / ctex-kit

Macro Packages and Scripts for Chinese TeX users
966 stars 124 forks source link

字体的 ligature 表现与 fontspec 不一致 #629

Closed duskmoon314 closed 2 years ago

duskmoon314 commented 2 years ago

我在使用 minted 和 FiraCode 字体来渲染代码块。但我发现 FiraCode 的 ligature 表现在加入 \usepackage[UTF8]{ctex} 后和使用 \usepackage{fontspec} 的英文环境不一致,大部分 ligature 都未启用的样子。我目前不是很清楚原因何在。

一个简单的例子:

\documentclass{article}

% 英文环境
\usepackage{fontspec}

% 中文环境
% \usepackage[UTF8]{ctex}

% 导入 FiraCode
\setmonofont[
    Contextuals={Alternate}
]{Fira Code}

\usepackage{minted}

\begin{document}

\begin{minted}{haskell}
    .= .- := =:=
 == != === !== =/=

<<- <-- <- <-> -> --> ->>
<=< <<= <==    <=> => ==> =>> >=>
>>= >>- >-     -< -<< =<<
 <~~ <~ ~~~ ~> ~~>

<<< << <= <>  >= >> >>>
     <| <|> |>

     <$ <$> $>
     <+ <+> +>
     <* <*> *>

\\ \\\ {- -} // ///
   /* /** **/ */ 
</ <!-- www  --> />
0xF  9:45  m-x *ptr

;; ;;; :: ::: !! !!!
?? ??? %% %%% && &&& 
|| ||| .. ... ..< []
-- --- ++ +++ ** ***

   ~= ~- -~ =~ ~@
 ^= ?= /= /== |= ||=
    ## ### ####
  #{ #[ #( #? #_ #_(

a*b a*A B*b A*B *a *A a* A*
a-b a-A B-b A-B -a -A a- A-
a+b a+A B+b A+B +a +A a+ A+
a:b a:A B:b A:B :a :A a: A:
\end{minted}

\end{document}

注:似乎在 verbatim 环境中,中英文是同样的效果。

REF:

muzimuzhi commented 2 years ago

meta

大部分 ligature 都未启用的样子。我目前不是很清楚原因何在。

建议贴上错误和正确输出的截图,甚至在截图里标注问题的具体位置,方便他人直接看出差别。不要只用文字描述。

简化

示例1:一些探索步骤 ```tex \documentclass{article} \usepackage{minted} \usepackage{xeCJK} \setmonofont{Fira Code} \def\pygdefs{% \def\PYGZlt{\char`\<} % less than \def\PYGZgt{\char`\>} % greater than \def\PYGZhy{\char`\-} % hyphen } \begin{document} \verb|\ttfamily|:\par {\ttfamily <> <-> } built-in \verb|verbatim| environment \xeCJKsetup{Verb=true} \makeatletter\def\verbatim@nolig@list{}\makeatother \begin{verbatim} <> <-> \end{verbatim} \verb|Verbatim| environment from \verb|fancyvrb/fvextra| \begin{Verbatim} <> <-> \end{Verbatim} \verb|minted| environment \begin{minted}{text} <> <-> \end{minted} Excerpted \verb|.pygtex| file content {\pygdefs \begin{Verbatim}[commandchars=\\\{\}] \PYGZlt{}\PYGZgt{} \PYGZlt{}\PYGZhy{}\PYGZgt{} \end{Verbatim} Excerpted \verb|.pygtex| file content, \verb|xeCJKactive=false| {\xeCJKsetup{xeCJKactive=false} \begin{Verbatim}[commandchars=\\\{\}] \PYGZlt{}\PYGZgt{} \PYGZlt{}\PYGZhy{}\PYGZgt{} \end{Verbatim} } Excerpted \verb|.pygtex| file content, \verb|xeCJKactive=true|, no extra braces \begin{Verbatim}[commandchars=\\\{\}] \PYGZlt\PYGZgt \PYGZlt\PYGZhy\PYGZgt \end{Verbatim} \verb|\ttfamily|, extra braces\par \ttfamily <{}> <{}-> <-{}> <{}-{}> \subsection*{w/o extra braces} <> <-> <{}> <{}-> <-{}> <{}-{}> \PYGZlt\PYGZgt \PYGZlt\PYGZhy\PYGZgt \PYGZlt{}\PYGZgt{} \PYGZlt{}\PYGZhy{}\PYGZgt{} } \end{document} ``` ![image](https://user-images.githubusercontent.com/6376638/174976213-eb399108-31ee-4be2-a244-f4d66556faea.png)

尝试解决

  1. 如果代码里没有中文,直接对相应环境关闭 xeCJK 的中文处理即可:\AddToHook{env/Verbatim/begin}{\xeCJKsetup{xeCJKactive=false}}

  2. 可以在 minted 里重定义 \PYGxxx 相关命令,改成需要一个参数,吃掉空的大括号。例如原来是 \def\PYGZlt{\char`\<} (lt = less than),重定义成 \def\PYGZlt#1{\char`\<}。因为 minted 已经有一个 patch \PYGZsq (sq = single quote) 的内部宏 \minted@patch@PYGZsq,可以把需要的重定义加进去。

    • 真正的改动应该(主要)发生在 pygments 项目,pygments/formatters/latex.py
      示例2:重定义 \PYGZxx 等命令,尝试解决
    % see https://tex.stackexchange.com/a/294369
    \begin{filecontents}[noheader]{ligatures.txt}
        .= .-  := =:=
     == != === !== =/=
    
    <<- <-- <- <-> -> --> ->>
    <=< <<= <==    <=> => ==> =>> >=>
    >>= >>- >-     -< -<< =<<
        <~~ <~ ~~~ ~> ~~>
    
    <<< << <= <>  >= >> >>>
         <| <|> |>
    
         <$ <$> $>
         <+ <+> +>
         <* <*> *>
    
    \\ \\\ {- -} // ///
       /* /** **/ */ 
    </ <!-- www  --> />
    0xF  9:45  m-x *ptr
    
    ;; ;;; :: ::: !! !!!
    ?? ??? %% %%% && &&& 
    || ||| .. ... ..< []
    -- --- ++ +++ ** ***
    
       ~= ~- -~ =~ ~@
     ^= ?= /= /== |= ||=
        ## ### ####
      #{ #[ #( #? #_ #_(
    
    a*b a*A B*b A*B *a *A a* A*
    a-b a-A B-b A-B -a -A a- A-
    a+b a+A B+b A+B +a +A a+ A+
    a:b a:A B:b A:B :a :A a: A:
    \end{filecontents}
    
    \documentclass[landscape]{article}
    \usepackage[margin=1in]{geometry}
    \usepackage{minted}
    \usepackage{multicol}
    \usepackage{xeCJK}
    
    % see https://github.com/gpoore/minted/issues/335
    \setmonofont{Fira Code}[Contextuals={Alternate},CharacterVariant={25,32}]
    
    \renewcommand{\columnseprule}{.4pt}
    
    \makeatletter
    \def\mintedPygPatches{%
      % insert \minted@patch@PYG
      \makeatletter
      \apptocmd\minted@patch@PYGZsq{\minted@patch@PYG}{}{\PatchFailed}%
      \makeatother
      % repatch \PYGZsq
      \begingroup
      \catcode`\'=\active
      \gdef\minted@patch@PYGZsq@i{\gdef\PYGZsq####1{'}}% does \PYGZsq need \gdef?
      \endgroup
      % patch other \PYGZxx commands,
      % - before: \def\PYGZbs{\char`\\}
      % - after:  \def\PYGZbs#1{\char`\\}
      % Command name list is excerpted from default.pygstyle, or
      % https://github.com/pygments/pygments/blob/master/pygments/formatters/latex.py
      \newcommand\minted@patch@PYG{%
        \@for\pygname:=bs,us,ob,cb,ca,am,lt,gt,sh,pc,dl,hy,dq,ti,at,lb,rb\do{%
          \csxdef{PYGZ\pygname}####1{%
            \unexpanded\expandafter\expandafter\expandafter{%
              \csname PYGZ\pygname\endcsname}}}}%
    }
    \makeatother
    
    \begin{document}
    \begin{multicols}{3}
      \subsection*{xeCJKactive=false}
      \xeCJKsetup{xeCJKactive=false}
      \inputminted{haskell}{ligatures.txt}
      \columnbreak
    
      \subsection*{xeCJKactive=true}
      \xeCJKsetup{xeCJKactive=true}
      \inputminted{haskell}{ligatures.txt}
      \columnbreak
    
      \subsection*{xeCJKactive=true + PYG patches}
      \xeCJKsetup{xeCJKactive=true}
      \mintedPygPatches
      \inputminted{haskell}{ligatures.txt}
    \end{multicols}
    \end{document}

    image

  3. 为什么 xeCJKactive=true 时,<><{}> 不同:

    • xeCJK 把输入分成了不同的类,<>Default-NormalSpace{} 是特殊的 Boundary
    • xeCJK 会在输入之间,插入代码,于是 <{} 变成 < <Default-to-Boundary toks> {}, -{} 变成 - {NormalSpace-to-Boundary toks} {},这些插入的代码妨碍了 ligature 的应用。
    示例3:清除特定 inter char toks ```tex \documentclass{article} \usepackage{xeCJK} \setmonofont{Fira Code} \begin{document} \def\test{\hfill\texttt{<> <->, <{}> <{}-> <-{}> <{}-{}>}} xeCJKactive=true: \ \test \xeCJKsetup{xeCJKactive=false} xeCJKactive=false: \test \xeCJKsetup{xeCJKactive=true} \ExplSyntaxOn \xeCJK_clear_inter_class_toks:nn {Default} {Boundary} % for `<{}` \xeCJK_clear_inter_class_toks:nn {NormalSpace} {Boundary} % for `-{}` \ExplSyntaxOff xeCJKactive=true + clear inter class toks:\test \end{document} ``` ![image](https://user-images.githubusercontent.com/6376638/174990211-7559cb18-b54c-466d-b412-45bc8ac76c39.png)
duskmoon314 commented 2 years ago

感谢您的帮助!