% grruby.sty: 한글(한자) macro
%
% according to the concept suggested by gromob
% implemented by nanim
%
% v0.2: package option rubycolor, command \grrubycolor (2015/05/02)
% v0.3: package option expos added (2019/05/17)
% v0.4: rubystyle lw, rw, user added (2019/05/18)
% v0.4.1: bug fix
% v0.5: ksruby support, deprecate \OffStuff
% v0.6: environ package dropped. option pdflatex introduced.
% v0.7: \AtEndOfPackage bug fix
% v0.8: redesign grrubypara(s) environments.
% v0.8.5: xparse
%
%
\ProvidesPackage{grruby}[2025/05/01 v0.8.5]

%\RequirePackage{expl3,xparse}
\RequirePackage{xparse}
\RequirePackage{l3keys2e}

\ExplSyntaxOn 

\keys_define:nn { grruby }
{
	rubystyle	.tl_set:N = \gr_ruby_style,
	rubystyle	.default:n = { \tl_set:Nn \gr_ruby_style { default } },
	rubycolor	.tl_set:N = \gr_ruby_color,
%	rubysize	.tl_set:N = \gr_ruby_size
	expos		.bool_set:N = \gr_expos_bool,
	pdflatex	.bool_set:N = \gr_pdflatex_bool
}

\bool_gset_false:N \gr_expos_bool
\tl_set:Nn \gr_ruby_color { gray }

\ProcessKeysOptions {grruby}

\bool_if:NF \gr_pdflatex_bool
{ \RequirePackage{kotex} }
\ExplSyntaxOff 

\RequirePackage{ragged2e}
\RequirePackage{relsize}
\RequirePackage{etoolbox}

\AtEndPreamble{\@ifpackageloaded{xcolor}{}{\RequirePackage{xcolor}}}

\ExplSyntaxOn

\bool_new:N \is_grword_on
\bool_gset_true:N \is_grword_on

\bool_new:N \g_ksruby_bool
\@ifpackageloaded{ksruby}{\bool_set_true:N \g_ksruby_bool}{\bool_set_false:N \g_ksruby_bool}

