% Package : scaletextbullet -- Resize the \textbullet without changing its % vertical center % Copyright : 2024-2026 (c) Oliver Beery % CTAN : https://ctan.org/pkg/scaletextbullet % Repository: https://github.com/beeryoliver/scaletextbullet % License : The LaTeX Project Public License 1.3c % Loading the package \NeedsTeXFormat{LaTeX2e}[2023-11-01] \ProvidesExplPackage{scaletextbullet}{2026-02-18}{2.0.5} {Resize the \noexpand\textbullet without changing its vertical center.} \msg_new:nnn { scaletextbullet } { l3kernel } { The~ scaletextbullet~ package~ could~ not~ load. \\ This~ package~ requires~ L3~ programming~ layer~ version~ 2023-11-01~ or~ later. } \IfExplAtLeastTF { 2023-11-01 } { } { \msg_critical:nn { scaletextbullet } { l3kernel } } % Some variables \fp_new:N \l__scaletextbullet_factor_fp \fp_set:Nn \l__scaletextbullet_factor_fp { 0.4 } \int_new:N \l__scaletextbullet_count_int \fp_new:N \l__scaletextbullet_scale_fp \box_new:N \l__scaletextbullet_textbullet_box % Used only to speed up floating point computations. \fp_const:Nn \c__scaletextbullet_count_two_fp { 0.7071 0678 1186 5475 } \fp_const:Nn \c__scaletextbullet_count_three_fp { 0.5773 5026 9189 6258 } \fp_const:Nn \c__scaletextbullet_count_four_fp { 0.5 } % Some functions % 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 user egreg: % https://tex.stackexchange.com/questions/620507 % the bottom and center of \textbullet \dim_new:N \l__scaletextbullet_bottom_dim \dim_new:N \l__scaletextbullet_center_dim \cs_new_protected:Npn \__scaletextbullet_box_scale:Nn #1#2 { \mode_leave_vertical: \hbox_set:Nn #1 { \textbullet } \dim_set:Nn \l__scaletextbullet_bottom_dim { \box_ht:N #1 - \fp_to_dim:n { \l__scaletextbullet_factor_fp * \dim_to_fp:n { \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 - \box_ht:N #1 / 2 } { \box_use:N #1 } } } \cs_new:Npn \__scaletextbullet_box_if_zero:NTF #1 { \bool_lazy_or:nnTF { \dim_compare_p:nNn { \box_wd:N #1 } = \c_zero_dim } { \dim_compare_p:nNn { \box_ht_plus_dp:N #1 } = \c_zero_dim } } % Document commands \NewDocumentCommand \settextbulletfactor { m } { \mode_if_math:TF { \msg_error:nnn { scaletextbullet } { math-mode-invalid } { settextbulletfactor } } { \__scaletextbullet_set_factor:n {#1} } } \cs_new_protected:Npn \__scaletextbullet_set_factor:n #1 { \fp_set:Nn \l__scaletextbullet_factor_fp {#1} \fp_compare:nF { \c_zero_fp < \l__scaletextbullet_factor_fp <= \c_one_fp } { \msg_error:nn { scaletextbullet } { factor-invalid } } } \NewDocumentCommand \scaletextbullet { m } { \mode_if_math:TF { \msg_error:nnn { scaletextbullet } { math-mode-invalid } { scaletextbullet } } { \__scaletextbullet_scale_textbullet:n {#1} } } \cs_new_protected:Npn \__scaletextbullet_scale_textbullet:n #1 { \fp_set:Nn \l__scaletextbullet_scale_fp {#1} \fp_compare:nNnTF \l__scaletextbullet_scale_fp < \c_zero_fp { \msg_error:nn { scaletextbullet } { scale-invalid } } { \__scaletextbullet_box_scale:Nn \l__scaletextbullet_textbullet_box { \l__scaletextbullet_scale_fp } \__scaletextbullet_box_if_zero:NTF \l__scaletextbullet_textbullet_box { \msg_warning:nn { scaletextbullet } { scale-zero } } { \box_use:N \l__scaletextbullet_textbullet_box } } } \NewDocumentCommand \scaletextbullets { o m } { \mode_if_math:TF { \msg_error:nnn { scaletextbullet } { math-mode-invalid } { scaletextbullets } } { \IfNoValueTF {#1} { \__scaletextbullet_scale_textbullets:n {#2} } { \__scaletextbullet_scale_textbullets:nn {#1} {#2} } } } \cs_new_protected:Npn \__scaletextbullet_scale_textbullets:n #1 { \int_set:Nn \l__scaletextbullet_count_int {#1} \int_compare:nNnTF \l__scaletextbullet_count_int > 0 { \int_case:nnF { \l__scaletextbullet_count_int } { { 1 } { \fp_set_eq:NN \l__scaletextbullet_scale_fp \c_one_fp } { 2 } { \fp_set_eq:NN \l__scaletextbullet_scale_fp \c__scaletextbullet_count_two_fp } { 3 } { \fp_set_eq:NN \l__scaletextbullet_scale_fp \c__scaletextbullet_count_three_fp } { 4 } { \fp_set_eq:NN \l__scaletextbullet_scale_fp \c__scaletextbullet_count_four_fp } } { \fp_set:Nn \l__scaletextbullet_scale_fp { \int_use:N \l__scaletextbullet_count_int ^ -0.5 } } \__scaletextbullet_box_scale:Nn \l__scaletextbullet_textbullet_box { \l__scaletextbullet_scale_fp } \prg_replicate:nn { \l__scaletextbullet_count_int } { \box_use:N \l__scaletextbullet_textbullet_box } } { \int_if_zero:nTF { \l__scaletextbullet_count_int } { \msg_warning:nn { scaletextbullet } { count-zero } } { \msg_error:nn { scaletextbullet } { count-invalid } } } } \cs_new_protected:Npn \__scaletextbullet_scale_textbullets:nn #1#2 { \fp_set:Nn \l__scaletextbullet_scale_fp {#1} \int_set:Nn \l__scaletextbullet_count_int {#2} \fp_compare:nNnTF \l__scaletextbullet_scale_fp < \c_zero_fp { \msg_error:nn { scaletextbullet } { scale-invalid } } { \int_compare:nNnTF \l__scaletextbullet_count_int > 0 { \__scaletextbullet_box_scale:Nn \l__scaletextbullet_textbullet_box { \l__scaletextbullet_scale_fp } \__scaletextbullet_box_if_zero:NTF \l__scaletextbullet_textbullet_box { \msg_warning:nn { scaletextbullet } { scale-zero } } { \prg_replicate:nn { \l__scaletextbullet_count_int } { \box_use:N \l__scaletextbullet_textbullet_box } } } { \int_if_zero:nTF { \l__scaletextbullet_count_int } { \msg_warning:nn { scaletextbullet } { count-zero } } { \msg_error:nn { scaletextbullet } { count-invalid } } } } } \NewDocumentCommand \scaletextbulletdebug { } { \mode_if_math:TF { \msg_error:nnn { scaletextbullet } { math-mode-invalid } { scaletextbulletdebug } } { \__scaletextbullet_debug: } } % I have referenced code by the user egreg: % https://tex.stackexchange.com/questions/620507 \cs_new_protected:Npn \__scaletextbullet_debug: { \int_step_inline:nn { 15 } { \fp_set:Nn \l__scaletextbullet_scale_fp { ##1 ^ -0.5 } \__scaletextbullet_box_scale:Nn \l__scaletextbullet_textbullet_box { \l__scaletextbullet_scale_fp } \box_use:N \l__scaletextbullet_textbullet_box } \, \group_begin: \setlength \fboxrule { 0.1pt } \setlength \fboxsep { 0pt } \framebox [ \fp_to_dim:n { \l__scaletextbullet_factor_fp * \dim_to_fp:n { \width } } ] { \textbullet } \group_end: } % Messages \msg_new:nnn { scaletextbullet } { math-mode-invalid } { '\iow_char:N \\ #1'~ invalid~ in~ math~ mode~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { factor-invalid } { Invalid~ \iow_char:N \\ textbullet~ factor~ '\fp_to_tl:N \l__scaletextbullet_factor_fp'~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { scale-invalid } { Invalid~ scale~ factor~ '\fp_to_tl:N \l__scaletextbullet_scale_fp'~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { count-invalid } { Invalid~ number~ of~ \iow_char:N \\ textbullet~ s~ '\int_use:N \l__scaletextbullet_count_int'~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { scale-zero } { The~ new~ \iow_char:N \\ textbullet~ would~ have~ zero~ dimensions~ \msg_line_context:. } \msg_new:nnn { scaletextbullet } { count-zero } { No~ \iow_char:N \\ textbullet~ s~ were~ printed~ \msg_line_context:. }