% Package : scaletextbullet -- Resize the \textbullet without changing its % vertical center % Copyright : 2024 (c) Oliver Beery % CTAN : https://ctan.org/pkg/scaletextbullet % Repository: https://github.com/beeryoliver/scaletextbullet % License : The LaTeX Project Public License 1.3c % LaTeX2e version 2023-11-01 added \IfExplAtLeastTF. \NeedsTeXFormat{LaTeX2e}[2023-11-01] \ProvidesExplPackage {scaletextbullet} {2024-11-25} {1.0.2} {Resize the \noexpand\textbullet without changing its vertical center.} % l3kernel version 2023-10-10 added many 'e'-variants. \IfExplAtLeastTF { 2023-10-10 } { } { \msg_new:nnn { scaletextbullet } { expl3-out-of-date } { The~ scaletextbullet~ package~ could~ not~ load.~ This~ package~ requires~ L3~ programming~ layer~ version~ 2023-10-10~ or~ later. } \msg_critical:nn { scaletextbullet } { expl3-out-of-date } } % This package does not require any other packages! % SOME VARIABLES \tl_new:N \l__scaletextbullet_factor_tl \tl_set:Nn \l__scaletextbullet_factor_tl { 0.4 } % Used to speed up \ScaleTextBullets{2}, \ScaleTextBullets{3}, and % \ScaleTextBullets{4}. \fp_const:Nn \c__scaletextbullet_ii_fp { 2 ^ -0.5 } \fp_const:Nn \c__scaletextbullet_iii_fp { 3 ^ -0.5 } \fp_const:Nn \c__scaletextbullet_iv_fp { 0.5 } % Scratch variables \int_new:N \l__scaletextbullet_tmp_int \fp_new:N \l__scaletextbullet_tmp_fp \box_new:N \l__scaletextbullet_tmp_box % SOME FUNCTIONS % Used to process user input. Sets an integer variable from an integer % expression and removes extra tokens. \cs_new_protected:Npn \__scaletextbullet_int_set_from_user:Nn #1#2 { \afterassignment \use_none_delimit_by_q_stop:w #1 = \numexpr #2 \relax \q_stop } % Argument processor \int_new:N \l__scaletextbullet_arg_process_int \cs_new_protected:Npn \__scaletextbullet_arg_process_int:n #1 { \__scaletextbullet_int_set_from_user:Nn \l__scaletextbullet_arg_process_int {#1} \tl_set:NV \ProcessedArgument \l__scaletextbullet_arg_process_int } % Lowers the \textbullet to the baseline, scales it by a factor of #2, and % then raises it back to the vertical center. % I have referenced code by the great egreg: % https://tex.stackexchange.com/questions/620507/how-to-resize-textbullet-without-the-bullet-moving-down \dim_new:N \l__scaletextbullet_bottom_dim % bottom of \textbullet \dim_new:N \l__scaletextbullet_center_dim % center of \textbullet \cs_new_protected:Npn \__scaletextbullet_box_scale:Nn #1#2 { \hbox_set:Nn #1 { \textbullet } \dim_set:Nn \l__scaletextbullet_bottom_dim { \box_ht:N #1 - \l__scaletextbullet_factor_tl \box_wd:N #1 } \dim_set:Nn \l__scaletextbullet_center_dim { ( \box_ht:N #1 + \l__scaletextbullet_bottom_dim ) / 2 } \hbox_set:Nn #1 { \box_move_down:nn { \l__scaletextbullet_bottom_dim } { \box_use:N #1 } } \box_scale:Nnn #1 {#2} {#2} \hbox_set:Nn #1 { \box_move_up:nn { \l__scaletextbullet_center_dim - 0.5 \box_ht:N #1 } { \box_use:N #1 } } } % MESSAGES \msg_new:nnn { scaletextbullet } { factor-out-of-bounds } { Invalid~ \iow_char:N \\textbullet~ factor~ '#1'~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { scale-out-of-bounds } { Invalid~ scale~ factor~ '#1'~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { count-out-of-bounds } { Invalid~ number~ of~ \iow_char:N \\textbullet s~ '#1'~ \msg_line_context:. } % DOCUMENT COMMANDS \NewDocumentCommand \SetTextBulletFactor { m } { \__scaletextbullet_set_factor:n {#1} } \cs_new_protected:Npn \__scaletextbullet_set_factor:n #1 { \fp_set:Nn \l__scaletextbullet_tmp_fp {#1} \fp_compare:nTF { \c_zero_fp < \l__scaletextbullet_tmp_fp <= \c_one_fp } { \tl_set:Ne \l__scaletextbullet_factor_tl { \fp_use:N \l__scaletextbullet_tmp_fp } } { \msg_error:nne { scaletextbullet } { factor-out-of-bounds } { \fp_use:N \l__scaletextbullet_tmp_fp } } } \NewDocumentCommand \ScaleTextBullet { m } { \mode_leave_vertical: \__scaletextbullet_scaletextbullet:n {#1} } \cs_new_protected:Npn \__scaletextbullet_scaletextbullet:n #1 { \fp_set:Nn \l__scaletextbullet_tmp_fp {#1} \fp_compare:nNnTF \l__scaletextbullet_tmp_fp > \c_zero_fp { \__scaletextbullet_box_scale:Nn \l__scaletextbullet_tmp_box { \l__scaletextbullet_tmp_fp } \box_use_drop:N \l__scaletextbullet_tmp_box } { \fp_compare:nNnF \l__scaletextbullet_tmp_fp = \c_zero_fp { \msg_error:nne { scaletextbullet } { scale-out-of-bounds } { \fp_use:N \l__scaletextbullet_tmp_fp } } } } \NewDocumentCommand \ScaleTextBullets { o >{ \__scaletextbullet_arg_process_int:n } m } { \mode_leave_vertical: \IfNoValueTF {#1} { \__scaletextbullet_scaletextbullets:n {#2} } { \__scaletextbullet_scaletextbullets:nn {#1} {#2} } } \cs_new_protected:Npn \__scaletextbullet_scaletextbullets:n #1 { \int_set:Nn \l__scaletextbullet_tmp_int {#1} \int_compare:nNnTF \l__scaletextbullet_tmp_int > 0 { \int_case:nnF { \l__scaletextbullet_tmp_int } { { 1 } { \fp_set_eq:NN \l__scaletextbullet_tmp_fp \c_one_fp } { 2 } { \fp_set_eq:NN \l__scaletextbullet_tmp_fp \c__scaletextbullet_ii_fp } { 3 } { \fp_set_eq:NN \l__scaletextbullet_tmp_fp \c__scaletextbullet_iii_fp } { 4 } { \fp_set_eq:NN \l__scaletextbullet_tmp_fp \c__scaletextbullet_iv_fp } } { \fp_set:Nn \l__scaletextbullet_tmp_fp { \l__scaletextbullet_tmp_int ^ -0.5 } } \__scaletextbullet_box_scale:Nn \l__scaletextbullet_tmp_box { \l__scaletextbullet_tmp_fp } \prg_replicate:nn { \l__scaletextbullet_tmp_int } { \box_use:N \l__scaletextbullet_tmp_box } } { \int_compare:nNnF \l__scaletextbullet_tmp_int = 0 { \msg_error:nnV { scaletextbullet } { count-out-of-bounds } \l__scaletextbullet_tmp_int } } } \cs_new_protected:Npn \__scaletextbullet_scaletextbullets:nn #1#2 { \fp_set:Nn \l__scaletextbullet_tmp_fp {#1} \fp_compare:nNnTF \l__scaletextbullet_tmp_fp > \c_zero_fp { \int_set:Nn \l__scaletextbullet_tmp_int {#2} \int_compare:nNnTF \l__scaletextbullet_tmp_int > 0 { \__scaletextbullet_box_scale:Nn \l__scaletextbullet_tmp_box { \l__scaletextbullet_tmp_fp } \prg_replicate:nn { \l__scaletextbullet_tmp_int } { \box_use:N \l__scaletextbullet_tmp_box } } { \int_compare:nNnF \l__scaletextbullet_tmp_int = 0 { \msg_error:nnV { scaletextbullet } { count-out-of-bounds } \l__scaletextbullet_tmp_int } } } { \fp_compare:nNnF \l__scaletextbullet_tmp_fp = \c_zero_fp { \msg_error:nne { scaletextbullet } { scale-out-of-bounds } { \fp_use:N \l__scaletextbullet_tmp_fp } } } } \NewDocumentCommand \scaletextbulletdebug { } { \mode_leave_vertical: \__scaletextbullet_debug: } % I have referenced code by the great egreg: % https://tex.stackexchange.com/questions/620507/how-to-resize-textbullet-without-the-bullet-moving-down \cs_new_protected:Npn \__scaletextbullet_debug: { \int_step_inline:nn { 15 } { \fp_set:Nn \l__scaletextbullet_tmp_fp { ##1 ^ -0.5 } \__scaletextbullet_box_scale:Nn \l__scaletextbullet_tmp_box { \l__scaletextbullet_tmp_fp } \box_use_drop:N \l__scaletextbullet_tmp_box } \, \group_begin: \dim_set:Nn \fboxrule { 0.1pt } \dim_zero:N \fboxsep \framebox [ \l__scaletextbullet_factor_tl \width ] { \textbullet } \group_end: }