xkwxdyy/xchoices

基线对齐问题

Closed this issue · 24 comments

我是在 411630b 上测试的:

\documentclass{article}
\usepackage{xchoices}
\begin{document}

\begin{xchoices}[label-style = Roman]
  \item Lorem ipsum.
  \item norem opsum.
\end{xchoices}

\begin{xchoices}[label-style = Roman, label-pos = left-top]
  \item Lorem ipsum.
  \item norem opsum.
\end{xchoices}

\end{document}
  1. 标签的默认位置是 label-pos = left(或者 anchor = west)。但是这会导致标签和选项内容的基线没有对齐,像“norem opsum” 没有升部就会特别明显。而且不同选项的基线也没对齐。

Screen Shot 2022-01-31 at 18 32 33

  1. 如果设置 label-pos = left-top,同样基线没有对齐。

Screen Shot 2022-01-31 at 18 33 30

感觉 coffin 不太适合处理这个问题。它直接将 coffin 的 pole 对齐,还没有考虑内部的文字位置,这样处理太机械了。可能 2e 的 \parboxminipage 更合适。

感觉 coffin 不太适合处理这个问题。它直接将 coffin 的 pole 对齐,还没有考虑内部的文字位置,这样处理太机械了。可能 2e 的 \parbox 和 minipage 更合适。

coffin已经是我想到的最优解了

  • anchor的不同设置就是基于coffin的拼接才实现,我知道的其他方式很难做到这么精细的对齐和调整
  • 在储存到box的时候,我使用了varwidth环境
\hbox_set:cw { l__xchoices_env_ \int_to_roman:n { \l__xchoices_env_xitem_int } _box }
      \begin{varwidth}{\hsize}
...
      \end{varwidth}
\hbox_set_end:

varwidth也是基于minipage的,我刚试着去掉了,换成了minipage但好像也还是一样的效果

  • anchor的不同设置就是基于coffin的拼接才实现,我知道的其他方式很难做到这么精细的对齐和调整

\parbox 的一个 optional argument 可以设置与外部(标签)的对齐方式,具体参考 lshort-zh-cn。

但是parbox的参数输入决定了在此处用不了,否则实现不了item的效果

  • anchor的不同设置就是基于coffin的拼接才实现,我知道的其他方式很难做到这么精细的对齐和调整

\parbox 的一个 optional argument 可以设置与外部(标签)的对齐方式,具体参考 lshort-zh-cn。

varwidth其实也可以设置外部对齐方式,和minipage, parbox一样,因为好像是基于minipage写的

如果能根据内容获得需要升降的内容就好了,因为可以在把box传到seq之前进行升降

我试试能不能只依赖 expl3 + xparse + \parboxminipage 实现。 毕竟 varwidth 不是核心宏包,应尽量避免依赖它。

我用varwidth是用\hsize来获得它的“最小宽度”,如果用minipage的话无法有这个效果,我不知道如何解决这个问题

经测试原来的实现方式可以不需要varwidth环境,但是对齐方式仍没有解决

我参考了 https://github.com/cgnieder/tasks/blob/303ff1dcd1eec81437e4ccbef5eb26e9b070f3d4/tasks.sty#L310-L365 的处理方法,可以用 vcoffin 替代 varwidth,然后在外面嵌套一层 hcoffin。其中 vcoffin 可以使用 TB 分别按照首行和末行的基线对齐,hcoffinT/BH 是一样的,都是按照基线对齐。另外可以用 \strut 将末行撑高一些,防止整体跟下文的段落过于接近。

\documentclass{article}

\usepackage{expl3}
\usepackage{xcoffins}
\usepackage{lipsum}

\begin{document}

\ExplSyntaxOn
\coffin_new:N \l__a_coffin
\coffin_new:N \l__b_coffin
\coffin_new:N \l__tmp_coffin

\hcoffin_set:Nn \l__a_coffin
  {
    \vcoffin_set:Nnn \l__tmp_coffin { 6em }
      {
        \noindent \strut
        norem~ opsum \newline
        Lorem~ ipsum \strut
      }
    \coffin_mark_handle:Nnnn \l__tmp_coffin {r} {H} {red}
    \coffin_mark_handle:Nnnn \l__tmp_coffin {r} {T} {green}
    \coffin_mark_handle:Nnnn \l__tmp_coffin {r} {B} {blue}
    \coffin_typeset:Nnnnn \l__tmp_coffin {l} {T} {0pt} {0pt}
  }
\hcoffin_set:Nn \l__b_coffin
  {
    \vcoffin_set:Nnn \l__tmp_coffin { 6em }
      {
        \noindent \strut
        Lorem~ ipsum \newline
        Lorem~ ipsum \strut
      }
    \coffin_typeset:Nnnnn \l__tmp_coffin {l} {T} {0pt} {0pt}
  }

\coffin_join:NnnNnnnn \l__a_coffin {r} {H} \l__b_coffin {l} {H} {0pt} {0pt}

\lipsum[1]\par
Lipsum~ \coffin_typeset:Nnnnn \l__a_coffin {l} {H} {0pt} {0pt} \par
\lipsum[1]\par

\ExplSyntaxOff

\end{document}

