summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/joinbox/joinbox.sty
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/joinbox/joinbox.sty')
-rw-r--r--macros/latex/contrib/joinbox/joinbox.sty418
1 files changed, 418 insertions, 0 deletions
diff --git a/macros/latex/contrib/joinbox/joinbox.sty b/macros/latex/contrib/joinbox/joinbox.sty
new file mode 100644
index 0000000000..8cf2cfcb54
--- /dev/null
+++ b/macros/latex/contrib/joinbox/joinbox.sty
@@ -0,0 +1,418 @@
+%%% ----------------------------------------------------------------------------
+%%% joinbox: Join figures to same height or width with LaTeX3
+%%% Author : Nan Geng <nangeng@nwafu.edu.cn>
+%%% Repository: https://gitee.com/nwafu_nan/joinfigs
+%%% License : The LaTeX Project Public License 1.3c
+%%% ----------------------------------------------------------------------------
+
+\NeedsTeXFormat{LaTeX2e}
+\RequirePackage{expl3}
+\ProvidesExplPackage{joinbox}{2023-08-11}{v1.0.0}
+ {Join figures to same height or width with LaTeX3}
+
+\RequirePackage{xparse}
+\RequirePackage{graphicx}
+
+%% \tl_if_eq:NnTF 与texlive 2020的兼容性设置
+\cs_if_exist:NF \tl_if_eq:NnTF
+ {
+ \tl_new:N \l__tblr_backport_b_tl
+ \prg_new_protected_conditional:Npnn \tl_if_eq:Nn #1 #2 { T, F, TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l__tblr_backport_b_tl {#2}
+ \exp_after:wN
+ \group_end:
+ \if_meaning:w #1 \l__tblr_backport_b_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \prg_generate_conditional_variant:Nnn \tl_if_eq:Nn { c } { TF, T, F }
+ }
+
+\cs_if_exist:NF \seq_map_indexed_function:NN
+ {
+ \cs_set_eq:NN \seq_map_indexed_function:NN \seq_indexed_map_function:NN
+ }
+
+% 定义变量
+\bool_new:N \l__joinbox_vertical_bool
+\bool_new:N \l__joinbox_out_scale_bool
+\int_new:N \l__joinbox_baseline_int
+\clist_const:Nn \c__joinbox_baseline_clist { t, vc, H, b }
+\clist_new:N \l__joinbox_name_clist
+\coffin_new:N \l__joinbox_out_coffin
+\coffin_new:N \l__joinbox_tmpa_coffin
+\coffin_new:N \l__joinbox_tmpb_coffin
+
+\dim_new:N \l__joinbox_out_length_dim
+\dim_new:N \l__joinbox_sep_dim
+\dim_new:N \l__joinbox_min_width_dim
+\dim_new:N \l__joinbox_min_height_dim
+
+%% 选项设计
+\keys_define:nn { joinbox }
+ {
+ % 输出结果基线位置
+ baseline .choice:,
+ baseline .value_required:n = true,
+ baseline .choices:nn = { t, vc, H, b }
+ {
+ \int_set_eq:NN \l__joinbox_baseline_int \l_keys_choice_int
+ },
+ baseline .default:n = vc,
+ baseline .initial:n = b,
+
+ % 输出尺寸(垂直拼接:宽度,水平拼接:高度)
+ outlen .code:n = { \dim_compare:nNnTF { #1 } < { 0pt }
+ {
+ \bool_set_false:N \l__joinbox_out_scale_bool
+ }{
+ \dim_compare:nNnTF { #1 } = { 0pt }
+ {
+ \bool_set_false:N \l__joinbox_out_scale_bool
+ }{
+ \bool_set_true:N \l__joinbox_out_scale_bool
+ \dim_set:Nn \l__joinbox_out_length_dim { #1 }
+ }
+ }
+ },
+ outlen .default:n = 0pt,
+ outlen .initial:n = 0pt,
+
+ % 拼音间距
+ sep .dim_set:N = \l__joinbox_sep_dim,
+ sep .default:n = 0pt,
+ sep .initial:n = 0pt,
+
+ unknown .code:n = { \__joinbox_error:n { unknown-option } }
+ % unknown .code:n = \__joinbox_unknown_key:V \l_keys_key_str,
+ }
+
+% \cs_new_protected:Npn \__joinbox_unknown_key:n #1
+% {
+% \clist_if_in:NnTF \c__joinbox_baseline_clist { #1 }
+% {
+% \int_zero:N \l_tmpa_int
+% \clist_map_inline:Nn \c__joinbox_baseline_clist
+% {
+% \int_incr:N \l_tmpa_int
+% \str_if_eq:nnT { ##1 } { #1 }
+% { \clist_map_break: }
+% }
+% \int_gset_eq:NN \l__joinbox_baseline_int \l_tmpa_int
+% }
+% {
+% \dim_compare:nNnTF { #1 } < { 0pt }
+% {
+% \bool_gset_false:N \l__joinbox_out_scale_bool
+% }{
+% \dim_compare:nNnTF { #1 } = { 0pt }
+% {
+% \bool_gset_false:N \l__joinbox_out_scale_bool
+% }{
+% \bool_gset_true:N \l__joinbox_out_scale_bool
+% \dim_gset:Nn \l__joinbox_out_length_dim { #1 }
+% }
+% }
+% }
+% }
+% \cs_generate_variant:Nn \__joinbox_unknown_key:n { V }
+
+\msg_new:nnn { joinbox } { unknown-option }
+ { package~ option~ "\l_keys_key_tl"~ is~ unknown. }
+
+%% 参数设置用户接口
+\NewDocumentCommand \joinset { m }
+ { \keys_set:nn { joinset } {#1} }
+
+% 计算box盒子的总高度
+% #1---盒子变量
+\cs_if_free:NT \box_ht_plus_dp:N
+ {
+ \cs_new_protected:Npn \box_ht_plus_dp:N #1
+ { \tex_dimexpr:D \box_ht:N #1 + \box_dp:N #1 \scan_stop: }
+ }
+
+% 计算coffin盒子的总高度
+% #1---盒子变量
+\cs_new_nopar:Npn \__joinbox_coffin_ht_plus_dp:N #1
+ {
+ \coffin_ht:N #1 + \coffin_dp:N #1
+ }
+
+% 计算两个盒子的最小宽度和最小高度
+% (最小宽度和最小宽度不一定属于同一个盒子)
+\cs_new:Npn \__joinbox_calc_min_size:nn #1#2
+ {
+ % 取得第1个盒子的宽度和高度
+ \hbox_set:Nn \l_tmpa_box
+ {
+ #1
+ }
+
+ \dim_set:Nn \l_tmpa_dim
+ {
+ \box_wd:N \l_tmpa_box
+ }
+ \dim_gset_eq:NN \l__joinbox_min_width_dim \l_tmpa_dim
+
+ \dim_set:Nn \l_tmpb_dim
+ {
+ \box_ht_plus_dp:N \l_tmpa_box
+ }
+ \dim_gset_eq:NN \l__joinbox_min_height_dim \l_tmpb_dim
+
+ % 取得第2个盒子的宽度和高度,并与第1个盒子比较
+ \hbox_set:Nn \l_tmpa_box
+ {
+ #2
+ }
+
+ \dim_set:Nn \l_tmpa_dim
+ {
+ \box_wd:N \l_tmpa_box
+ }
+ % 比较并记录最小宽度
+ \dim_compare:nNnT \l__joinbox_min_width_dim > \l_tmpa_dim
+ {
+ \dim_gset_eq:NN \l__joinbox_min_width_dim \l_tmpa_dim
+ }
+
+ \dim_set:Nn \l_tmpb_dim
+ {
+ \box_ht_plus_dp:N \l_tmpa_box
+ }
+
+ % 比较并记录最小总高度(高度+深度)
+ \dim_compare:nNnT \l__joinbox_min_height_dim > \l_tmpb_dim
+ {
+ \dim_gset_eq:NN \l__joinbox_min_height_dim \l_tmpb_dim
+ }
+ }
+
+% 输出盒子
+\cs_new:Npn \__joinbox_typeout_coffin:N #1
+ {
+ % 输出拼接后的盒子
+ \int_case:nn { \l__joinbox_baseline_int }
+ {
+ { 1 }{%
+ \coffin_typeset:Nnnnn #1
+ { l } { t } { 0pt } { 0pt }
+ }
+ { 2 }{%
+ \coffin_typeset:Nnnnn #1
+ { l } { vc } { 0pt } { 0pt }
+ }
+ { 3 }{%
+ \coffin_typeset:Nnnnn #1
+ { l } { H } { 0pt } { 0pt }
+ }
+ { 4 }{%
+ \coffin_typeset:Nnnnn #1
+ { l } { b } { 0pt } { 0pt }
+ }
+ }
+ }
+
+% 盒子拼接内部函数
+% 将指定文件名列表中的图像拼接成一个盒子并将基线调整为中心线输出
+% #1---第1个盒子的内容
+% #2---第2个盒子的内容
+\cs_new:Npn \__joinbox_handle:nn #1#2
+ {
+ \group_begin:
+
+ \coffin_clear:N \l__joinbox_out_coffin
+ % 设置第1个盒子
+ \hcoffin_set:Nn \l__joinbox_out_coffin
+ {
+ #1
+ }
+
+ \bool_if:NTF \l__joinbox_vertical_bool
+ {
+ % 按最小宽度缩放第1个盒子
+ \coffin_scale:Nnn \l__joinbox_out_coffin
+ {
+ \dim_ratio:nn { \l__joinbox_min_width_dim }{ \coffin_wd:N \l__joinbox_out_coffin }
+ }
+ {
+ \dim_ratio:nn { \l__joinbox_min_width_dim }{ \coffin_wd:N \l__joinbox_out_coffin }
+ }
+
+ % 处理第2个盒子
+ \hcoffin_set:Nn \l__joinbox_tmpa_coffin
+ {
+ #2
+ }
+
+ % 按最小宽度缩放第2个盒子
+ \coffin_scale:Nnn \l__joinbox_tmpa_coffin
+ {
+ \dim_ratio:nn { \l__joinbox_min_width_dim }{ \coffin_wd:N \l__joinbox_tmpa_coffin }
+ }
+ {
+ \dim_ratio:nn { \l__joinbox_min_width_dim }{ \coffin_wd:N \l__joinbox_tmpa_coffin }
+ }
+
+ % \dim_show:N \l__joinbox_sep_dim
+ % 将第2个盒子拼接到第一个盒子
+ \coffin_join:NnnNnnnn \l__joinbox_out_coffin
+ { hc } { b } \l__joinbox_tmpa_coffin { hc } { t } { 0pt } { -\l__joinbox_sep_dim }
+
+ % 按指定输出宽度缩放输出盒子
+ \bool_if:NT \l__joinbox_out_scale_bool
+ {
+ \coffin_scale:Nnn \l__joinbox_out_coffin
+ {
+ \dim_ratio:nn { \l__joinbox_out_length_dim }{ \l__joinbox_min_width_dim }
+ }
+ {
+ \dim_ratio:nn { \l__joinbox_out_length_dim }{ \l__joinbox_min_width_dim }
+ }
+ }
+
+ % 输出拼接后的盒子
+ \__joinbox_typeout_coffin:N \l__joinbox_out_coffin
+ }{
+ % 按最小高度缩放第1个盒子
+ \coffin_scale:Nnn \l__joinbox_out_coffin
+ {
+ \dim_ratio:nn { \l__joinbox_min_height_dim }{ \__joinbox_coffin_ht_plus_dp:N \l__joinbox_out_coffin }
+ }
+ {
+ \dim_ratio:nn { \l__joinbox_min_height_dim }{ \__joinbox_coffin_ht_plus_dp:N \l__joinbox_out_coffin }
+ }
+
+ % 处理第2个盒子
+ \hcoffin_set:Nn \l__joinbox_tmpa_coffin
+ {
+ #2
+ }
+
+ % 按最小高度缩放第2个盒子
+ \coffin_scale:Nnn \l__joinbox_tmpa_coffin
+ {
+ \dim_ratio:nn { \l__joinbox_min_height_dim }{ \__joinbox_coffin_ht_plus_dp:N \l__joinbox_tmpa_coffin }
+ }
+ {
+ \dim_ratio:nn { \l__joinbox_min_height_dim }{ \__joinbox_coffin_ht_plus_dp:N \l__joinbox_tmpa_coffin }
+ }
+
+ % 将第2个盒子拼接到第1个盒子
+ \coffin_join:NnnNnnnn \l__joinbox_out_coffin
+ { vc } { r } \l__joinbox_tmpa_coffin { vc } { l } { \l__joinbox_sep_dim } { 0pt }
+
+ % 按指定输出高度缩放输出盒子
+ \bool_if:NT \l__joinbox_out_scale_bool
+ {
+ \coffin_scale:Nnn \l__joinbox_out_coffin
+ {
+ \dim_ratio:nn { \l__joinbox_out_length_dim }{ \l__joinbox_min_height_dim }
+ }
+ {
+ \dim_ratio:nn { \l__joinbox_out_length_dim }{ \l__joinbox_min_height_dim }
+ }
+ }
+
+ % 输出拼接后的盒子
+ \__joinbox_typeout_coffin:N \l__joinbox_out_coffin
+ }
+ \group_end:
+ }
+
+\cs_new:Npn \__joinbox_figs:
+ {
+ % 设置第1个图像
+ \clist_pop:NN \l__joinbox_name_clist \l_tmpa_tl
+ \coffin_clear:N \l__joinbox_tmpa_coffin
+ \hcoffin_set:Nn \l__joinbox_tmpa_coffin
+ {
+ \includegraphics{ \l_tmpa_tl }
+ }
+
+ % 循环处理其它图像
+ \clist_map_inline:Nn \l__joinbox_name_clist
+ {
+ \coffin_clear:N \l__joinbox_tmpb_coffin
+ \hcoffin_set:Nn \l__joinbox_tmpb_coffin
+ {
+ \includegraphics{ ##1 }
+ }
+
+ \__joinbox_calc_min_size:nn
+ {
+ \__joinbox_typeout_coffin:N \l__joinbox_tmpa_coffin
+ }
+ {
+ \__joinbox_typeout_coffin:N \l__joinbox_tmpb_coffin
+ }
+
+ \hcoffin_set:Nn \l__joinbox_tmpa_coffin
+ {
+ \__joinbox_handle:nn
+ {
+ \__joinbox_typeout_coffin:N \l__joinbox_tmpa_coffin
+ }
+ {
+ \__joinbox_typeout_coffin:N \l__joinbox_tmpb_coffin
+ }
+ }
+ }
+ \__joinbox_typeout_coffin:N \l__joinbox_tmpa_coffin
+ }
+
+% 盒子拼接用户接口
+% 将两个盒子按指定方式拼接成一个盒子并将基线调整为中心线后输出
+% #1---是否为*命令,如有*则采用水平拼接,无*则采用垂直拼接
+% #2---可选参数,用key-value选项指定拼接参数
+% #3---第1个盒子的内容
+% #4---第2个盒子的内容
+\NewDocumentCommand{\joinbox}{ s O{} m m}
+ {
+ \IfBooleanTF{#1}
+ {
+ \bool_set_false:N \l__joinbox_vertical_bool
+ }{
+ \bool_set_true:N \l__joinbox_vertical_bool
+ }
+
+ \group_begin:
+ % 设置拼接参数
+ \keys_set:nn { joinbox } { #2 }
+ % 计算最小宽度和高度
+ \__joinbox_calc_min_size:nn { #3 } { #4 }
+ % 拼接输出
+ \__joinbox_handle:nn { #3 }{ #4 }
+ \group_end:
+ }
+
+% 图像拼接用户接口
+% 将指定文件名列表中的图像拼接成一个盒子并将基线调整为中心线输出
+% #1---是否为*命令,如有*则采用水平拼接,无*则采用垂直拼接
+% #2---可选参数,用key-value选项指定拼接参数
+% #3---必选参数,用逗号分隔的,需要拼接的图像文件名称(可以带有路径)
+\NewDocumentCommand{\joinfigs}{ s O{} m}
+ {
+ \IfBooleanTF{#1}
+ {
+ \bool_set_false:N \l__joinbox_vertical_bool
+ }{
+ \bool_set_true:N \l__joinbox_vertical_bool
+ }
+
+ \group_begin:
+ % 设置拼接参数
+ \keys_set:nn { joinbox } { #2 }
+ % 设置文件名列表
+ \clist_set:Nn \l__joinbox_name_clist { #3 }
+ % 拼接图像
+ \__joinbox_figs:
+ \group_end:
+ }
+
+\endinput