雾月
雾月
这家伙很懒,什么也没写!

注册于 4年前

回答
217
文章
2
关注者
23

如果只需要 \captionof,不需要强制 abovebelowcaption 宏包就提供这个命令,必须用在一个环境内,不过最好是用 \captionsetup{type=...},这样可以正确处理超链接。

\begin{center}
\captionsetup{type=table...}
\caption{...}
...
\end{center}

如果需要强制设置 caption 的位置,而不管 \caption 是放在图片/文字前面还是后面,那 \captionaboveof 的功能不是这个(至少现在的版本不是)。如果需要类似的功能,floatrow 宏包可以全局设置 caption 的位置,也可用它的 floatrow 环境配合 \ffigbox 等。

封装一下 minipage,设置 \parindent=0pt

\documentclass{article}

\ExplSyntaxOn
\box_new:N \l__skyrmion_left_box
\box_new:N \l__skyrmion_right_box
\box_new:N \l__skyrmion_seg_box
\keys_define:nn { skyrmion }
  {
    lefthand~ratio .fp_set:N = \l__skyrmion_left_ratio_fp ,
    lefthand~width .dim_set:N = \l__skyrmion_left_dim ,
    righthand~ratio .dim_set:N = \l__skyrmion_right_ratio_fp ,
    righthand~width .dim_set:N = \l__skyrmion_right_dim ,
    sidebyside~gap .dim_set:N = \l__skyrmion_gap_dim ,
    sidebyside~gap .initial:n = 10mm ,
    before~left .tl_set:N = \l__skyrmion_before_left_tl ,
    before~right .tl_set:N = \l__skyrmion_before_right_tl ,
  }
\cs_new_protected:Npn \twopartsplit
  {
    \dim_compare:nNnTF \l__skyrmion_right_dim < { 10sp }
      {
        %% Width is too small! Abort!
      }
      {
        \__skyrmion_twopart_end_save:
        \__skyrmion_twopart_save:NN \l__skyrmion_right_box \l__skyrmion_right_dim
        \l__skyrmion_before_right_tl \ignorespaces
      }
  }
\NewDocumentEnvironment{twopart}{ O{} }
  {
    \par \keys_set:nn { skyrmion } {#1}
    \__skyrmion_calc_width: 
    \__skyrmion_twopart_save:NN \l__skyrmion_left_box \l__skyrmion_left_dim
    \l__skyrmion_before_left_tl \ignorespaces
  }
  {
    \__skyrmion_twopart_end_save:
    \__skyrmion_twopart_typeset:
  }
\cs_new:Npn \__skyrmion_calc_width:
  {
    \bool_lazy_and:nnTF
      { \dim_compare_p:nNn \l__skyrmion_left_dim = \c_zero_dim }
      { \dim_compare_p:nNn \l__skyrmion_right_dim = \c_zero_dim }
      {
        \bool_lazy_and:nnTF
          { \fp_compare_p:nNn { abs ( \l__skyrmion_left_ratio_fp ) } < { 0.0001 } }
          { \fp_compare_p:nNn { abs ( \l__skyrmion_right_ratio_fp ) } < { 0.0001 } }
          {
            \dim_set:Nn \l__skyrmion_left_dim { 0.5\linewidth - 0.5\l__skyrmion_gap_dim }
            \dim_set:Nn \l__skyrmion_right_dim { \l__skyrmion_left_dim }
          }
          {
            \fp_compare:nNnTF { abs ( \l__skyrmion_left_ratio_fp ) } < { 0.0001 }
              {
                \dim_set:Nn \l__skyrmion_right_dim 
                  { 
                    \fp_use:N \l__skyrmion_right_ratio_fp 
                    \dimexpr \linewidth - \l__skyrmion_gap_dim \relax
                  }
                \dim_set:Nn \l__skyrmion_left_dim 
                  { \linewidth - \l__skyrmion_gap_dim - \l__skyrmion_right_dim }
              }
              {
                \dim_set:Nn \l__skyrmion_left_dim 
                  { 
                    \fp_use:N \l__skyrmion_left_ratio_fp 
                    \dimexpr \linewidth - \l__skyrmion_gap_dim \relax
                  }
                \dim_set:Nn \l__skyrmion_right_dim 
                  { \linewidth - \l__skyrmion_gap_dim - \l__skyrmion_left_dim }
              }
          }
      }
      {
        \dim_compare:nNnTF \l__skyrmion_left_dim = \c_zero_dim
          {
            \dim_set:Nn \l__skyrmion_left_dim 
              { \linewidth - \l__skyrmion_gap_dim - \l__skyrmion_right_dim }
          }
          {
            \dim_set:Nn \l__skyrmion_right_dim 
              { \linewidth - \l__skyrmion_gap_dim - \l__skyrmion_left_dim }
          }
      }
  }
\cs_new:Npn \__skyrmion_twopart_save:NN #1 #2
  {
    \hbox_set:Nw #1
    \minipage {#2}
    \setlength{\parindent}{0pt}
  }
\cs_new:Npn \__skyrmion_twopart_end_save:
  {
    \endminipage
    \hbox_set_end:
  }
\cs_new:Npn \__skyrmion_seg: { } % 画线,可以用 tikz 等等
\cs_new:Npn \__skyrmion_twopart_typeset:
  {
    \hbox_to_wd:nn { \linewidth }
      {
        \hbox_set:Nn \l__skyrmion_seg_box { \__skyrmion_seg: }
        \hbox_unpack_drop:N \l__skyrmion_left_box
        \hss \clap { \box_use_drop:N \l__skyrmion_seg_box } \hss
        \hbox_unpack_drop:N \l__skyrmion_right_box
      }
  }

\ExplSyntaxOff

\usepackage{lipsum,amsthm,tcolorbox}
\NewTColorBox { textfig } { O { .64\linewidth } }
  {
    sidebyside, sidebyside gap = .02\linewidth, boxsep = 0pt,
    left = 0pt, right = 0pt, top = 3pt, bottom = 0pt,
    lefthand width = #1, standard jigsaw, opacityback = 0, frame empty
  }
\newtheorem{problem}{Problem}

\begin{document}

\begin{textfig}
  \begin{problem}
    \lipsum[2]
  \end{problem}
  \tcblower
  \centering
  \includegraphics[width = .5\linewidth]{example-image-a}
\end{textfig}

\begin{twopart}[sidebyside gap=.02\linewidth, lefthand width=.64\linewidth]
  \begin{problem}
    \lipsum[2]
  \end{problem}
\twopartsplit
  \centering
  \includegraphics[width = .5\linewidth]{example-image-a}
\end{twopart}

\end{document}

也可以用 coffin,代码是相似的。

一般情况下,如果要实现类似的需求,是交换 1、2 个参数的顺序,或者用不同类型的定界符([] () <> 等)。

也可以用一个特殊的值表示默认值,遇到这个特殊值的时候把它替换为默认值。

\ExplSyntaxOn
\cs_new_protected:Npn \__my_make_default:nnn #1#2#3
  {
    \tl_if_eq:nnTF {#3} {#1}
      { \tl_set:Nn \ProcessedArgument {#2} }
      { \tl_set:Nn \ProcessedArgument {#3} }
  }
\cs_new_eq:NN \TrueDefault \__my_make_default:nnn
\ExplSyntaxOff

\NewDocumentCommand{\cmdt}{ 
  >{\TrueDefault{?}{default1}} O{?} % 如果参数为 ? 就替换为 default1
  >{\TrueDefault{?}{default2}} O{?}
  m
}{%
    \textbf{The result of \texttt{\string\cmdt}:} \par
    optional param1: #1 \par
    optional param2: #2 \par
    mandattory param: #3 \par
}

\cmdt{Explorer}

\cmdt[anaconda][python]{Explorer}

\cmdt[anaconda]{Explorer}

\cmdt[?][python]{Explorer}

猜你想要 lttemplates(即 xtemplate),即将进入 LaTeX2e 内核(不出意外的话是 Released 2024-11-01),目前(2024.10.31)可以使用 xelatex-dev 等 dev 版。

l3keys(包括其它键值宏包)都不会每次没给出键时自动使用默认值,只会使用初始值,但 lttemplates 会。

\documentclass{article}

\ExplSyntaxOn
\quark_new:N \q__skyrmion_mark
\quark_new:N \q__skyrmion_stop
\prg_new_conditional:Npnn \skyrmion_if_item_star:n #1 { TF } % 完全可展的函数
  {
    \str_if_eq:eeTF { \skyrmion_strip_end_star:n {#1} } { \exp_not:n {#1} }
      { \prg_return_false: } % 如果移除掉结尾 * 号之后,两个相同,说明原来没有星号
      { \prg_return_true:  } % 反之,则有
  }
\cs_generate_variant:Nn \skyrmion_if_item_star:nTF { e }
\cs_new:Npn \skyrmion_strip_end_star:n #1 % 完全可展的函数,用 e 展开可得到结果
  { 
    \__skyrmion_strip_end_star:w 
      \prg_do_nothing: #1   \q__skyrmion_mark % 如果 #1 以 * 结尾,就用这个
      #1 * \q__skyrmion_mark % 自己加上 *,如果 #1 不以 * 结尾,就会用这个
      \q__skyrmion_stop 
      {#1} % <- 注意这个
  }
\cs_new:Npn \__skyrmion_strip_end_star:w #1 *\q__skyrmion_mark #2 \q__skyrmion_stop
  {
    \tl_if_empty:nTF {#2} % 如果 #2 为空,说明使用是我们自己加的 *
      { \exp_not:n } % <- 这个保护的是上面带花括号的 {#1}
      { \exp_not:o {#1} \use_none:n } % <- \use_none:n 移除上面的 {#1}
  }
\cs_generate_variant:Nn \skyrmion_strip_end_star:n { e }

\clist_new:N \l__skyrmion_tmp_clist
\NewDocumentCommand \skyrmionif { m }
  {
    \skyrmion_strip_end_star:e { \clist_item:Nn \l__skyrmion_tmp_clist {#1} }
    { ~is~ } % <- TeX 会忽略每行开头的空格,
    \skyrmion_if_item_star:eTF
      { \clist_item:Nn \l__skyrmion_tmp_clist {#1} }
      { True } { False }
  }
\clist_set:Nn \l__skyrmion_tmp_clist
  { explorer, fishrows*, eureka, admin, skyrmion }
\ExplSyntaxOff

\begin{document}

\skyrmionif{1}

\skyrmionif{2}

\ExplSyntaxOn \cs_generate_variant:Nn \tl_to_str:n { e } \ttfamily
\tl_to_str:e { \skyrmion_strip_end_star:n { fishrows* } } |
\tl_to_str:e { \skyrmion_strip_end_star:n { {}* } } |
\tl_to_str:e { \skyrmion_strip_end_star:n { {} } } |
\tl_to_str:e { \skyrmion_strip_end_star:n {   * } } |
\ExplSyntaxOff

\end{document}

如果设置了 BoldFontItalicFontxeCJK 不会再使用伪粗体。而 ctex 为预定义的字体都设置了这两个键,因此后续的修改无效。只需重新定义 xeCJKBoldFontItalicFont 这两个键即可。

必须在加载 ctex 之前就加载 xeCJK,或者用 \PassOptionsToPackage,否则设置的 xeCJK 宏包选项无效。

\documentclass{article}

\PassOptionsToPackage{AutoFakeBold=3.0}{xeCJK}
\ExplSyntaxOn
\AddToHook{package/ctex/before}{
  \AddToHook{package/xeCJK/after}{
    \keys_define:nn { xeCJK / features }
      { BoldFont .code:n = , ItalicFont .code:n = }
  }
}
\AddToHook{package/ctex/after}{
  \keys_define:nn { xeCJK / features }
    {
      BoldFont .tl_set:N = \l__xeCJK_font_name_bf_tl ,
      ItalicFont .tl_set:N = \l__xeCJK_font_name_it_tl
    }
}
\ExplSyntaxOff
\usepackage[UTF8]{ctex}
\usepackage{fontspec}

\begin{document}

我上{\bfseries 早八}

\CJKfontspec[BoldFont=FandolHei, ItalicFont=FandolKai]{FandolSong}
我上{\bfseries 早八}

\end{document}

只可用 xelatex 编译。

也可把这两个键定义为使用 AutoFakeBoldAutoFakeSlant 等。

\makeatletter\ExplSyntaxOn
\clist_new:N \g__my_packages_clist
\int_new:N \g__my_level_int
\AddToHook{package/before}{\int_gincr:N \g__my_level_int}
\AddToHook{package/after}{
  \int_gdecr:N \g__my_level_int
  \int_compare:nNnT \g__my_level_int = { 0 }
    { \clist_gput_right:Nx \g__my_packages_clist { \@currname } }
}
\AddToHook{enddocument/info}{ \tl_show:e { Packages:~ \g__my_packages_clist } }
\ExplSyntaxOff\makeatother

\documentclass{} % <- 改成你的文档类
\begin{document}
\end{document}

然后在终端中正常编译。这样可以列出使用的文档类中直接加载的宏包(不包括它们依赖的宏包,如果需要,把代码中的 level=0 改成其它值,或直接删去即可),也会包含这个文档在导言区加载的宏包(这里没有)。

如果不是用 xelatex 编译的,最后一个加载的宏包可能是 epstopdf-base,这是 LaTeX 自动加载的,直接忽略即可。

用通用命令钩子 \AddToHook{cmd/<document cmd>/before}{<write to aux>} 在需要判断是否在正文使用了的命令前加上一段代码,在辅助文件中写入一些标记,然后在 begindocument 钩子(或 \AtBeginDocument,或 begindocument/after 钩子)判断是否有此标记,然后根据需要重定义命令即可。

实际上,正是由于 elegantbook 加载了 bm 宏包才导致这个错误。bm 设置 \let\boldsymbol\bm,但为什么直接嵌套 \bm 不会出错呢?因为最外层的那个 \bm 设置 \let\bm\@firstofone,所以里面的 \bm 不会生效。但并没有 \let\boldsymbol\@firstofone,嵌套 \boldsymbol 就会出错。

\boldsymbol 直接把字体替换为加粗的,如果加粗的字体没有这个字形,就保持不变。而 \bm 会尝试替换字体,如果没有会使用 “poor man's bold”,即伪粗体。

问题2,没有一劳永逸的解决方案,具体问题具体分析。

像下面那个例子一样,加上 \clap

text 键设置的内容应当是宽度为 0pt 的。\clap 等于 \makebox[0pt][c]

怎么没解决呢?

\renewcommand\mainmatter{\if@openright\cleardoublepage\else\clearpage\fi % <-
  \@mainmattertrue
  \pagenumbering{arabic}}

报错信息要看完整。

(./skyrmion-part1-module.code.tex
! Undefined control sequence.
l.1 \skyrmion
             _provide_module:n {part1}
? 

说明是在 skyrmion-part1-module.code.tex 这个文件的第一行报错,明显是由于没有处于 LaTeX3 环境而出错。因为是在导言区导入的模块,并不是 LaTeX3 环境。把名字替换成 \SkyrmionProvideModule 就好了。

另外,只是简单的用 \file_if_exist_input:n..,如果重复加载一个模块,就会出现重复定义的问题,还可能会像加载 tikz 库一样,遇到类别码的问题,建议使用 \@onefilewithoptions 加载文件,不仅可以解决这两个问题,还可以像加载宏包时有宏包选项一样,有自己的模块选项。

%% skyrmion.cls, 另外两个模块文件的第一行也要修改
\def\skyrmion@date{2024/10/05}
\def\skyrmion@version{0.1.1}

\ExplSyntaxOn
\cs_new_protected_nopar:Npn \SkyrmionProvideModule #1
  {
    \ProvidesExplFile{skyrmion-#1-module.code.tex}{\skyrmion@date}{\skyrmion@version}
    {skyrmion~ \text_titlecase:n { #1 } ~Module}
  }
\ExplSyntaxOff

\ProvidesExplClass{skyrmion} {\skyrmion@date} {\skyrmion@version}
{Skyrmion in Hong Kong}

\cs_new_protected:Npn \skyrmion_msg_new:nn #1#2 
  { \msg_new:nnn { skyrmion } { #1 } { #2 } }
\cs_new_protected:Npn \skyrmion_msg_error:nn #1#2
  { \msg_error:nnn { skyrmion } { #1 } { #2 } }
\cs_generate_variant:Nn \skyrmion_msg_error:nn { nx }
\skyrmion_msg_new:nn { not found module }
  {
    The~skyrmion~module~`#1'~not~found.
  }
\tl_const:Nn \c__skyrmion_module_ext_tl { tex }
\cs_new_protected_nopar:Npn \skyrmion_load_module:n #1 
  {
    \clist_map_inline:nn { #1 }
    {
      \file_if_exist:nTF { skyrmion-##1-module.code. \c__skyrmion_module_ext_tl }
      { \@onefilewithoptions { skyrmion-##1-module.code } [{}] [0000-00-00] { \c__skyrmion_module_ext_tl } } % 第一个方括号是模块选项,第二个是日期
      {
        \skyrmion_msg_error:nn { not found module } { ##1 }
      }
    }
  }

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions\relax
\LoadClass[a4paper]{article}

\keys_define:nn {skyrmion/foo}
{
  tel.tl_set:N = \l__skyrmion_tel_tl
}

\NewDocumentCommand{\foo}{m}
{
  \keys_set:nn {skyrmion/foo} {#1}
  \int_compare:nNnT { \tl_count:N \l__skyrmion_tel_tl } = {8}
   {
     \skyrmion_load_module:n {part1}
   }
  \int_compare:nNnT { \tl_count:N \l__skyrmion_tel_tl } = {10}
   {
     \skyrmion_load_module:n {part2}
   }
}
% \skyrmion_load_module:n {part1} % ?

\endinput

没想到简单的方法,只能重写整个 output routine,很麻烦,而且很多宏包都不容易兼容。

发布
问题