我想定义一个非常类似于的宏\url
,它接受或多或少任意的字符串(包括哈希/ )#
并将它们传递给其他命令(即,,,\href
)。我尝试应用一些\url
\nolinkurl
其他 解决方案(例如,本地更改 catcodes 等),但失败了。我还希望我的新宏可以传递给其他宏,例如\footnote
。
以下不起作用的 MWE 展示了一种实现,将任意字符串作为输入,将它们转换为转义的 tex 兼容字符串(通过使用\tl_to_str:n
xparse命令处理器),并将它们传递下去,没有任何错误。然而,这完全破坏了宏的含义,它只是输出转义的文本(我真的不明白为什么)。
理想情况下,宏甚至会在将相应的参数传递给时检测哈希并对其进行转义\href
,因为这是必需的(但对于原始的则不是\url
)。
\documentclass{article}
\PassOptionsToPackage{hyphens}{url}
\usepackage[hidelinks]{hyperref}
\usepackage{xparse}
\usepackage{letltxmacro}
\ExplSyntaxOn
\LetLtxMacro\oldurl\url
\RenewDocumentCommand\url{>{\tl_to_str:n}m>{\tl_to_str:n}g}{%
\IfNoValueTF{#2}{%
\oldurl{#1}%
}{%
\href{#1}{#2}%
}%
}
\ExplSyntaxOff
\setlength{\parindent}{0pt}
\begin{document}
\smallskip
\noindent\rule{\linewidth}{1ex}
\smallskip
url\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break/#me}
\smallskip
2url\#1\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break-me/#please}{www.yahoo.com/break/me/please}
\smallskip
2url\#2\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break-me/please}{www.yahoo.com/break/me/#please}
\smallskip
2url\#3\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break-me/#please}{www.yahoo.com/break-me/#please}
\noindent\rule{\linewidth}{1ex}
footnote: \footnote{\url{https://www.yahoo.com/break-me/#please}{www.yahoo.com/break-me/#please}}
\end{document}
答案1
你不能在参数处理器中放置任意命令。参数处理器必须将其结果保留在 中\ProcessedArgument
,因此而不是:
\RenewDocumentCommand\url{>{\tl_to_str:n}m}{...}
你需要:
\cs_new_protected:Npn \Detokenize #1
{ \tl_set:Nx \ProcessedArgument { \tl_to_str:n {#1} } }
\RenewDocumentCommand\url{>{\Detokenize}m}{...}
也就是说,\detokenize
哈希值加倍,因此您需要取消加倍才能获得正确的 URL。我定义了一个\UndoubleHashes
参数处理器,它使用 将参数转换为字符串\tl_to_str:n
,然后循环查找双重哈希并将其替换为单个哈希。
此外,您必须确保特殊字符具有\url
预期的 catcode,因此您需要使\^^M
、\%
、\#
、\&
、\_
、处于\~
活动\$
状态(但“其他”也可以)。
经过此更改,PDF 查看器的工具提示将显示正确的 URL:
\documentclass{article}
\PassOptionsToPackage{hyphens}{url}
\usepackage[hidelinks]{hyperref}
\usepackage{xparse}
\usepackage{letltxmacro}
\makeatletter
\DeclareRobustCommand*{\breakhref}[3][]{%
\begingroup
\setkeys{href}{#1}%
\hyper@normalise\breakhref@{#2}{#3}}
\begingroup
\lccode`\~=`\#
\lowercase{\endgroup
\def\breakhref@#1{\expandafter\breakhref@split#1~~\\}%
\def\breakhref@split#1~#2~#3\\#4{%
\hyper@@link{#1}{#2}{#4}%
\endgroup}}%
\makeatother
\ExplSyntaxOn
\cs_new_protected:Npn \UndoubleHashes #1
{
\tl_set:Nx \ProcessedArgument
{ \stefanct_undouble_hashes:n {#1} }
}
\cs_new:Npx \stefanct_undouble_hashes:n #1
{
\exp_not:N \exp_last_unbraced:No
\exp_not:N \__stefanct_undouble_hashes:w { \exp_not:N \tl_to_str:n {#1} }
\c_hash_str \c_hash_str \exp_not:N \q_nil \s_stop
}
\use:x
{
\cs_new:Npn \exp_not:N \__stefanct_undouble_hashes:w
##1 \c_hash_str \c_hash_str ##2
{
##1
\exp_not:N \quark_if_nil:nTF {##2}
{ \exp_not:N \use_none_delimit_by_s_stop:w }
{ \c_hash_str ##2 }
\exp_not:N \__stefanct_undouble_hashes:w
}
}
\tl_new:N \l__stefanct_tmpa_tl
\cs_generate_variant:Nn \tl_replace_all:Nnn { Nx }
\LetLtxMacro\oldurl\url
\RenewDocumentCommand\url { }
{
\group_begin:
\tl_map_inline:nn { \^^M \% \# \& \_ \~ \$ }
{ \char_set_catcode_active:n { `##1 } }
\char_set_catcode_other:n { `\# }
\urlaux
}
\group_begin:
\char_set_catcode_active:n { `\# }
\char_set_catcode_parameter:n { `\$ }
\cs_new_protected:Npn \stefanct_url:nn $1 $2
{
\tl_set:Nn \l__stefanct_tmpa_tl {$2}
\tl_replace_all:Nxn \l__stefanct_tmpa_tl { \c_hash_str } { # }
\char_set_active_eq:NN # \__stefanct_breakable_hash:
\IfNoValueTF {$2}
{ \oldurl {$1} }
{ \exp_args:NnV \breakhref {$1} \l__stefanct_tmpa_tl }
\group_end:
}
\group_end:
\cs_new_protected:Npn \__stefanct_breakable_hash:
{
\penalty \UrlBreakPenalty
\c_hash_str
}
\NewDocumentCommand\urlaux{>{\UndoubleHashes}m>{\UndoubleHashes}g}
{ \stefanct_url:nn {#1} {#2} }
\ExplSyntaxOff
\setlength{\parindent}{0pt}
\begin{document}
\smallskip
\noindent\rule{\linewidth}{1ex}
\smallskip
url\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break/#me%too}
\smallskip
url\hfill\hspace{0.6\linewidth}\url{httpswwwyahoocombreak#me%too}
\smallskip
2url\#1\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break-me/#please}{www.yahoo.com/break/me/please}
\smallskip
2url\#2\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break-me/please}{www.yahoo.com/break/me/#please}
\smallskip
2url\#3\hfill\hspace{0.6\linewidth}\url{https://www.yahoo.com/break-me/#please}{www.yahoo.com/break-me/#please}
\noindent\rule{\linewidth}{1ex}
footnote: \footnote{\url{https://www.yahoo.com/break-me/#please}{www.yahoo.com/break-me/#please}}
\end{document}
代码允许在#
符号前拆分 URL。要拆分它后a 、改变定义中的和 的#
顺序。\penalty \UrlBreakPenalty
\c_hash_str
\__stefanct_breakable_hash: