在保留字距调整的同时为单词的一部分添加下划线

在保留字距调整的同时为单词的一部分添加下划线

这个问题导致了一个新的方案的出现:
lua-ul

下划线文本的常用方法(至少是我所知道的:无论是使用\underline还是例如soul包)在使用时会破坏字距调整部分一个词。我目前的解决方案包括两个步骤

  1. 我输入要加下划线的字母(这可以处理与前一个字母的字距)
  2. 我输入下划线
  3. 我抓取以下字母并手动添加字距

MWE 及结果图片

\documentclass{article}

\makeatletter
\def\foo#1{%
   #1%
   \setbox0=\hbox{#1}%
   \hb@xt@\z@{\hss\vrule height -1.2pt depth 1.6pt width\wd0}%
   \addkerncorrection{#1}%
}

\def\addkerncorrection#1#2{%
   \setbox0=\hbox{{#1}{#2}}%
   \setbox1=\hbox{#1#2}%
   \dimen@=\wd1
   \advance\dimen@ by -\wd0
   \kern\dimen@ #2%
}
\makeatother

\begin{document}

V\foo{A}V

VAV

V\underline{A}V

\end{document}

在此处输入图片描述

问题是,如果下划线部分是单词的最后一部分,这种方法就会失败。当然,我可以用一个空组来纠正这个问题,但我想避免这种情况。我有一个:-)基于 from 的有效技巧(我拒绝在这里发布)\new@ifnextchar来测试后面的空格字符,但我开始对这种黑客行为的水平感到不舒服。此外,当使用编码时,它会因诸如 之类的内容而崩溃amsgen,因此应该输入为。utf8l\foo{ie}ßl\foo{ie}\ss

所以问题是:有没有一种(更好的)方法来强调单词的一部分,同时保留正确的字距并且不吞掉空格?

对于那些可能认为这是一个 XY 问题的人来说:这是关于用不同的音调排版诗篇,例如

在此处输入图片描述

答案1

如果你正在使用 LuaTeX,你可以使用新的lua-ul包还允许例如嵌套下划线:

\documentclass{article}
\usepackage{lua-ul,luacolor}
\usepackage{tikzducks,pict2e}

\newunderlinetype\myunderduck{\cleaders\hbox{%
    \begin{tikzpicture}[x=.5ex,y=.5ex,baseline=.8ex]%
      \duck
    \end{tikzpicture}%
}}
\newunderlinetype\myunderwavy{\cleaders\hbox{%
    \setlength\unitlength{.3ex}%
    \begin{picture}(4,0)(0,1)
      \thicklines
      \color{red}%
      \qbezier(0,0)(1,1)(2,0)
      \qbezier(2,0)(3,-1)(4,0)
    \end{picture}%
}}
\newcommand\underDuck[1]{{\myunderduck#1}}
\newcommand\underWavy[1]{{\myunderwavy#1}}
\begin{document}
V\underLine{A}V

\underDuck{VAV}

\underDuck{V}\underLine{AV}

\underDuck{These are \underWavy{ucks}}

\strikeThrough{Dinner is ready!}
\end{document}

在此处输入图片描述

旧答案(后来变成lua-ul

使用 LuaTeX,您可以使用属性来标记要加下划线的字符。这不会干扰字距调整/连字/换行,因为它仅在所有这些操作完成后才起作用。

我为自定义线条粗细、放置、鸭子下划线等添加了一些额外的灵活性......但回调基本上只是在节点树上进行迭代,并且在找到标记节点时绘制引线:

\documentclass{article}
\usepackage{luacode}
\usepackage{tikzducks,pict2e}

\newattribute\underlineattr
\begin{luacode*}
  local underlineattr = token.create'underlineattr'.index
  local underline_types = {}
  function new_underline_type()
    table.insert(underline_types, tex.box[0].head)
    tex.box[0].head = nil
    tex.sprint(#underline_types)
  end
  local add_underline_h
  local function add_underline_v(head)
    for n in node.traverse(head) do
      if head.id == node.id'hlist' then
        add_underline_h(n)
      elseif head.id == node.id'vlist' then
        add_underline_v(n.head)
      end
    end
  end
  function add_underline_h(head)
    node.slide(head.head)
    local last_value
    local first
    for n in node.traverse(head.head) do
      local new_value = node.has_attribute(n, underlineattr)
      if n.id == node.id'hlist' then
        new_value = nil
        add_underline_h(n)
      elseif n.id == node.id'vlist' then
        new_value = nil
        add_underline_v(n.head)
      elseif n.id == node.id'kern' and n.subtype == 0 then
        if n.next and not node.has_attribute(n.next, underlineattr) then
          new_value = nil
        else
          new_value = last_value
        end
      elseif n.id == node.id'glue' and (
          n.subtype == 8 or
          n.subtype == 9 or
          n.subtype == 15 or
      false) then
        new_value = nil
      end
      if last_value ~= new_value then
        if last_value then
          local width = node.rangedimensions(head, first, n)
          local kern = node.new'kern'
          kern.kern = -width
          kern.next = node.copy(underline_types[last_value])
          kern.next.width = width
          kern.next.next = n
          n.prev.next = kern
        end
        if new_value then
          first = n
        end
        last_value = new_value
      end
    end
    if last_value then
      local width = node.rangedimensions(head, first)
      local kern = node.new'kern'
      kern.kern = -width
      kern.next = node.copy(underline_types[last_value])
      kern.next.width = width
      node.tail(head.head).next = kern
    end
  end
  local function filter(b, loc, prev, mirror)
    add_underline_v(b)
    local new_prev = mirror and b.height or b.depth
    if prev > -65536000 then
      local lineglue = tex.baselineskip.width - prev - (mirror and b.depth or b.height)
      local skip
      if lineglue < tex.lineskiplimit then
        skip = node.new('glue', 1)
        node.setglue(skip, node.getglue(tex.lineskip))
      else
        skip = node.new('glue', 2)
        node.setglue(skip, node.getglue(tex.baselineskip))
        skip.width = lineglue
      end
      skip.next = b
      b = skip
    end
    return b, new_prev
    -- return node.prepend_prevdepth(b)
  end
  luatexbase.callbacktypes.append_to_vlist_filter = 3 -- This should not be necessary
  luatexbase.add_to_callback('append_to_vlist_filter', filter, 'add underlines to list')
\end{luacode*}

\newcommand\newunderlinetype[2]{%
  \setbox0\hbox{#2\hskip0pt}%
  \chardef#1=\directlua{new_underline_type()}\relax
}
\newunderlinetype\myunderline{\leaders\vrule height-1ptdepth1.5pt}
\newunderlinetype\mystrikethrough{\leaders\vrule height2.5ptdepth-2pt}
\newunderlinetype\myunderduck{\cleaders\hbox{%
    \begin{tikzpicture}[baseline=3,scale=0.05]%
      \duck
    \end{tikzpicture}%
}}
\newunderlinetype\myunderwavy{\leaders\hbox{%
    \setlength\unitlength{.3mm}%
    \begin{picture}(4,0)(0,1)
      \thicklines
      \color{red}%
      \qbezier(0,0)(1,1)(2,0)
      \qbezier(2,0)(3,-1)(4,0)
    \end{picture}%
}}
\newcommand\underLine[1]{{\underlineattr=\myunderline#1}}
\newcommand\underDuck[1]{{\underlineattr=\myunderduck#1}}
\newcommand\underWavy[1]{{\underlineattr=\myunderwavy#1}}
\newcommand\strikeThrough[1]{{\underlineattr=\mystrikethrough#1}}
\begin{document}
V\underLine{A}V

\underDuck{VAV}

\underDuck{V}\underLine{AV}

\underDuck{These are \underWavy{ucks}}

\strikeThrough{Dinner is ready!}
\end{document}

结果

相关内容