summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/circledtext/circledtext.sty
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/circledtext/circledtext.sty')
-rw-r--r--macros/latex/contrib/circledtext/circledtext.sty952
1 files changed, 952 insertions, 0 deletions
diff --git a/macros/latex/contrib/circledtext/circledtext.sty b/macros/latex/contrib/circledtext/circledtext.sty
new file mode 100644
index 0000000000..d8f125043f
--- /dev/null
+++ b/macros/latex/contrib/circledtext/circledtext.sty
@@ -0,0 +1,952 @@
+%% Copyright (C) 2020-2022 by Nan Geng <nangeng@nwafu.edu.cn>
+%% --------------------------------------------------------------------------
+%%
+%% This work may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. This version of this license is in
+%% http://www.latex-project.org/lppl/lppl-1-3c.txt
+%% and the latest version of this license is in
+%% http://www.latex-project.org/lppl.txt
+%% and version 1.3 or later is part of all distributions of
+%% LaTeX version 2005/12/01 or later.
+%%
+%% This work has the LPPL maintenance status "maintained".
+%%
+%% The Current Maintainer of this work is Nan Geng.
+%%
+%% --------------------------------------------------------------------------
+%%
+\NeedsTeXFormat{LaTeX2e}[2020/10/01]
+\RequirePackage{expl3}
+\ProvidesExplPackage{circledtext}{2022-04-24}{v1.0.0}
+ {Typeset circled text with l3draw}
+
+\RequirePackage { xtemplate, l3keys2e, l3draw, xparse }
+
+% 保证TeXLive的向下兼容
+\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: }
+ }
+
+% 带圈文字用户接口
+% #1 *号命令,实现阴文
+% #2 格式选项
+% #3 文字
+\NewDocumentCommand{\circledtext}{ s O{} m }
+ {
+ \group_begin:
+ \IfBooleanTF{ #1 }
+ {
+ \bool_set_true:N \l__circledtext_negative_bool
+ }{
+ \bool_set_false:N \l__circledtext_negative_bool
+ }
+ \__circledtext_handle:nn { #2 } { #3 }
+ \group_end:
+ }
+
+% 是否反白标志
+\bool_new:N \l__circledtext_negative_bool
+
+% 基字符盒子
+\box_new:N \l__circledtext_basebox_box
+
+% 字符盒子类型
+\tl_new:N \l__circledtext_char_box_type_tl
+% 字符盒子类型列表
+\clist_new:N \g__circledtext_char_box_list_clist
+
+% 缩放方式
+\tl_new:N \l__circledtext_resize_method_tl
+% 缩放方式列表
+\clist_new:N \g__circledtext_resize_method_clist
+
+% 基字符盒子正方形连长
+\dim_new:N \l__circledtext_char_box_size_dim
+% 基字符盒子正方形外接圆半径
+\dim_new:N \l__circledtext_char_box_radius_dim
+% 基字符宽度
+\dim_new:N \l__circledtext_char_width_dim
+% 基字符高度
+\dim_new:N \l__circledtext_char_height_dim
+% 字符(串)盒子宽度
+\dim_new:N \l__circledtext_box_width_dim
+% 字符(串)盒子高度
+\dim_new:N \l__circledtext_box_height_dim
+% 字符(串)外框线宽
+\dim_new:N \l__circledtext_box_linewidth_dim
+% 字符(串)内部十字、米字装饰线线宽
+\dim_new:N \l__circledtext_cross_linewidth_dim
+% 字符(串)深度(带格式)
+\dim_new:N \l__circledtext_char_dp_dim
+
+% 字符(串)盒子容器
+\coffin_new:N \l__circledtext_str_box_coffin
+% 字符框盒子容器
+\coffin_new:N \l__circledtext_box_coffin
+% 字符盒子容器
+\coffin_new:N \l__circledtext_char_coffin
+% 临时盒子容器
+\coffin_new:N \l__circledtext_tmpa_coffin
+% 临时盒子容器
+\coffin_new:N \l__circledtext_tmpb_coffin
+
+% 基字符宽度
+\dim_new:N \charboxwd
+% 基字符高度
+\dim_new:N \charboxht
+
+% 待处理字符(串)
+\tl_new:N \l__circledtext_chars_tl
+% 字符(串)排版格式
+\tl_new:N \l__circledtext_character_format_tl
+% 内部十字、米字装饰线颜色点边框颜色比例
+\int_new:N \l__circledtext_cross_color_ratio_int
+\int_set:Nn \l__circledtext_cross_color_ratio_int { 30 }
+% 字符(串)的字形类型(实线、虚线等)
+\int_new:N \l__circledtext_charstroke_type_int
+
+% 填充色辅助函数
+\cs_new_nopar:Nn \__circledtext_aux_color_boxfill:
+ { }
+
+% 颜色命名函数
+% #1 颜色名称
+% #2 颜色表达式
+\cs_set_nopar:Npn \__circledtext_color_select:nn #1#2
+ {
+ \color_set:nn {#1} {#2}
+ }
+\cs_generate_variant:Nn \__circledtext_color_select:nn {nx}
+
+% 颜色命名函数
+% #1 颜色名称
+% #2 颜色空间
+% #3 颜色分量值
+\cs_set_nopar:Npn \__circledtext_color_select:nnn #1#2#3
+ {
+ \color_set:nnn {#1} {#2} {#3}
+ }
+\cs_generate_variant:Nn \__circledtext_color_select:nnn {nnx}
+
+% 计算基字符的宽和高
+\cs_new:Npn \__circledtext_calc_basechar_w_h:
+ {
+ \dim_set:Nn \l__circledtext_char_width_dim
+ {
+ \box_wd:N \l__circledtext_basebox_box
+ }
+ \dim_set:Nn \l__circledtext_char_height_dim
+ {
+ \box_ht_plus_dp:N \l__circledtext_basebox_box
+ }
+ }
+
+% 盒子容器总高度计算函数
+\cs_new_nopar:Npn \__circledtext_coffin_ht_plus_dp:N #1
+ {
+ \coffin_ht:N #1 + \coffin_dp:N #1
+ }
+
+% 计算基字符外框大小(外接正方形边长和外接圆半径)
+\cs_new:Npn \__circledtext_calc_char_box_size:
+ {
+ % 设置基字符格式的基字符盒子
+ \hbox_set:Nn \l_tmpa_box
+ {
+ \tl_use:N \l__circledtext_character_format_tl
+ \tl_use:N \c__circledtext_basechar_tl
+ }
+
+ % 盒子宽度
+ \dim_set:Nn \l_tmpa_dim
+ {
+ \box_wd:N \l_tmpa_box
+ }
+ % 盒子高度
+ \dim_set:Nn \l_tmpb_dim
+ {
+ \box_ht_plus_dp:N \l_tmpa_box
+ }
+
+ % 盒子深度
+ \dim_set:Nn \l__circledtext_char_dp_dim
+ {
+ \box_dp:N \l_tmpa_box
+ }
+
+ % 正方形边长
+ \dim_compare:nNnTF \l_tmpa_dim > \l_tmpb_dim
+ {
+ \dim_gset_eq:NN \l__circledtext_char_box_size_dim \l_tmpa_dim
+ }
+ {
+ \dim_gset_eq:NN \l__circledtext_char_box_size_dim \l_tmpb_dim
+ }
+
+ % 设置基字符盒子正方形宽和高(相等)
+ \dim_gset_eq:NN \charboxwd \l__circledtext_char_box_size_dim
+ \dim_gset_eq:NN \charboxht \l__circledtext_char_box_size_dim
+
+ % 外接圆半径
+ \dim_gset:Nn \l__circledtext_char_box_radius_dim
+ {
+ \fp_to_dim:n
+ {
+ \fp_eval:n { \l__circledtext_char_box_size_dim * sqrt(2)/ 2 }
+ }
+ }
+ }
+
+% 字符盒子构造类型函数名称生成函数
+% 名称中6个参数分别表示:
+% #1 左下角x坐标
+% #2 左下角y坐标
+% #3 右上角x坐标
+% #4 右上角y坐标
+% #5 x方向缩放比例(扩展保留参数)
+% #6 y方向缩放比例(扩展保留参数)
+\cs_new_nopar:Npn \__circledtext_char_box_type:n #1
+ {
+ __circledtext_char_box_construct_type_ #1 :nnnnnn
+ }
+% 字符盒子构造类型函数名称命令生成函数
+% 名称命令中6个参数分别表示:
+% #1 左下角x坐标
+% #2 左下角y坐标
+% #3 右上角x坐标
+% #4 右上角y坐标
+% #5 x方向缩放比例(扩展保留参数)
+% #6 y方向缩放比例(扩展保留参数)
+\cs_new_nopar:Npn \__circledtext_char_box_type_c:n #1
+ {
+ \use:c
+ {
+ __circledtext_char_box_construct_type_ #1 :nnnnnn
+ }
+ }
+
+% 字符盒子构造类型函数生成器函数
+% #1 类型名称
+\cs_new:Npn \__circledtext_new_char_box_construct:nn #1
+ {
+ % 将类型名称记入clist
+ \clist_put_right:Nn \g__circledtext_char_box_list_clist {#1}
+ % 类似\cs_new:cn __circledtext_char_box_construct_type_none:nnnnnn
+ \cs_new:cn { \__circledtext_char_box_type:n {#1} }
+ }
+
+% 定义字符边框盒子类型
+
+% 无边框
+\__circledtext_new_char_box_construct:nn { none } { }
+
+% 实心正方形
+\__circledtext_new_char_box_construct:nn { filledsquare }
+ {
+ \cs_if_eq:NNF \__circledtext_aux_color_boxfill: \c_empty_tl
+ {
+ \__circledtext_aux_color_boxfill:
+ \draw_scope_begin:
+ \bool_if:NTF \l__circledtext_negative_bool
+ {
+ \color_fill:n { circledtextcharcolor }
+ }{
+ \color_fill:n { circledtextboxfill }
+ }
+ % \color_fill:n { circledtextboxfill }
+ \draw_path_rectangle:nn { #1, #2 } { #3, #4 }
+ \draw_path_use_clear:n { fill }
+ \draw_scope_end:
+ }
+ }
+
+% 外框正方形
+\__circledtext_new_char_box_construct:nn { boxsquare }
+ {
+ \draw_scope_begin:
+ \color_stroke:n { circledtextcharboxcolor }
+ \draw_path_rectangle:nn { #1, #2 } { #3, #4 }
+ \draw_path_use_clear:n { stroke }
+ \draw_scope_end:
+ }
+
+% 正方形对角十字线
+\__circledtext_new_char_box_construct:nn { dcrosssquare }
+ {
+ \draw_scope_begin:
+ \tl_if_empty:NF \l__circledtext_dash_pattern_tl
+ {
+ \exp_args:No \draw_dash_pattern:nn { \l__circledtext_dash_pattern_tl } { 0pt }
+ }
+ \draw_linewidth:n{ \l__circledtext_cross_linewidth_dim }
+ \color_stroke:n { circledtextcrosscolor }
+ \draw_path_moveto:n { #1, #2 }
+ \draw_path_lineto:n { #3, #4 }
+ \draw_path_moveto:n { #1, #4 }
+ \draw_path_lineto:n { #3, #2 }
+ \draw_path_use_clear:n { stroke }
+ \draw_scope_end:
+ }
+
+% 对角十字线
+\__circledtext_new_char_box_construct:nn { crosssquare }
+ {
+ \draw_scope_begin:
+ \tl_if_empty:NF \l__circledtext_dash_pattern_tl
+ {
+ \exp_args:No \draw_dash_pattern:nn { \l__circledtext_dash_pattern_tl } { 0pt }
+ }
+ \draw_linewidth:n{ \l__circledtext_cross_linewidth_dim }
+ \color_stroke:n { circledtextcrosscolor }
+ \draw_path_moveto:n { #3/2, #2 }
+ \draw_path_lineto:n { #3/2, #4 }
+ \draw_path_moveto:n { #1, #4/2 }
+ \draw_path_lineto:n { #3, #4/2 }
+ \draw_path_use_clear:n { stroke }
+ \draw_scope_end:
+ }
+
+% 实心圆
+\__circledtext_new_char_box_construct:nn { filledcircle }
+ {
+ \cs_if_eq:NNF \__circledtext_aux_color_boxfill: \c_empty_tl
+ {
+ \__circledtext_aux_color_boxfill:
+ \draw_scope_begin:
+ \bool_if:NTF \l__circledtext_negative_bool
+ {
+ \color_fill:n { circledtextcharcolor }
+ }{
+ \color_fill:n { circledtextboxfill }
+ }
+ % \color_fill:n { circledtextboxfill }
+ \draw_path_circle:nn
+ { \draw_point_interpolate_line:nnn { 0.5 } { #1, #2} { #3, #4} }
+ { \l__circledtext_char_box_radius_dim }
+ \draw_path_use_clear:n { fill }
+ \draw_scope_end:
+ }
+ }
+
+% 外框圆
+\__circledtext_new_char_box_construct:nn { boxcircle }
+ {
+ \draw_scope_begin:
+ \color_stroke:n { circledtextcharboxcolor }
+ \draw_path_circle:nn
+ { \draw_point_interpolate_line:nnn { 0.5 } { #1, #2} { #3, #4} }
+ { \l__circledtext_char_box_radius_dim }
+ \draw_path_use_clear:n { stroke }
+ \draw_scope_end:
+ }
+
+% 十字线
+\__circledtext_new_char_box_construct:nn { cross }
+ {
+ \draw_scope_begin:
+ \tl_if_empty:NF \l__circledtext_dash_pattern_tl
+ {
+ \exp_args:No \draw_dash_pattern:nn { \l__circledtext_dash_pattern_tl } { 0pt }
+ }
+ \draw_linewidth:n{ \l__circledtext_cross_linewidth_dim }
+ \color_stroke:n { circledtextcrosscolor }
+ \draw_path_moveto:n
+ {
+ \draw_point_intersect_line_circle:nnnnn
+ { #3/2, #2 } % line's first point
+ { #3/2, #4 } % line's second point
+ { \draw_point_interpolate_line:nnn { 0.5 } { #1, #2} { #3, #4} }
+ { \l__circledtext_char_box_radius_dim }
+ {1} % index of intersect
+ }
+ \draw_path_lineto:n
+ {
+ \draw_point_intersect_line_circle:nnnnn
+ { #3/2, #2 } % line's first point
+ { #3/2, #4 } % line's second point
+ { \draw_point_interpolate_line:nnn { 0.5 } { #1, #2} { #3, #4} }
+ { \l__circledtext_char_box_radius_dim }
+ {2} % index of intersect
+ }
+ \draw_path_moveto:n
+ {
+ \draw_point_intersect_line_circle:nnnnn
+ { #1, #4/2 } % line's first point
+ { #3, #4/2 } % line's second point
+ { \draw_point_interpolate_line:nnn { 0.5 } { #1, #2} { #3, #4} }
+ { \l__circledtext_char_box_radius_dim }
+ {1} % index of intersect
+ }
+ \draw_path_lineto:n
+ {
+ \draw_point_intersect_line_circle:nnnnn
+ { #1, #4/2 } % line's first point
+ { #3, #4/2 } % line's second point
+ { \draw_point_interpolate_line:nnn { 0.5 } { #1, #2} { #3, #4} }
+ { \l__circledtext_char_box_radius_dim }
+ {2} % index of intersect
+ }
+ \draw_path_use_clear:n { stroke }
+ \draw_scope_end:
+ }
+
+% 对角十字线
+\__circledtext_new_char_box_construct:nn { dcross }
+ {
+ \draw_scope_begin:
+ \tl_if_empty:NF \l__circledtext_dash_pattern_tl
+ {
+ \exp_args:No \draw_dash_pattern:nn { \l__circledtext_dash_pattern_tl } { 0pt }
+ }
+ \draw_linewidth:n{ \l__circledtext_cross_linewidth_dim }
+ \color_stroke:n { circledtextcrosscolor }
+ \draw_path_moveto:n { #1, #2 }
+ \draw_path_lineto:n { #3, #4 }
+ \draw_path_moveto:n { #1, #4 }
+ \draw_path_lineto:n { #3, #2 }
+ \draw_path_use_clear:n { stroke }
+ \draw_scope_end:
+ }
+
+% 米字线
+\__circledtext_new_char_box_construct:nn { x+ }
+ {
+ \__circledtext_char_box_type_c:n { dcross } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { cross } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框实心圆
+\__circledtext_new_char_box_construct:nn { o }
+ {
+ \__circledtext_char_box_type_c:n { filledcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框和内十字线实心圆
+\__circledtext_new_char_box_construct:nn { o+ }
+ {
+ \__circledtext_char_box_type_c:n { filledcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { cross } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框和内对角十字线实心圆
+\__circledtext_new_char_box_construct:nn { ox }
+ {
+ \__circledtext_char_box_type_c:n { filledcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { dcross } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框和内米字线实心圆
+\__circledtext_new_char_box_construct:nn { ox+ }
+ {
+ \__circledtext_char_box_type_c:n { filledcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { dcross } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { cross } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxcircle } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框实心正方形
+\__circledtext_new_char_box_construct:nn { O }
+ {
+ \__circledtext_char_box_type_c:n { filledsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 正方形米字线
+\__circledtext_new_char_box_construct:nn { X+ }
+ {
+ \__circledtext_char_box_type_c:n { dcrosssquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { crosssquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框和内十字线实心正方形
+\__circledtext_new_char_box_construct:nn { O+ }
+ {
+ \__circledtext_char_box_type_c:n { filledsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { crosssquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框和内对角十字线实心正方形
+\__circledtext_new_char_box_construct:nn { OX }
+ {
+ \__circledtext_char_box_type_c:n { filledsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { dcrosssquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+% 带边框和内米字线实心正方形
+\__circledtext_new_char_box_construct:nn { OX+ }
+ {
+ \__circledtext_char_box_type_c:n { filledsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { dcrosssquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { crosssquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ \__circledtext_char_box_type_c:n { boxsquare } {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+
+\msg_new:nnn { circledtext } { box-exists } { The~ box~ type~ `#1~ not~ exists. }
+
+% 缩放类型名称生成函数
+\cs_new_nopar:Npn \__circledtext_resize:n #1
+ {
+ __circledtext_processor_resize_ #1 :w
+ }
+
+% 缩放类型函数名称命令生成函数
+\cs_new_nopar:Npn \__circledtext_resize_c:n #1
+ {
+ \use:c
+ {
+ __circledtext_processor_resize_ #1 :w
+ }
+ }
+
+% 缩放代码生成函数
+% #1 dim长度变量1
+% #2 dim长度变量2
+% #3 缩放代码1
+% #4 缩放代码2
+% #5 缩放代码3
+% 如果 #1 > 0 ,取#3代码
+% 如果 #1 <= 0 且#2 > 0 ,取#4代码
+% 如果 #1 <= 0 且#2 <= 0,取#5代码
+\cs_new:Npn \__circledtext_dim_gezero_dispatch:NNnnn #1#2 #3#4#5
+ {
+ \dim_compare:nNnTF #1 > \c_zero_dim
+ { #3 }
+ {
+ \dim_compare:nNnTF #2 > \c_zero_dim
+ { #4 } { #5 }
+ }
+ }
+
+% 缩放代码生成函数
+% #1 dim长度变量1
+% #2 dim长度变量2
+% #3 缩放代码1
+% #4 缩放代码2
+% #5 缩放代码3
+% #6 缩放代码4
+% 如果 #1 > 0 且 #2 > 0 ,取#3代码
+% 如果 #1 > 0 且 #2 <= 0,取#4代码
+% 如果 #1 <= 0 且 #2 > 0 ,取#5代码
+% 如果 #1 <= 0 且 #2 <= 0,取#6代码
+\cs_new:Npn \__circledtext_dim_gezero_dispatch:NNnnnn #1#2 #3#4#5#6
+ {
+ \dim_compare:nNnTF #1 > \c_zero_dim
+ {
+ \dim_compare:nNnTF #2 > \c_zero_dim
+ { #3 } { #4 }
+ }
+ {
+ \dim_compare:nNnTF #2 > \c_zero_dim
+ { #5 } { #6 }
+ }
+ }
+
+% 缩放代码生成函数(分别按高度、宽度或实际尺寸缩放)
+\cs_new:Npn \__circledtext_force_size_dispatch:nnn % height, width, none
+ {
+ \__circledtext_dim_gezero_dispatch:NNnnn \l__circledtext_height_dim
+ \l__circledtext_width_dim
+ }
+
+% 缩放代码生成函数(分别按高宽、高度、宽度或实际尺寸缩放)
+\cs_new:Npn \__circledtext_force_size_dispatch:nnnn % both, height, width, none
+ {
+ \__circledtext_dim_gezero_dispatch:NNnnnn \l__circledtext_box_height_dim
+ \l__circledtext_box_width_dim
+ }
+
+% 构造缩放类型
+\cs_new:Npn \__circledtext_new_resize_method:nn #1
+ {
+ \clist_put_right:Nn \g__circledtext_resize_method_clist {#1}
+ \cs_new:cpn { \__circledtext_resize:n {#1} }
+ }
+
+% 无缩放
+\__circledtext_new_resize_method:nn { none } { }
+
+% 按实际参数缩放
+\__circledtext_new_resize_method:nn { real }
+ {
+ \__circledtext_force_size_dispatch:nnnn
+ {% 盒子宽高缩放
+ \coffin_resize:Nnn \l__circledtext_box_coffin
+ \l__circledtext_box_width_dim
+ \l__circledtext_box_height_dim
+ }
+ {% 指定高度为比例缩放
+ \coffin_scale:Nnn \l__circledtext_box_coffin
+ {
+ \dim_ratio:nn { \l__circledtext_box_height_dim }
+ { \__circledtext_coffin_ht_plus_dp:N \l__circledtext_box_coffin }
+ }
+ {
+ \dim_ratio:nn { \l__circledtext_box_height_dim }
+ { \__circledtext_coffin_ht_plus_dp:N \l__circledtext_box_coffin }
+ }
+ }
+ {% 指定宽度为比例缩放
+ \coffin_scale:Nnn \l__circledtext_box_coffin
+ {
+ \dim_ratio:nn { \l__circledtext_box_width_dim }
+ { \coffin_wd:N \l__circledtext_box_coffin }
+ }
+ {
+ \dim_ratio:nn { \l__circledtext_box_width_dim }
+ { \coffin_wd:N \l__circledtext_box_coffin }
+ }
+ }
+ {% 实际宽、高比例缩放
+ \coffin_scale:Nnn \l__circledtext_box_coffin
+ { \l__circledtext_x_scale_tl }
+ { \l__circledtext_y_scale_tl }
+ }
+ }
+
+% 按基字符参数缩放
+\__circledtext_new_resize_method:nn { base }
+ {
+ \__circledtext_force_size_dispatch:nnnn
+ {% 按指定盒子宽高缩放
+ \coffin_resize:Nnn \l__circledtext_box_coffin
+ \l__circledtext_box_width_dim
+ \l__circledtext_box_height_dim
+ }
+ {% 基字符宽高乘以高度比例缩放
+ \coffin_resize:Nnn \l__circledtext_box_coffin
+ {
+ \l__circledtext_char_width_dim * \dim_ratio:nn { \l__circledtext_box_height_dim }
+ { \__circledtext_coffin_ht_plus_dp:N \l__circledtext_box_coffin }
+ }
+ {
+ \l__circledtext_box_height_dim
+ }
+ }
+ {% 基字符宽高乘以宽度比例缩放
+ \coffin_resize:Nnn \l__circledtext_box_coffin
+ {
+ \l__circledtext_box_width_dim
+ }
+ {
+ \l__circledtext_char_height_dim * \dim_ratio:nn { \l__circledtext_box_width_dim }
+ { \coffin_wd:N \l__circledtext_box_coffin }
+ }
+ }
+ {% 基字符乘比例系数后缩放
+ \coffin_resize:Nnn \l__circledtext_box_coffin
+ {
+ \l__circledtext_x_scale_tl \l__circledtext_char_width_dim
+ }
+ {
+ \l__circledtext_y_scale_tl \l__circledtext_char_height_dim
+ }
+ }
+ }
+\msg_new:nnn { circledtext } { resize-type } { using~ `#1'~ resize. }
+
+% 笔画设置函数
+\cs_new:Npn \__circledtext_chars_stroke:nn #1#2
+ {
+ \special { pdf:code ~ q ~ #1 } #2 \special { pdf:code ~ Q }
+ }
+% 笔画构造函数
+\cs_new_protected:Npn \__circledtext_chars_stroke_construct:n #1
+ {
+ \int_case:nn {\l__circledtext_charstroke_type_int}
+ {
+ {1}{ #1 }
+ {2}{
+ \__circledtext_chars_stroke:nn { 1 ~ Tr ~ 0.10 ~ w ~ [] ~ 0 ~ d ~ 1 ~ J } {#1}
+ }
+ {3}{
+ \__circledtext_chars_stroke:nn { 1 ~ Tr ~ 0.10 ~ w ~ [1~1] ~ 0 ~ d ~ 1 ~ J } {#1}
+ }
+ {4}{
+ \__circledtext_chars_stroke:nn { 3 ~ Tr } {#1}
+ }
+
+ }
+ }
+\cs_generate_variant:Nn \__circledtext_chars_stroke_construct:n { V }
+\cs_generate_variant:Nn \__circledtext_chars_stroke_construct:n { x }
+
+% key_value选项设计
+\keys_define:nn { circledtext }
+ {
+ % 基字符
+ basechar .code:n = { \tl_gset:Nx \c__circledtext_basechar_tl {#1}
+ \__circledtext_calc_basechar_w_h:
+ },
+ % 字符格式
+ charf .code:n = { \tl_gset:Nn \l__circledtext_character_format_tl {#1}
+ \__circledtext_calc_char_box_size:
+ },
+ % 边框类型
+ boxtype .code:n = { \exp_args:NNx \clist_if_in:NnTF
+ \g__circledtext_char_box_list_clist {#1}
+ { \tl_set:Nx \l__circledtext_char_box_type_tl {#1} }
+ { \msg_error:nnx { circledtext } { box-exists } {#1} }
+ % \__circledtext_calc_char_box_size:
+ },
+ % 缩放方式
+ resize .code:n = { \exp_args:NNx \clist_if_in:NnTF
+ \g__circledtext_resize_method_clist {#1}
+ { \tl_set:Nx \l__circledtext_resize_method_tl {#1} }
+ { \msg_error:nnx { circledtext } { resize-method } {#1} }
+ },
+ % 缩放参数
+ xscale .tl_set:N = \l__circledtext_x_scale_tl ,
+ xscale .initial:n = 1 ,
+ yscale .tl_set:N = \l__circledtext_y_scale_tl ,
+ yscale .initial:n = 1 ,
+ scale .meta:n = { xscale = #1 , yscale = #1 } ,
+ width .dim_set:N = \l__circledtext_box_width_dim ,
+ height .dim_set:N = \l__circledtext_box_height_dim ,
+
+ % 字符边框线宽
+ boxlinewidth .dim_set:N = \l__circledtext_box_linewidth_dim ,
+ boxlinewidth .initial:n = 0.4pt ,
+ % 十字线线宽
+ crosslinewidth .dim_set:N = \l__circledtext_cross_linewidth_dim ,
+ crosslinewidth .initial:n = 0.3pt ,
+
+ % 边框线颜色
+ boxcolor .code:n = { \tl_set:Nx \l_tmpa_tl {
+ #1 ! \int_use:N \l__circledtext_cross_color_ratio_int }
+ \__circledtext_color_select:nn { circledtextcharboxcolor } {#1}
+ \__circledtext_color_select:nx { circledtextcrosscolor }
+ { \l_tmpa_tl }
+ } ,
+ boxcolor .initial:n = black ,
+ boxcolor* .code:n = { \tl_set:Nx \l_tmpa_tl {
+ #1 ! \int_use:N \l__circledtext_cross_color_ratio_int }
+ \__circledtext_color_select:nnn { circledtextcharboxcolor } #1
+ \__circledtext_color_select:nnx { circledtextcrosscolor }
+ \l_tmpa_tl
+ } ,
+ % 十字线颜色点边框颜色的比例(必须在boxcolor后)
+ crosscolorratio .code:n = { \int_set:Nn \l__circledtext_cross_color_ratio_int { #1 }
+ \__circledtext_color_select:nn { circledtextcrosscolor }
+ { circledtextcharboxcolor ! #1 }
+ },
+ crosscolorratio .initial:n = 30,
+
+ % 字符颜色
+ charcolor .code:n = { \__circledtext_color_select:nn { circledtextcharcolor } {#1} } ,
+ charcolor .initial:n = black ,
+ charcolor* .code:n = { \__circledtext_color_select:nnn { circledtextcharcolor } #1 } ,
+ color .meta:n = { boxcolor = #1, crosscolor = #1, charcolor = #1 } ,
+ color* .meta:n = { boxcolor* = #1, crosscolor = #1, charcolor* = #1 } ,
+ % 字符盒子背景填充颜色
+ boxfill .code:n = { \exp_args:Nx \tl_if_empty:nTF {#1}
+ { \__circledtext_color_select:nn { circledtextboxfill }
+ { white }
+ \bool_if:NTF \l__circledtext_negative_bool,
+ {\cs_set_nopar:Npn \__circledtext_aux_color_boxfill:
+ { \color_fill:n { white } }
+ }
+ {\cs_set_nopar:Npn \__circledtext_aux_color_boxfill: { }}
+ }{ \__circledtext_color_select:nn { circledtextboxfill } {#1}
+ \cs_set_nopar:Npn \__circledtext_aux_color_boxfill:
+ { \color_fill:n {#1} }
+ }
+ } ,
+ boxfill .initial:n = {} ,
+ boxfill* .code:n = { \__circledtext_color_select:nnn { circledtextboxfill } #1
+ \cs_set_nopar:Npn \__circledtext_aux_color_boxfill:
+ { \color_fill:nn #1 }
+ } ,
+ % 笔画参数
+ charstroke .choice:,
+ charstroke .value_required:n = true,
+ charstroke .choices:nn =
+ { none, solid, dashed, invisible }
+ { \int_set_eq:NN \l__circledtext_charstroke_type_int \l_keys_choice_int },
+ charstroke .initial:n = none,
+ % 虚线样式
+ dashpattern .tl_set:N = \l__circledtext_dash_pattern_tl ,
+ dashpattern .initial:n = { } ,
+
+ unknown .code:n = { \msg_error:nn { circledtext } { unknown-option } }
+ }
+\msg_new:nnn { circledtext } { unknown-option }
+ { package~ option~ "\l_keys_key_tl"~ is~ unknown. }
+
+% 根据编译引擎设置基字符
+\sys_if_engine_xetex:TF
+ {
+ \keys_set:nn { circledtext }
+ {
+ basechar = 好,
+ }
+ }{
+ \sys_if_engine_luatex:TF
+ {
+ \keys_set:nn { circledtext }
+ {
+ basechar = 好,
+ }
+ }{
+ \keys_set:nn { circledtext }
+ {
+ basechar = x,
+ }
+ }
+ }
+
+\keys_set:nn { circledtext }
+ {
+ charf = \normalsize,
+ boxtype = o,
+ resize = none,
+ }
+
+% 选项设置用户接口
+\NewDocumentCommand \circledtextset { m }
+ { \keys_set:nn { circledtext } {#1} }
+
+% 构建字符盒子,如字符为空,则用基字符构造
+% #1 需要处理的字符(串)
+\cs_new:Npn \__circledtext_single_char_construct:N #1
+ {
+ \tl_if_empty:NTF #1
+ {
+ \hcoffin_set:Nn \l__circledtext_char_coffin
+ {
+ \__circledtext_single_handle:N \c__circledtext_basechar_tl
+ }
+ }{
+ \hcoffin_set:Nn \l__circledtext_char_coffin
+ {
+ \__circledtext_single_handle:N #1
+ }
+ }
+ }
+
+% 构建带圈文字内部函数
+% #1 选项内容
+% #2 需要处理的字符(串)
+\cs_new:Npn \__circledtext_handle:nn #1#2
+ {
+ \group_begin:
+ % 选项设置
+ \keys_set:nn { circledtext } { #1 }
+
+ % 设置字符串
+ \tl_gset:Nx \l__circledtext_chars_tl {#2}
+
+ % 构造盒子
+ \__circledtext_single_char_construct:N \l__circledtext_chars_tl
+
+ % 复制盒子准备缩放与输出
+ \coffin_set_eq:NN \l__circledtext_box_coffin \l__circledtext_char_coffin
+
+ % 测量盒子容器总高度
+ \dim_set:Nn \l_tmpa_dim
+ { \__circledtext_coffin_ht_plus_dp:N \l__circledtext_box_coffin }
+ % 缩放字符盒子容器到字符大小
+ \coffin_scale:Nnn \l__circledtext_box_coffin
+ {
+ \dim_ratio:nn { \charboxht } { \l_tmpa_dim }
+ }
+ {
+ \dim_ratio:nn { \charboxht } { \l_tmpa_dim }
+ }
+
+ % 根据指定的方式缩放盒子
+ \__circledtext_resize_c:n { \l__circledtext_resize_method_tl }
+
+ % 输出盒子(下沉深度距离)
+ \coffin_typeset:Nnnnn \l__circledtext_box_coffin
+ { l } { b } { 0pt } { -\l__circledtext_char_dp_dim }
+ \group_end:
+ }
+
+% 字符处理句柄函数
+% #1 需要处理的字符(串)变量
+\cs_new:Npn \__circledtext_single_handle:N #1
+ {
+ \group_begin:
+ \tl_set:Nf \l__circledtext_curr_char_tl {#1}
+ \__circledtext_single_construct:N \l__circledtext_curr_char_tl
+ \group_end:
+ }
+
+% 构造带圈字符
+% #1 需要处理的字符(串)变量
+\cs_new:Npn \__circledtext_single_construct:N #1
+ {
+ % 按指定的格式和内容构造一个字符盒子容器
+ \hcoffin_set:Nn \l__circledtext_box_coffin
+ {
+ % \cs_if_eq:NNTF \__circledtext_aux_color_boxfill: \c_empty_tl
+ % {
+ % \color_select:n { circledtextcharcolor }
+ % }{
+ \bool_if:NTF \l__circledtext_negative_bool
+ {
+ \color_select:n { circledtextboxfill }
+ }{
+ \color_select:n { circledtextcharcolor }
+ }
+ % }
+ \tl_use:N \l__circledtext_character_format_tl
+ \__circledtext_chars_stroke_construct:n { #1 }
+ }
+
+ % 构造字符盒子容器边框
+ \__circledtext_single_box_construct:
+ }
+
+% 字符盒子及边框组合盒子容器构造函数
+\cs_new:Npn \__circledtext_single_box_construct:
+ {
+ % 根据文字内容计算缩放比例
+ \dim_set:Nn \l_tmpa_dim
+ { \coffin_wd:N \l__circledtext_box_coffin }
+ \dim_set:Nn \l_tmpb_dim
+ { \__circledtext_coffin_ht_plus_dp:N \l__circledtext_box_coffin }
+ \dim_compare:nNnF \l_tmpa_dim > \l_tmpb_dim
+ {
+ \dim_set_eq:NN \l_tmpa_dim \l_tmpb_dim
+ }
+
+ % 缩放字符盒子容器
+ \coffin_scale:Nnn \l__circledtext_box_coffin
+ {
+ \dim_ratio:nn { \charboxwd } { \l_tmpa_dim }
+ }
+ {
+ \dim_ratio:nn { \charboxht } { \l_tmpa_dim }
+ }
+
+ % 如果是矩形外框,则适当缩小文字
+ \clist_if_in:nVT {O,O+,OX,OX+,X+} \l__circledtext_char_box_type_tl
+ {
+ \coffin_scale:Nnn \l__circledtext_box_coffin
+ { 0.75 } { 0.75 }
+ }
+
+ % 绘制外框并拼装结果
+ \draw_begin:
+ \draw_linewidth:n { \l__circledtext_box_linewidth_dim }
+
+ \draw_path_scope_begin:
+ \__circledtext_char_box_type_c:n { \l__circledtext_char_box_type_tl }
+ { 0 } { 0 } { \charboxwd } { \charboxht } { 1.0 } { 1.0 }
+ \draw_transform_shift:n {\charboxwd / 2.0, \charboxht / 2.0 }
+ \draw_coffin_use:Nnn \l__circledtext_box_coffin { hc } { vc }
+ \draw_path_scope_end:
+ \draw_end:
+ }
+
+\endinput