我用varwidth是用\hsize来获得它的“最小宽度”,如果用minipage的话无法有这个效果,我不知道如何解决这个问题

也可以先将每个选项的内容装进 hbox 中获得自然宽度,然后根据版心和列数要求算出选项最终的宽度,以这个宽度作为 vcoffin 的固定宽度,跟 \parboxminipage 类似,而且更方便对齐。

我用varwidth是用\hsize来获得它的“最小宽度”,如果用minipage的话无法有这个效果,我不知道如何解决这个问题

也可以先将每个选项的内容装进 hbox 中获得自然宽度,然后根据版心和列数要求算出选项最终的宽度,以这个宽度作为 vcoffin 的固定宽度,跟 \parboxminipage 类似,而且更方便对齐。

这样的话每个选项的宽度是一样的了?

可我的\item 只能把内容存到一个里面,要么是box要么是coffin,这怎么处理呢

这样的话每个选项的宽度是一样的了?

对。

可我的\item 只能把内容存到一个里面,要么是box要么是coffin,这怎么处理呢

\seq_set_split:Nnn 后每个选项的内容是以 tl 的形式存在 seq 中的,可以分别存到 boxcoffin

但我内容都没有怎么获得列数呢? 原本的做法是先获得内容再去计算宽度判断

我一直没弄懂h-*v-*比如box, coffin的区别

但我内容都没有怎么获得列数呢? 原本的做法是先获得内容再去计算宽度判断

先从 seq 读取内容放进 hbox 计算宽度和列数,再从 seq 读取一遍内容放进 coffin 用于输出。

我一直没弄懂h-*v-*比如box, coffin的区别

其实我也没有完全搞懂底层 h 和 v 的区别,需要阅读 texbook。

6f1b6b0
尝试用vcoffin处理,但是失败了

\cs_new:cpn { __xchoices_label_pos_set_left:NN } #1#2
  {
    \coffin_join:NnnNnnnn
      #1 { H } { r }
      #2 { H } { l }
      { 5pt + \l__xchoices_coffin_join_horizontal_sep_dim }
      { \l__xchoices_coffin_join_vertical_sep_dim }
...

leftjoin的垂直基线改掉就可以了,但是带来的结果就是图片会变成left-bottom,这个如何处理
image

调用了graphbox宏包的align选项解决,不过需要用户手动添加

\begin{xchoices}
  \item testtest
  \item* \includegraphics[width = 2cm, align = c]{example-image-a}
  \item testtesttesttesttest
  \item* testtesttesttesttesttestte
  \item testtesttesttesttesttesttesttes
\end{xchoices}

image

调用了graphbox宏包的align选项解决,不过需要用户手动添加

我感觉可以在存入 hbox 后判断高度,如果超过 2\baselineskip,在 vcoffin 这一步就用 \coffin_set_vertical_pole 较正一下 T 的位置。

经测试用\baselineskip可以检测最小的图片是1cm高(在不设置行距等情况下)

\documentclass{ctexart}
\usepackage{xchoices}
\usepackage{graphbox}

% 统一控制答案的显示
\xchoicesetup{
  showanswer
}
\begin{document}
  \begin{enumerate}
    \item 
      2020 年 3 月 14 日是全球首个国际圆周率日 ( $\pi$ Day). 历史上, 求圆周率 $\pi$ 的方法有多种, 与**传统数学中的 “割圆术”相似. 数学家阿尔. 卡西的方 法是: 当正整数 $n$ 充分大时, 计算单位圆的内接正 $6 n$ 边形的周长和外切 正 $6 n$ 边形 (各边均与圆相切的正 $6 n$ 边形) 的周长, 将它们的算术平均数 作为 $2 \pi$ 的近似值. 按照阿尔. 卡西的方法, $\pi$ 的近似值的表达式是 \xparen
        \begin{xchoices}
          \item testtest
          \item* \includegraphics[height = 10mm]{example-image-a}
          \item testtesttesttesttest
          \item* testtesttesttesttesttestte
          \item testtesttesttesttesttesttesttes
        \end{xchoices}
    \item
      2020 年 3 月 14 日是全球首个国际圆周率日 ( $\pi$ Day). 历史上, 求圆周率 $\pi$ 的方法有多种, 与**传统数学中的 “割圆术”相似. 数学家阿尔. 卡西的方 法是: 当正整数 $n$ 充分大时, 计算单位圆的内接正 $6 n$ 边形的周长和外切 正 $6 n$ 边形 (各边均与圆相切的正 $6 n$ 边形) 的周长, 将它们的算术平均数 作为 $2 \pi$ 的近似值. 按照阿尔. 卡西的方法, $\pi$ 的近似值的表达式是 \xparen
        \begin{xchoices}[showanswer = false]
          \item 选项1
          \item* 选项2
          \item 选项3
          \item* 选项4
        \end{xchoices}
  \end{enumerate}

  \begin{xchoices}[label-style = alph]
    \item \textbf{ \huge norem opsum}
    \item Lipnorem opsum
  \end{xchoices}
\end{document}

image
而且把字放大后,字依旧是基线对齐,基本解决问题

b47b66f
感谢李老师,我自己测试情况是问题解决了