\bool_if:NTF \gr_pdflatex_bool
{
	\cs_new:Npn \gr_rubystyle_box:nnn #1 #2 #3
	{
		\raisebox{#1}{ #2 \color { \gr_ruby_color } #3 }
	}
}
{
	\cs_new:Npn \gr_rubystyle_box:nnn #1 #2 #3
	{
		\ifLuaTeX\hangulbyhangulfont=1\hanjabyhanjafont=1\fi
		\addhangulfontfeature{CharRaise=#1}
		\addhanjafontfeature{CharRaise=#1}
		#2 \color{ \gr_ruby_color } #3
	}
}

\NewDocumentCommand \gr_ruby_style_default { u( u) g }
{
	\tl_if_empty:NT \gr_ruby_color { \tl_set:Nn \gr_ruby_color { gray } }
	{
%		\addhangulfontfeature{CharRaise=.133em}
%		\smaller \color{\gr_ruby_color}  #1
		\gr_rubystyle_box:nnn { .1333em } { \smaller } { #1 }
	}
	#2
	\IfValueT { #3 } { \footnote { #3 } }
}

\NewDocumentCommand \gr_ruby_style_rup { u( u) g }
{
	\tl_if_empty:NT \gr_ruby_color { \tl_set:Nn \gr_ruby_color { black } }
	#2
	{
%		\addhangulfontfeature{CharRaise=.25em}
%		\color{\gr_ruby_color} \footnotesize #1
		\gr_rubystyle_box:nnn { .25em } { \footnotesize } { #1 }
	}
	\IfValueT { #3 } { \footnote { #3 } }
}

% 한자 위에 글자마다 한글 루비
\cs_new:Npn \ruby_each_char:nn #1 #2
{
	\seq_set_split:Nnn \l_tmpa_seq {} { #1 }
	\seq_set_split:Nnn \l_tmpb_seq {} { #2 }
	\seq_map_inline:Nn \l_tmpa_seq
	{
		\seq_pop_left:NN \l_tmpb_seq \l_tmpb_tl
		\exp_args:Nno \ruby { ##1 } { \l_tmpb_tl } \allowbreak
	}
}

\NewDocumentCommand \gr_ruby_style_XXruby { u( u) g }
{
	\bool_if:NTF \g_ksruby_bool
	{
		\ksrubyextra{\color{\gr_ruby_color}}
		\ksrubyeachchardefault
		\ksruby { #2 } { #1 }
	}
	{
		\ruby_each_char:nn { #2 } { #1 }
	}
	\IfValueT { #3 } { \footnote{ #3 } }
}

\NewDocumentCommand \gr_ruby_style_ruby { u( u) g }
{
	\bool_if:NTF \g_ksruby_bool
	{
		\ksrubyeachchardefault[false]
		\ksrubyextra{\color{\gr_ruby_color}}
		\ksruby { #2 } { #1 }
	}
	{
		\ruby { #2 } { #1 }
	}
	\IfValueT { #3 } { \footnote { #3 } }
}

% 한글+작은글자 한자
\NewDocumentCommand \gr_ruby_style_progress { u( u) g }
{
	\tl_if_empty:NT \gr_ruby_color { \tl_set:Nn \gr_ruby_color { black } }
	#1 { \color{\gr_ruby_color} \footnotesize #2 }
	\IfValueT { #3 } { \footnote{#3} }
}

%% lword & rword
\NewDocumentCommand \gr_ruby_style_lword { u( u) g }
{
	#1
	\IfValueT { #3 } { \footnote{#3} }
}

\NewDocumentCommand \gr_ruby_style_rword { u( u) g }
{
	#2
	\IfValueT { #3 } { \footnote{#3} }
}

%% UserDefined
\newcommand \grrubyUserDefined [2] {
	#1 ( #2 )
}

\NewDocumentCommand \gr_ruby_style_user { u( u) g }
{
	\grrubyUserDefined { #1 } { #2 }
	\IfValueT { #3 } { \footnote{#3} }
}

\NewDocumentCommand \gr_proc_word: { u( u) g }
{
	\str_if_in:nnTF { #1 } { $ }
	{
		#1 (#2) \IfValueT{#3}{{#3}}
	}
	{
    	\bool_if:NTF \gr_expos_bool
    	{
        	\IfValueTF { #3 }
        	{ \gr_proc_word_fn: #2 ( #1 ) { #3 } }
        	{ \gr_proc_word_fn: #2 ( #1 ) }
    	}
    	{
        	\IfValueTF { #3 }
        	{ \gr_proc_word_fn: #1 ( #2 ) { #3 } }
        	{ \gr_proc_word_fn: #1 ( #2 ) }
    	}
	}
}

\cs_new:Nn \gr_identity_word:
{ \scan_stop: }

\cs_set_eq:NN \grfoo \gr_proc_word:	

\NewDocumentCommand \grfooXX { O{} }
{
	#1\grfoo 
}

\NewDocumentCommand \gromov_request { m }
{
	\seq_clear:N \wordwise_para_seq
	\seq_set_split:Nnn \wordwise_para_seq { ~ } { #1 }
	\seq_map_inline:Nn \wordwise_para_seq
	{
		\str_if_in:nnTF { ##1 } { ( }
		{
			\str_if_in:nnTF { ##1 } { ) } 
			{
				\bool_if:NTF \is_grword_on
				{ \exp_args:Nx \gr_proc_word: ##1 }
				{ \exp_args:Nx \gr_identity_word: ##1 }
			}
			{ ##1 }
		}
		{ ##1 }
		\space
	}
}

\cs_new_protected:Nn \my_ruby_off:
{
	\bool_gset_false:N \is_grword_on
}

\cs_new_protected:Nn \my_ruby_on:
{
	\bool_gset_true:N \is_grword_on
}

\def\grfoopar#1\par{\gromov_request{#1}\par}

%\NewEnviron{grruby}{\exp_args:Nx \gromov_request {\BODY} }
\NewDocumentEnvironment { grruby } { b }
{
	\gromov_request { #1 }
}{}

%\NewDocumentCommand \MathStuff { +m }
%{
%	\if_mode_vertical: \leavevmode \fi:
%	{ \my_ruby_off: #1 \unskip \my_ruby_on: }
%}

\NewDocumentEnvironment {grrubypars} { }
{
 	\everypar { \grfoopar }
}
{
	\ignorespacesafterend
}

\NewDocumentCommand \grrubystyle { m }
{
	\str_case:nnF { #1 }
	{
		{ default  } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_default  }
		{ gromob   } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_default  }
		{ XXruby   } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_XXruby   }
		{ progress } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_progress }
		{ rup      } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_rup      }
		{ ruby     } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_ruby     }
		{ lw       } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_lword    }
		{ rw       } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_rword    }
		{ user     } { \cs_gset_eq:NN \gr_proc_word_fn: \gr_ruby_style_user     }
	}
	{
		\cs_gset_eq:NN \gr_proc_word: \gr_ruby_style_default
	}
}

\NewDocumentCommand \grrubycolor { m }
{
	\tl_set:Nn \gr_ruby_color { #1 }
}

\NewDocumentCommand \grexpos { o }
{
	\IfValueTF { #1 }
	{
		\str_case:nn { #1 }
		{
			{ true } { \bool_gset_true:N \gr_expos_bool }
			{ false } { \bool_gset_false:N \gr_expos_bool }
		}
	}
	{
		\bool_gset_inverse:N \gr_expos_bool
	}
}

\exp_args:Nx \grrubystyle { \gr_ruby_style }
\cs_if_exist:NF \gr_proc_word_fn:
{ \grrubystyle { default } }

%%%%% for next version
%\char_set_catcode_space:n { 183 } 

%\tl_new:N \l_chkword_tl
%\clist_new:N \g_chkword_clist
%\clist_set:Nn \g_chkword_clist { `, ', ", “,”, \cntrdot,·}
%
%\cs_new:Npn \_chk_and_exit:nN #1 #2
%{
%    \token_if_eq_charcode:NNTF #1 \par
%    {
%    	\_tl_flush_chkword:N \l_chkword_tl
%		\par
%	}
%    { #2 }
%}
%
%\cs_new:Npn \checkpara_rc:n #1
%{
%    \peek_catcode:NTF \c_space_token
%    {
%    	\clist_if_in:NnTF \g_chkword_clist { #1 }
%		{
%			\tl_if_empty:NF \l_chkword_tl
%			{
%				\_tl_flush_chkword:N \l_chkword_tl
%				\tl_clear:N \l_chkword_tl
%			}
%			#1
%		}
%		{
%    	  \tl_put_right:Nn \l_chkword_tl { #1 }
%		  \_tl_flush_chkword:N \l_chkword_tl
%		  \tl_clear:N \l_chkword_tl
%        }
%		\space
%        \_chk_and_exit:nN #1 \checkpara_rc:n
%    }
%    {
%    	\clist_if_in:NnTF \g_chkword_clist { #1 }
%		{
%			\tl_if_empty:NF \l_chkword_tl
%			{
%				\_tl_flush_chkword:N \l_chkword_tl
%				\tl_clear:N \l_chkword_tl
%			}
%			#1
%		}
%		{
%			\tl_put_right:Nn \l_chkword_tl { #1 }
%		}
%        \_chk_and_exit:nN #1 \checkpara_rc:n
%    }
%}
%
%\cs_new:Npn \_tl_flush_chkword:N #1
%{
%	\exp_args:Nnx 
%%	\regex_match:nnTF { \( *[^0-9a-zA-Z가-힣] \) } { #1 }
%	\regex_match:nnTF { \( *[㐀-鿐豈-齃] \) } { #1 }
%	{
%		\exp_last_unbraced:Nx \grfoo \l_chkword_tl
%	}
%	{
%		\l_chkword_tl
%	}
%}
%
%\cs_set_protected_nopar:Nn \checkpara:
%{
%	\tl_clear:N \l_chkword_tl
%	\checkpara_rc:n
%}
%
%\cs_set_eq:NN \checkpara \checkpara:
%
%\NewDocumentEnvironment { grrubypara } { b }
%{
%	\group_begin:
%	\checkpara:
%	#1
%	\par
%	\group_end:
%}
%{
%	\scan_stop:
%}
%
%\NewDocumentEnvironment { grrubyparas } { +b }
%{
%	\group_begin:
%	\everypar{ \checkpara: }
%	#1
%	\par
%	\group_end:
%}{ \scan_stop: }

\tl_new:N \l_grrubyparaarg_tl

\NewDocumentEnvironment { grrubypara } { b }
{
	\group_begin:
	\tl_set:Nn \l_grrubyparaarg_tl { #1 }
	\regex_replace_all:nnN { ([\H]+?)\((.+?)\) } { \ \c{grfooXX} \1 ( \2 ) } \l_grrubyparaarg_tl
	\tl_use:N \l_grrubyparaarg_tl
	\group_end:
	\par
}{}

\NewDocumentEnvironment { grrubyparas } { +b }
{
	\regex_split:nnN { \c{par} } { #1 } \l_tmpa_seq
	\seq_map_inline:Nn \l_tmpa_seq
	{
		\begin{grrubypara}
		##1
		\end{grrubypara}
	}
}{}

\ExplSyntaxOff

\endinput
