新命令内的正则表达式可防止制作工具提示的工具提示

新命令内的正则表达式可防止制作工具提示的工具提示

我正在制作一个文档,希望词汇表中条目的定义能够作为工具提示出现。

工具提示代码本身来自答案: 无法在工具提示中嵌入超链接引用

我的函数接受 3 个参数,第一个参数是定义工具提示函数的名称,第二个参数是出现在词汇表中的名称 - 可能超过 1 个单词,第三个参数是定义。

\newcommand{\defTerm}[3]{%
\expandafter\newcommand\csname #1\endcsname[1]{%
{\textbf{\tooltip{##1}{\begin{varwidth}{10cm}#3\end{varwidth}}}}%
}%
\newglossaryentry{#1}{name=#2,description={#3}}%
}

理想情况下,带有定义的工具提示也适用于词汇表本身的术语。这意味着在某些情况下,在正文中,第三个参数将包含创建工具提示的命令。

例如:

\defTerm{GP}{GP}{The ground plane.}
\defTerm{Transverse}{Transverse}{This is orthogonal to the \GP{GP}.}

然后在文本中

The \Transverse{transverse} plane is ...

尝试创建包含内容的工具提示时失败

这与 \GP{GP} 正交。

但是\GP{GP}尝试在其中制作工具提示。

我想要实现的是修改\defTerm命令,以便在使用工具提示命令时,正则表达式将定义(参数#3)内的任何命令替换为\textbf。在普通正则表达式中搜索:(\\[A-Z]\w+{)([a-zA-Z\s]*)(})并替换为\textbf{$2}

这里我有两个问题。我在测试环境中使用正则表达式,但我不知道如何搜索反斜杠字符。这可能与编译顺序有关。我的第二个问题与如何使用\ExplSyntaxOn和有关\ExplSyntaxOff。我工作的不工作的命令是

% Command to add items to glossary and create the command for using tooltips
\newcommand{\defTerm}[3]{%
\expandafter\newcommand\csname #1\endcsname[1]{%
    \ExplSyntaxOn
    \tl_set:Nn \l_tmpa_tl { #3 }
    \regex_replace_all:nnN {([A-Z]\w+{)([a-zA-Z\s]*)(})} {textbf{\2}} \l_tmpa_tl %Also should have backslash
    \tl_use:N \l_tmpa_tl
    \ExplSyntaxOff
    {\textbf{\tooltip{##1}{\begin{varwidth}{10cm}#3\end{varwidth}}}}%
}%
\newglossaryentry{#1}{name=#2,description={#3}}%
}

以下是完整的工作代码。我已将想要进行的更改注释掉。

\documentclass{article}
\usepackage{varwidth} % To allow maximum width boxes in tooltips
\usepackage{tooltips}

\begin{document}
\defTerm{GP}{GP}{The ground plane.}
\defTerm{Transverse}{Transverse}{This is orthogonal to the GP.}
%\defTerm{Transverse}{Transverse}{This is orthogonal to the \GP{GP}.} % What I would like to use instead of the line above
The \Transverse{transverse} plane is ...
\end{document} 

下面是 tooltips.sty。说实话,我对 \tootip 命令了解不多。我只需要不带星号的选项,不需要更改字体颜色,因此任何简化都会受到赞赏。

% https://tex.stackexchange.com/questions/397639/cant-embed-hyperlink-references-in-a-tooltip

\ProvidesPackage{tooltips}

% Command to add items to glossary and create the command for using tooltips
\newcommand{\defTerm}[3]{%
\expandafter\newcommand\csname #1\endcsname[1]{%
    % \ExplSyntaxOn
    % \tl_set:Nn \l_tmpa_tl { #3 }
    % \regex_replace_all:nnN {([A-Z]\w+{)([a-zA-Z\s]*)(})} {textbf{\2}} \l_tmpa_tl %Also should have backslash
    % \tl_use:N \l_tmpa_tl
    % \ExplSyntaxOff
    {\textbf{\tooltip{##1}{\begin{varwidth}{10cm}#3\end{varwidth}}}}%
}%
%\newglossaryentry{#1}{name=#2,description={#3}}%
}


\usepackage{pdfbase}[2017/03/16]
\usepackage{xparse,ocgbase}
\usepackage{xcolor,calc}
\usepackage{tikzpagenodes,linegoal}
\usetikzlibrary{calc}

\ExplSyntaxOn
\let\tpPdfLink\pbs_pdflink:nn
\let\tpPdfAnnot\pbs_pdfannot:nnnn\let\tpPdfLastAnn\pbs_pdflastann:
\let\tpAppendToFields\pbs_appendtofields:n
\def\tpPdfXform{\pbs_pdfxform:nnnnn{1}{1}{}{}}
\let\tpPdfLastXform\pbs_pdflastxform:
\let\cListSet\clist_set:Nn\let\cListItem\clist_item:Nn
\ExplSyntaxOff

\makeatletter
\NewDocumentCommand{\tooltip}{%
   ssssO{\ifdefined\@linkcolor\@linkcolor\else black\fi}mO{yellow!20}mO{0pt,0pt}%
}{{%
  \leavevmode%
  \IfBooleanT{#2}{%
    %for variants with two and more stars, put tip box on a PDF Layer (OCG)
\ocgbase@new@ocg{tipOCG.\thetcnt}{%
      /Print<</PrintState/OFF>>/Export<</ExportState/OFF>>%
    }{false}%
    \xdef\tpTipOcg{\ocgbase@last@ocg}%
    %prevent simultaneous visibility of multiple non-draggable tooltips
    \ocgbase@add@ocg@to@radiobtn@grp{tool@tips}{\ocgbase@last@ocg}%
  }%
  \tpPdfLink{%
    \IfBooleanTF{#4}{%
      /Subtype/Link/Border[0 0 0]/A <</S/SetOCGState/State [/Toggle \tpTipOcg]>>
    }{%
      /Subtype/Screen%
      /AA<<%
        \IfBooleanTF{#3}{%
          /E<</S/SetOCGState/State [/Toggle \tpTipOcg]>>%
        }{%
          \IfBooleanTF{#2}{%
            /E<</S/SetOCGState/State [/ON \tpTipOcg]>>%
            /X<</S/SetOCGState/State [/OFF \tpTipOcg]>>%
           }{
            \IfBooleanTF{#1}{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                }%
                if(fd.display==display.hidden){%
                  fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }else{%
                  if(!click\thetcnt&&!dragging\thetcnt)
 {fd.display=display.hidden;}%
                  if(!dragging\thetcnt){click\thetcnt=false;}%
                }%
                this.dirty=false;%
              )>>%
            }{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                 }%
                if(fd.display==display.hidden){%
                   fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }%
              this.dirty=false;%
              )>>%
              /X<</S/JavaScript/JS(%
                if(!click\thetcnt&&!dragging\thetcnt){fd.display=display.hidden;}%
                if(!dragging\thetcnt){click\thetcnt=false;}%
                this.dirty=false;%
              )>>%
            }%
            /U<</S/JavaScript/JS(click\thetcnt=true;this.dirty=false;)>>%
            /PC<</S/JavaScript/JS (%
              var fd=this.getField('tip.\thetcnt');%
              try{fd.rect=fdor\thetcnt;}catch(e){}%
              fd.display=display.hidden;this.dirty=false;%
             )>>%
            /PO<</S/JavaScript/JS(this.dirty=false;)>>%
          }%
        }%
      >>%
    }%
  }{{\color{#5}#6}}%
  \sbox\tiptext{%
    \IfBooleanT{#2}{%
       \ocgbase@oc@bdc{\tpTipOcg}\ocgbase@open@stack@push{\tpTipOcg}}%
    \fcolorbox{black}{#7}{#8}%
    \IfBooleanT{#2}{\ocgbase@oc@emc\ocgbase@open@stack@pop\tpNull}%
  }%
   \cListSet\tpOffsets{#9}%
   \edef\twd{\the\wd\tiptext}%
   \edef\tht{\the\ht\tiptext}%
   \edef\tdp{\the\dp\tiptext}%
   \tipshift=0pt%
   \IfBooleanTF{#2}{%
     %OCG-based (that is, all non-draggable) boxes should not extend beyond the
     %current column as they may get overlaid by text in the neighbouring column
     \setlength\whatsleft{\linegoal}%
   }{%
     \measureremainder{\whatsleft}%
   }%
   \ifdim\whatsleft<\dimexpr\twd+\cListItem\tpOffsets{1}\relax%
     \setlength\tipshift{\whatsleft-\twd-\cListItem\tpOffsets{1}}\fi%
   \IfBooleanF{#2}{\tpPdfXform{\tiptext}}%
   \raisebox{\heightof{#6}+\tdp+\cListItem\tpOffsets{2}}[0pt][0pt]{%
     \makebox[0pt][l]{\hspace{\dimexpr\tipshift+\cListItem\tpOffsets{1}\relax}%
     \IfBooleanTF{#2}{\usebox{\tiptext}}{%
       \tpPdfAnnot{\twd}{\tht}{\tdp}{%
         /Subtype/Widget/FT/Btn/T (tip.\thetcnt)%
         /AP<</N \tpPdfLastXform>>%
         /MK<</TP 1/I \tpPdfLastXform/IF<</S/A/FB true/A [0.0 0.0]>>>>%
         /Ff 65536/F 3%
         /AA <<%
           /U <<%
             /S/JavaScript/JS(%
               var fd=event.target;%
               var mX=this.mouseX;var mY=this.mouseY;%
               var drag=function(){%
                 var nX=this.mouseX;var nY=this.mouseY;%
                 var dX=nX-mX;var dY=nY-mY;%
                 var fdr=fd.rect;%
                 fdr[0]+=dX;fdr[1]+=dY;fdr[2]+=dX;fdr[3]+=dY;%
                 fd.rect=fdr;mX=nX;mY=nY;%
               };%
               if(!dragging\thetcnt){%
                 dragging\thetcnt=true;Int=app.setInterval("drag()",1);%
               }%
               else{app.clearInterval(Int);dragging\thetcnt=false;}%
               this.dirty=false;%
             )%
           >>%
         >>%
       }%
       \tpAppendToFields{\tpPdfLastAnn}%
     }%
   }}%
   \stepcounter{tcnt}%
 }}
 \makeatother
 \newsavebox\tiptext\newcounter{tcnt}
 \newlength{\whatsleft}\newlength{\tipshift}
 \newcommand{\measureremainder}[1]{%
   \begin{tikzpicture}[overlay,remember picture]
     \path let \p0 = (0,0), \p1 = (current page.east) in
       [/utils/exec={\pgfmathsetlength#1{\x1-\x0}\global#1=#1}];
   \end{tikzpicture}%
 }

答案1

我建议如下:

\newcommand\tooltip@textbf[9]{\textbf{#6}}
\DeclareExpandableDocumentCommand{\tooltip}{%
   ssssO{\ifdefined\@linkcolor\@linkcolor\else black\fi}mO{yellow!20}O{0pt,0pt}m%
}{{%
  \expandafter\let\csname tooltip code\endcsname\tooltip@textbf
   % ... rest of macro definition here
   %
}

这使得\tooltip将自身定义为\textbf。请注意\DeclareExpandableDocumentCommand-- 的参数文本中使用的命令\tooltip必须是可扩展的(这是嵌套工具提示中发生的错误,因为\tooltip命令本身不可扩展)。另请注意,我交换了 的参数 8 和 9 的顺序\tooltip。这是因为可扩展命令不能以可选参数结尾。这避免了对任何花哨的正则表达式的需求。

对于您的\defTerm命令,我建议您在定义的命令中添加一个特殊前缀和一个特殊的“访问器”命令。这样您就可以拥有一个名称与通用命令相同的术语,并且更容易看出哪些宏是术语。此更改与工具提示问题的解决方案无关。

\newcommand\useTerm[1]{\csname term-#1\endcsname}
\newcommand{\defTerm}[3]{%
    \expandafter\newcommand\csname term-#1\endcsname[1]{%
    {\tooltip{\textbf{##1}}{\begin{varwidth}{10cm}#3\end{varwidth}}}%
    }%
    \newglossaryentry{#1}{name=#2,description={#3}}%
}

最后,如果您要使用 expl3 解决方案,请注意,、、\makeatletter和所有通常都应在命令之外发生。例如:\makeatletter\ExplSyntaxOn\ExplSyntaxOff

% Wrong, leads to "Undefined control sequence \tl"
\newcommand\mycommand{%
    \ExplSyntaxOn
    \tl_set:Nn \l_tmpa_tl { something }
    % ... more stuff
    \ExplSyntaxOff
}

% Right (or at least it will work)
\ExplSyntaxOn
\newcommand\mycommand{
    \tl_set:Nn \l_tmpa_tl { something }
    % ... more stuff
}
\ExplSyntaxOff

% Right:
\makeatletter
\newcommand\mycommand{\@firstofone}
\makeatother

好的,这是完整的代码。请注意,我删除了包glossaries,所以我注释掉了该\newglossaryentry行。这是因为该glossaries包与你的问题的任何部分都无关。如果你使用它,你可以放回去\usepackage{glossaries}并取消注释\newglossaryentry

\documentclass{article}
\usepackage{varwidth} % To allow maximum width boxes in tooltips
\usepackage{tooltips}
\begin{document}
\defTerm{GP}{GP}{The ground plane.}
\defTerm{Transverse}{Transverse}{This is orthogonal to the \useTerm{GP}{GP}.} % What I would like to use instead of the line above

The \useTerm{Transverse}{transverse} plane is ...
\end{document} 

tooltips.sty

\ProvidesPackage{tooltips}

% Command to add items to glossary and create the command for using tooltips

\newcommand\useTerm[1]{\csname term-#1\endcsname}
\newcommand{\defTerm}[3]{%
    \expandafter\newcommand\csname term-#1\endcsname[1]{%
    {\tooltip{\textbf{##1}}{\begin{varwidth}{10cm}#3\end{varwidth}}}%
    }%
    %\newglossaryentry{#1}{name=#2,description={#3}}%
}




\usepackage{pdfbase}[2017/03/16]
\usepackage{xparse,ocgbase}
\usepackage{xcolor,calc}
\usepackage{tikzpagenodes,linegoal}
\usetikzlibrary{calc}

\ExplSyntaxOn
\let\tpPdfLink\pbs_pdflink:nn
\let\tpPdfAnnot\pbs_pdfannot:nnnn\let\tpPdfLastAnn\pbs_pdflastann:
\let\tpAppendToFields\pbs_appendtofields:n
\def\tpPdfXform{\pbs_pdfxform:nnnnn{1}{1}{}{}}
\let\tpPdfLastXform\pbs_pdflastxform:
\let\cListSet\clist_set:Nn\let\cListItem\clist_item:Nn
\ExplSyntaxOff

\makeatletter
\newcommand\tooltip@textbf[9]{{#6}}
\DeclareExpandableDocumentCommand{\tooltip}{%
   ssssO{\ifdefined\@linkcolor\@linkcolor\else black\fi}mO{yellow!20}O{0pt,0pt}m%
}{{%
  \expandafter\let\csname tooltip code\endcsname\tooltip@textbf
  \leavevmode%
  \IfBooleanT{#2}{%
    %for variants with two and more stars, put tip box on a PDF Layer (OCG)
\ocgbase@new@ocg{tipOCG.\thetcnt}{%
      /Print<</PrintState/OFF>>/Export<</ExportState/OFF>>%
    }{false}%
    \xdef\tpTipOcg{\ocgbase@last@ocg}%
    %prevent simultaneous visibility of multiple non-draggable tooltips
    \ocgbase@add@ocg@to@radiobtn@grp{tool@tips}{\ocgbase@last@ocg}%
  }%
  \tpPdfLink{%
    \IfBooleanTF{#4}{%
      /Subtype/Link/Border[0 0 0]/A <</S/SetOCGState/State [/Toggle \tpTipOcg]>>
    }{%
      /Subtype/Screen%
      /AA<<%
        \IfBooleanTF{#3}{%
          /E<</S/SetOCGState/State [/Toggle \tpTipOcg]>>%
        }{%
          \IfBooleanTF{#2}{%
            /E<</S/SetOCGState/State [/ON \tpTipOcg]>>%
            /X<</S/SetOCGState/State [/OFF \tpTipOcg]>>%
           }{
            \IfBooleanTF{#1}{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                }%
                if(fd.display==display.hidden){%
                  fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }else{%
                  if(!click\thetcnt&&!dragging\thetcnt)
 {fd.display=display.hidden;}%
                  if(!dragging\thetcnt){click\thetcnt=false;}%
                }%
                this.dirty=false;%
              )>>%
            }{%
              /E<</S/JavaScript/JS(%
                var fd=this.getField('tip.\thetcnt');%
                if(typeof(click\thetcnt)=='undefined'){%
                  var click\thetcnt=false;%
                  var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
                 }%
                if(fd.display==display.hidden){%
                   fd.delay=true;fd.display=display.visible;fd.delay=false;%
                }%
              this.dirty=false;%
              )>>%
              /X<</S/JavaScript/JS(%
                if(!click\thetcnt&&!dragging\thetcnt){fd.display=display.hidden;}%
                if(!dragging\thetcnt){click\thetcnt=false;}%
                this.dirty=false;%
              )>>%
            }%
            /U<</S/JavaScript/JS(click\thetcnt=true;this.dirty=false;)>>%
            /PC<</S/JavaScript/JS (%
              var fd=this.getField('tip.\thetcnt');%
              try{fd.rect=fdor\thetcnt;}catch(e){}%
              fd.display=display.hidden;this.dirty=false;%
             )>>%
            /PO<</S/JavaScript/JS(this.dirty=false;)>>%
          }%
        }%
      >>%
    }%
  }{{\color{#5}#6}}%
  \sbox\tiptext{%
    \IfBooleanT{#2}{%
       \ocgbase@oc@bdc{\tpTipOcg}\ocgbase@open@stack@push{\tpTipOcg}}%
    \fcolorbox{black}{#7}{#9}%
    \IfBooleanT{#2}{\ocgbase@oc@emc\ocgbase@open@stack@pop\tpNull}%
  }%
   \cListSet\tpOffsets{#8}%
   \edef\twd{\the\wd\tiptext}%
   \edef\tht{\the\ht\tiptext}%
   \edef\tdp{\the\dp\tiptext}%
   \tipshift=0pt%
   \IfBooleanTF{#2}{%
     %OCG-based (that is, all non-draggable) boxes should not extend beyond the
     %current column as they may get overlaid by text in the neighbouring column
     \setlength\whatsleft{\linegoal}%
   }{%
     \measureremainder{\whatsleft}%
   }%
   \ifdim\whatsleft<\dimexpr\twd+\cListItem\tpOffsets{1}\relax%
     \setlength\tipshift{\whatsleft-\twd-\cListItem\tpOffsets{1}}\fi%
   \IfBooleanF{#2}{\tpPdfXform{\tiptext}}%
   \raisebox{\heightof{#6}+\tdp+\cListItem\tpOffsets{2}}[0pt][0pt]{%
     \makebox[0pt][l]{\hspace{\dimexpr\tipshift+\cListItem\tpOffsets{1}\relax}%
     \IfBooleanTF{#2}{\usebox{\tiptext}}{%
       \tpPdfAnnot{\twd}{\tht}{\tdp}{%
         /Subtype/Widget/FT/Btn/T (tip.\thetcnt)%
         /AP<</N \tpPdfLastXform>>%
         /MK<</TP 1/I \tpPdfLastXform/IF<</S/A/FB true/A [0.0 0.0]>>>>%
         /Ff 65536/F 3%
         /AA <<%
           /U <<%
             /S/JavaScript/JS(%
               var fd=event.target;%
               var mX=this.mouseX;var mY=this.mouseY;%
               var drag=function(){%
                 var nX=this.mouseX;var nY=this.mouseY;%
                 var dX=nX-mX;var dY=nY-mY;%
                 var fdr=fd.rect;%
                 fdr[0]+=dX;fdr[1]+=dY;fdr[2]+=dX;fdr[3]+=dY;%
                 fd.rect=fdr;mX=nX;mY=nY;%
               };%
               if(!dragging\thetcnt){%
                 dragging\thetcnt=true;Int=app.setInterval("drag()",1);%
               }%
               else{app.clearInterval(Int);dragging\thetcnt=false;}%
               this.dirty=false;%
             )%
           >>%
         >>%
       }%
       \tpAppendToFields{\tpPdfLastAnn}%
     }%
   }}%
   \stepcounter{tcnt}%
 }}
 \makeatother
 \newsavebox\tiptext\newcounter{tcnt}
 \newlength{\whatsleft}\newlength{\tipshift}
 \newcommand{\measureremainder}[1]{%
   \begin{tikzpicture}[overlay,remember picture]
     \path let \p0 = (0,0), \p1 = (current page.east) in
       [/utils/exec={\pgfmathsetlength#1{\x1-\x0}\global#1=#1}];
   \end{tikzpicture}%
 }

相关内容