% \iffalse meta-comment
% !TEX program  = XeLaTeX
%<*internal>
\iffalse
%</internal>
%<*readme>
xpinyin
=======

`xpinyin` is a LaTeX package written to simplify the input of Hanyu Pinyin.
The package provides macros to automatically add pinyin to Chinese characters.
It can only be used in conjunction with `xeCJK` or `CJK/CJKutf8` package.

Given that the implementation of pinyin package of `CJK` bundle is not very
well, it seems that xpinyin is a good replacement of it.

You can read the package manual (in Chinese) for more detailed explanations.

Contributing
------------

This package is a part of the [ctex-kit](https://github.com/CTeX-org/ctex-kit) project.

Issues and pull requests are welcome.

Copyright and Licence
---------------------

    Copyright (C) 2012-2022 by Qing Lee <sobenlee@gmail.com>
    ----------------------------------------------------------------------

    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 Qing Lee.

    This package consists of the file  xpinyin.dtx,
                 and the derived files xpinyin.sty,
                                       xpinyin.pdf,
                                       xpinyin.ins,
                                       xpinyin.lua,
                                       xpinyin.db,
                                       xpinyin-database.def and
                                       README.md (this file).

%</readme>
%<*internal>
\fi
\begingroup
  \def\temp{LaTeX2e}
\expandafter\endgroup\ifx\temp\fmtname\else
\csname fi\endcsname
%</internal>
%<*install>

%<install>\begingroup\expandafter\expandafter\expandafter\endgroup
%<install>\expandafter\ifx\csname directlua\endcsname\relax
%<install>  \begingroup
%<install>  \newlinechar=10 %
%<install>  \errmessage{^^J
%<install>    ********************************************^^J
%<install>    * LuaTeX is required to unpack this package.^^J
%<install>    ********************************************^^J
%<install>  }
%<install>  \endgroup
%<install>  \csname @@end\expandafter\endcsname\expandafter\end
%<install>\fi

\input ctxdocstrip %

\let\MetaPrefix\relax

\preamble

    Copyright (C) 2012-2022 by Qing Lee <sobenlee@gmail.com>
--------------------------------------------------------------------------

    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 Qing Lee.

--------------------------------------------------------------------------

\endpreamble

\postamble

    This package consists of the file  xpinyin.dtx,
                 and the derived files xpinyin.sty,
                                       xpinyin.pdf,
                                       xpinyin.ins,
                                       xpinyin.lua,
                                       xpinyin.db,
                                       xpinyin-database.def and
                                       README.md.
\endpostamble

\declarepostamble\emptypostamble
\endpostamble

\def\MetaPrefix{-- }

\generate
  {
    \usedir{source/latex/xpinyin}
    \usepostamble\emptypostamble
    \file{xpinyin.lua} {\from{\jobname.dtx}{lua}}
  }

\let\MetaPrefix\DoubleperCent

%<install>\directlua
%<install>  {
%<install>    if not kpse.find_file("xpinyin.db") then
%<install>      dofile(kpse.find_file("xpinyin.lua"))
%<install>    end
%<install>  }
%<install>\generate
%<install>  {
%<install>    \usedir{tex/latex/xpinyin}
%<install>    \usepostamble\emptypostamble
%<install>    \file{xpinyin-database.def}
%<install>      {
%<install>        \from{\jobname.dtx} {database}
%<install>        \from{xpinyin.db}   {}
%<install>      }
%<install>  }

\generate
  {
%</install>
%<*internal>
    \usedir{source/latex/xpinyin}
    \file{xpinyin.ins} {\from{\jobname.dtx}{install}}
%</internal>
%<*install>
    \usedir{tex/latex/xpinyin}
    \file{xpinyin.sty} {\from{\jobname.dtx}{package}}
    \nopreamble\nopostamble
    \usedir{doc/latex/xpinyin}
    \file{README.md}   {\from{\jobname.dtx}{readme}}
  }

\endbatchfile
%</install>
%<*internal>
\fi
%</internal>
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\RequirePackage{expl3}
%<+package>\GetIdInfo$Id: xpinyin.dtx dd0cc20 2022-07-14 18:53:49 +0800 Qing Lee <sobenlee@gmail.com> $
%<package>  {Automatically add pinyin to Chinese characters}
%<package>\ProvidesExplPackage{\ExplFileName}
%<package>  {\ExplFileDate}{3.1}{\ExplFileDescription}
%<database>\ProvidesFile{xpinyin-database.def}%
%<database>  [2022/07/14 v3.1 xpinyin database]
%<*driver>
\documentclass{ctxdoc}
\usepackage{xpinyin}
\newfontfamily\PinYinFont{TeX Gyre Adventor}
\xpinyinsetup{font=\PinYinFont,multiple=\color{red}}
\begin{document}
  \DocInput{\jobname.dtx}
  \IndexLayout
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{695}
% \GetFileId{xpinyin.sty}
%
% \title{\bfseries\pkg{xpinyin} 宏包}
% \author{李清\\ \path{sobenlee@gmail.com}}
% \date{\filedate\qquad\fileversion\thanks{\ctexkitrev{\ExplFileVersion}.}}
% \maketitle
%
% \begin{documentation}
%
% \section{简介}
%
% \pkg{xpinyin} 是一个 \LaTeX 宏包，提供了为汉字自动注音的功能。
%
% \section{基本用法}
%
% \pkg{xpinyin} 支持采用 \texttt{GBK} 和 \texttt{UTF-8} 编码的 \TeX 源文件，建议
% 总是使用 \texttt{UTF-8}。如果使用 \LaTeX 或 \pdfLaTeX\ 的编译方式，则根据编码的
% 情况，\pkg{xpinyin} 依赖 \package{CJK} 或者 \package[cjk]{CJKutf8} 宏包。
% 如果使用 \XeLaTeX，则依赖 \package{xeCJK} 宏包。如果它们没有在 \pkg{xpinyin}
% 之前被载入，\pkg{xpinyin} 将根据编译方式自动选择，\LaTeX 或 \pdfLaTeX\ 将使用
% \pkg{CJKutf8}。
%
% \pkg{xpinyin} 还依赖 \package{l3kernel} 和 \package{l3packages}，使用 (pdf)\LaTeX
% 下的 \texttt{GBK} 编码时，还将依赖 \package{xCJK2uni}。
%
% 需要注意的是，\pkg{xpinyin} 缺省将拼音的字体设置为与文档的主字体（\tn{normalfont}）相同，
% 所以为了保证声调字母的正确输出，应该选用合适的西文主字体。也可以通过将在下一节介绍的
% \meta{font} 选项来单独设置拼音的字体。
%
% \begin{minipage}[t]{\dimexpr(\linewidth-\parindent-2em)/2\relax}
% \XeLaTeX 下的简单示例：
% \begin{verbatim}[frame=single,gobble=3]
%   \documentclass{article}
%   \usepackage{xeCJK}
%   \usepackage{xpinyin}
%   \setmainfont{CMU Serif}
%   \setCJKmainfont{SimSun}
%
%   \begin{document}
%   \xpinyin*{汉语拼音示例}
%   \end{document}
% \end{verbatim}
% \end{minipage}\qquad
% \begin{minipage}[t]{\dimexpr(\linewidth-\parindent-2em)/2\relax}
% (pdf)\LaTeX 下的简单示例：
% \begin{verbatim}[frame=single,gobble=3]
%   \documentclass{article}
%   \usepackage{CJKutf8}
%   \usepackage{xpinyin}
%   \usepackage[T1]{fontenc}
%   \usepackage{lmodern}
%
%   \begin{document}
%   \begin{CJK}{UTF8}{gbsn}
%   \xpinyin*{汉语拼音示例}
%   \end{CJK}
%   \end{document}
% \end{verbatim}
% \hrule height \dp\strutbox width 0pt \relax
% \end{minipage}
%
% 运行上述示例要求系统安装了设置的字体，源文件用 |UTF-8| 编码保存，使用相应的编译方式。
%
% \pkg{xpinyin} 可以与 \package{ctex} 宏包或文档类共同使用，使用方式与上面类似。
%
% \section{用户手册}
%
% \begin{function}{pinyinscope}
%   \begin{syntax}
%     \tn{begin}\{pinyinscope\}\oarg{options}
%     .....
%     \tn{end}\{pinyinscope\}
%   \end{syntax}
%   为 \env{pinyinscope} 环境中的汉字自动注音。例如
%   \begin{Example}[frame=single,numbers=left,gobble=5]
%     \begin{pinyinscope}
%     列位看官：你道此书从何而来？说起根由，虽近荒唐，细按则深有趣味。
%     待在下将此来历注明，方使阅者\xpinyin{了}{liao3}然不惑。
%     \end{pinyinscope}
%   \end{Example}
% \end{function}
%
%   可选项 \meta{options} 用于局部设置拼音的格式，将在下面说明。
%
% \begin{function}{\xpinyin}
%   \begin{syntax}
%     \tn{xpinyin} \oarg{options} \Arg{单个汉字} \Arg{拼音}
%     \tn{xpinyin*} \oarg{options} \Arg{文字}
%   \end{syntax}
%   对于多音字，可以使用 \tn{xpinyin} 为其设置拼音；而 \tn{xpinyin*} 相当于
%   \env{pinyinscope} 环境的命令形式。\tn{xpinyin} 可以在 \env{pinyinscope} 环境和
%   \tn{xpinyin*} 中使用。例如，
%   \begin{SideBySideExample}
%     \xpinyin{长}{chang2}\\
%     \xpinyin*{甄士隐梦幻识通灵}\\
%     \xpinyin*{\xpinyin{重}{zhong4}要}
%   \end{SideBySideExample}
% \end{function}
%
% \begin{function}{\pinyin}
%   \begin{syntax}
%     \tn{pinyin} \oarg{options} \Arg{拼音}
%   \end{syntax}
%   用于输出拼音，为了输入的方便 \texttt{\"u} 可以用 |v| 代替。例如，
%   \begin{SideBySideExample}
%     \pinyin{lv2zi}\\
%     \pinyin{nv3hai2zi}
%   \end{SideBySideExample}
% \end{function}
%
% \begin{function}{\setpinyin}
%   \begin{syntax}
%     \tn{setpinyin} \Arg{汉字} \Arg{拼音}
%   \end{syntax}
%   \pkg{xpinyin} 宏包的拼音数据（\file{xpinyin-database.def}）来源于 \texttt{Unicode}
%   的 \texttt{Unihan} 数据库\footnotemark 中的 \file{Unihan_Readings.txt} 文件。对于多
%   音字，一般来说这个文件选用的是常用读音。可以使用 \tn{setpinyin} 来设置多音字的首选读音。
% \end{function}
%
% \footnotetext{\url{http://www.unicode.org/Public/UNIDATA/Unihan.zip}}
%
% \begin{function}{\xpinyinsetup}
%   \begin{syntax}
%     \tn{xpinyinsetup} \{ <key_1>=<val_1>, <key_2>=<val_2>, ... \}
%   \end{syntax}
%   用于在导言区或文档中，设置拼音的格式。目前可以设置的 \meta{key} 如下介绍。
% \end{function}
%
% \begin{function}{ratio}
%   \begin{syntax}
%     ratio = \Arg{number}
%   \end{syntax}
%   设置拼音字体大小与当前正文字体大小的比例，缺省值是 |0.4|。
% \end{function}
%
% \begin{function}{vsep}
%   \begin{syntax}
%     vsep = \Arg{dimen}
%   \end{syntax}
%   设置拼音的基线与汉字基线的间距，缺省值是 |1 em|。
% \end{function}
%
% \begin{function}{hsep}
%   \begin{syntax}
%     hsep = \Arg{skip}
%   \end{syntax}
%   设置注音汉字之间的间距，缺省值与 \tn{CJKglue} 的值相同。为了断行时行末的对齐，设置
%   的 \meta{skip} 最后有一定的弹性。例如
%   \begin{Example}[frame=single,numbers=left,gobble=5]
%     \xpinyin*[ratio={.7},hsep={.5em plus .1em},vsep={1.1em}]{贾雨村风尘怀闺秀}
%   \end{Example}
% \end{function}
%
% \begin{function}{pysep}
%   \begin{syntax}
%     pysep = \Arg{glue}
%   \end{syntax}
%   设置 \tn{pinyin} 输出的相邻两个汉语拼音的空白，缺省值是一个空格。
% \end{function}
%
% \begin{function}{font}
%   \begin{syntax}
%     font = \Arg{font}
%   \end{syntax}
%   设置拼音的字体，缺省值是 \tn{normalfont}，即以正文西文字体相同。为了保证拼音能正确
%   输出，最好选用收字量较大的西文字体。
% \end{function}
%
% \begin{function}{format}
%   \begin{syntax}
%     format = \Arg{format}
%   \end{syntax}
%   设置拼音的其他格式，例如颜色等，缺省值为空。
% \end{function}
%
% \begin{function}{multiple}
%   \begin{syntax}
%     multiple = \Arg{format}
%   \end{syntax}
%   设置多音字拼音的其他格式，缺省值为空。可以通过这个选项来提醒校正多音字的拼音。
%   注意这个选项会覆盖 \opt{format} 设置的格式。例如本文档设置多音字拼音的颜色是
%   红色（需要载入 \pkg{color} 宏包）：
%   \begin{frameverb}
%   \xpinyinsetup{multiple={\color{red}}}
%   \end{frameverb}
% \end{function}
%
% \begin{function}[added=2014/01/12]{footnote}
%   \begin{syntax}
%     footnote = <\TFF>
%   \end{syntax}
%   是否对拼音环境中的脚注（\tn{footnote}）汉字加上拼音。缺省值为 \texttt{false}。
%   更一般的情况，请使用 \tn{disablepinyin}。
% \end{function}
%
% \begin{function}[added=2014/01/12]{\disablepinyin,\enablepinyin}
%   \tn{disablepinyin} 用于在拼音环境（\env{pinyinscope}）中临时取消对汉字的注音，而
%   \tn{enablepinyin} 用于其后的恢复。
% \end{function}
%
% \end{documentation}
%
% \StopEventually{}
%
% \begin{implementation}
%
% \section{代码实现}
%
%    \begin{macrocode}
%<*package>
%<@@=xpinyin>
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { xpinyin } { l3-too-old }
  {
    Support~package~'expl3'~too~old. \\\\
    Please~update~an~up~to~date~version~of~the~bundles\\\\
    'l3kernel'~and~'l3packages'\\\\
    using~your~TeX~package~manager~or~from~CTAN.
  }
\@ifpackagelater { expl3 } { 2019/03/05 } { }
  { \msg_error:nn { xpinyin } { l3-too-old } }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { xpinyin } { engine-not-supported }
  { Engine~`\c_sys_engine_str'~is~not~yet~supported,~xpinyin~will~abort! }
\bool_lazy_or:nnF
  { \sys_if_engine_xetex_p: }
  { \sys_if_engine_pdftex_p: }
  { \msg_critical:nn { xpinyin } { engine-not-supported } }
%    \end{macrocode}
%
%    \begin{macrocode}
\cs_if_exist:NF \NewDocumentCommand
  { \RequirePackage { xparse } }
%    \end{macrocode}
%
% \begin{variable}{\c_@@_tone_prop}
% 重音标记 |\`|、|\'| 和 |\=| 在 \env{tabbing} 环境中被移作他用，为避免错误，
% 我们使用内部命令 \tn{@tabacckludge} 或 \tn{a} 来定义。
%    \begin{macrocode}
\prop_const_from_keyval:Nn \c_@@_tone_prop
  {
    { ā } = { \@tabacckludge= a } ,
    { á } = { \@tabacckludge' a } ,
    { ǎ } = { \v a } ,
    { à } = { \@tabacckludge` a } ,
    { ō } = { \@tabacckludge= o } ,
    { ó } = { \@tabacckludge' o } ,
    { ǒ } = { \v o } ,
    { ò } = { \@tabacckludge` o } ,
    { ē } = { \@tabacckludge= e } ,
    { é } = { \@tabacckludge' e } ,
    { ě } = { \v e } ,
    { è } = { \@tabacckludge` e } ,
    { ū } = { \@tabacckludge= u } ,
    { ú } = { \@tabacckludge' u } ,
    { ǔ } = { \v u } ,
    { ù } = { \@tabacckludge` u } ,
    { ḿ } = { \@tabacckludge' m } ,
    { ń } = { \@tabacckludge' n } ,
    { ň } = { \v n } ,
    { ǹ } = { \@tabacckludge` n } ,
    { ī } = { \@tabacckludge= { \i } } ,
    { í } = { \@tabacckludge' { \i } } ,
    { ǐ } = { \v { \i } } ,
    { ì } = { \@tabacckludge` { \i } } ,
    { ü } = { \" u } ,
    { ǖ } = { \@tabacckludge= { \" u } } ,
    { ǘ } = { \@tabacckludge' { \" u } } ,
    { ǚ } = { \v { \" u } } ,
    { ǜ } = { \@tabacckludge` { \" u } }
  }
%    \end{macrocode}
% \end{variable}
%
%
% \begin{macro}{\@@_UTF_char:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_UTF_char:nn #1#2
  {
    \cs_if_exist:cF { u8:#1 }
      { \tl_const:cn { u8:#1 } {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_GBK_char:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_GBK_char:nn #1#2
  {
    \@@_UTF_char:nn {#1} {#2}
    \exp_args:Nx \@@_GBK_char_aux:nn { \tl_head:n {#1} } {#1}
  }
\cs_new_protected:Npn \@@_GBK_char_aux:nn #1#2
  { \exp_args:Nf \@@_GBK_char_aux:nnn { \int_eval:n { `#1 } } {#1} {#2} }
\cs_new_protected:Npn \@@_GBK_char_aux:nnn #1#2#3
  {
    \cs_if_exist:cF { @@_UTF_ #1 :w }
      {
        \exp_args:Nf \@@_GBK_char_def:nnn
          {
            \int_case:nn { \tl_count:n {#3} }
              {
                { 2 } { ##1 }
                { 3 } { ##1##2 }
                { 4 } { ##1##2##3 }
              }
          }
          {#1} {#2}
        \exp_args:Nc \@@_save_UTF_cs:Nn { @@_UTF_ #1 :w } {#1}
        \tl_gput_right:Nx \c_@@_reset_UTF_catcode_tl
          { \char_set_catcode:nn {#1} { \char_value_catcode:n {#1} } }
        \char_set_catcode_active:n {#1}
      }
  }
\cs_new_protected:Npn \@@_GBK_char_def:nnn #1#2#3
  {
    \cs_new_protected:cpn { @@_UTF_ #2 :w } #1
      { \use:c { u8: \tl_to_str:n { #3#1 } } }
  }
\tl_new:N \c_@@_reset_UTF_catcode_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_save_UTF_cs:Nn}
%    \begin{macrocode}
\group_begin:
\char_set_catcode_active:n { 126 }
\cs_new_protected:Npn \@@_save_UTF_cs:Nn #1#2
  {
    \group_begin:
    \char_set_lccode:nn { 126 } {#2}
    \tex_lowercase:D
      {
        \group_end:
        \tl_gput_right:Nn \c_@@_reset_UTF_cs_tl { \cs_set_eq:NN ~ #1 }
      }
  }
\group_end:
\tl_new:N \c_@@_reset_UTF_cs_tl
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\bool_new:N \g_@@_GBK_bool
\@ifpackageloaded { xeCJK }
  { \AtEndOfPackage { \@@_adjust_xeCJK_hook: } }
  {
    \@ifpackageloaded { CJKutf8 }
      {
        \prop_map_function:NN \c_@@_tone_prop \@@_UTF_char:nn
        \AtEndOfPackage { \@@_adjust_CJK_hook: }
      }
      {
        \@ifpackageloaded { CJK }
          {
            \RequirePackage { xCJK2uni }
            \prop_map_function:NN \c_@@_tone_prop \@@_GBK_char:nn
            \AtEndOfPackage
              {
                \tl_put_right:Nn \l_@@_pinyin_box_hook_tl
                  { \c_@@_reset_UTF_cs_tl }
                \@@_adjust_CJK_hook:
                \tl_use:N \c_@@_reset_UTF_catcode_tl
              }
            \bool_gset_true:N \g_@@_GBK_bool
          }
          {
            \sys_if_engine_xetex:TF
              {
                \RequirePackage { xeCJK }
                \AtEndOfPackage { \@@_adjust_xeCJK_hook: }
              }
              {
                \RequirePackage { CJKutf8 }
                \prop_map_function:NN \c_@@_tone_prop \@@_UTF_char:nn
                \AtEndOfPackage { \@@_adjust_CJK_hook: }
              }
          }
      }
  }
%    \end{macrocode}
%
% \begin{variable}{\l_@@_tmpa_box,\l_@@_tmpb_box}
%    \begin{macrocode}
\box_new:N \l_@@_tmpa_box
\box_new:N \l_@@_tmpb_box
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_width:Nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_width:Nn #1#2
  {
    \hbox_set:Nn \l_@@_tmpa_box {#2}
    #1 = \box_wd:N \l_@@_tmpa_box
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_make_pinyin_box:nnn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_make_pinyin_box:nnn #1#2#3
  {
    \@@_leavevmode:
    \hbox_overlap_right:n
      {
        \hbox_set:Nn \l_@@_tmpa_box
          { \@@_CJKsymbol_hook: \@@_save_CJKsymbol:n {#2} }
        \hbox_set:Nn \l_@@_tmpb_box
          {
            \l_@@_pinyin_box_hook_tl
            \@@_select_font:
            \l_@@_format_tl
            \clist_if_exist:cT { c_@@_multiple_ #1 _clist }
              { \l_@@_multiple_tl }
            {#3}
          }
        \dim_compare:nNnT
          { \box_wd:N \l_@@_tmpb_box } >
          { \box_wd:N \l_@@_tmpa_box + \l_@@_CJKglue_dim }
          {
            \box_resize_to_wd_and_ht:Nnn \l_@@_tmpb_box
              { \box_wd:N \l_@@_tmpa_box + \l_@@_CJKglue_dim }
              { \box_ht:N \l_@@_tmpb_box }
          }
        \box_move_up:nn { \l_@@_vsep_tl }
          {
            \hbox_to_wd:nn { \box_wd:N \l_@@_tmpa_box }
              { \tex_hss:D \box_use_drop:N \l_@@_tmpb_box \tex_hss:D }
          }
      }
  }
\tl_new:N \l_@@_pinyin_box_hook_tl
\sys_if_engine_pdftex:T
  {
    \tl_put_right:Nn \l_@@_pinyin_box_hook_tl
      { \cs_set_eq:NN \CJK@plane \tex_undefined:D }
  }
\cs_generate_variant:Nn \@@_make_pinyin_box:nnn { x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_CJKsymbol:n}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_CJKsymbol:n #1
  { \@@_CJKsymbol:xn { \@@_to_unicode:n {#1} } {#1} }
\cs_new_protected:Npn \@@_CJKsymbol:nn #1#2
  {
    \@@_make_pinyin_box:nnn {#1} {#2} { \use:c { c_@@_ #1 _tl } }
    \@@_save_CJKsymbol:n {#2}
  }
\cs_generate_variant:Nn \@@_CJKsymbol:nn { x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{pinyinscope}
%    \begin{macrocode}
\NewDocumentEnvironment { pinyinscope } { O { } }
  {
    \keys_set:nn { xpinyin } {#1}
    \enablepinyin
  }
  { }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xpinyin}
%    \begin{macrocode}
\NewDocumentCommand \xpinyin { s O { } m }
  {
    \mode_leave_vertical:
    \IfBooleanTF {#1}
      {
        \group_begin:
          \keys_set:nn { xpinyin } {#2}
          \enablepinyin
          #3
        \group_end:
      }
      {
        \group_begin:
          \keys_set:nn { xpinyin } {#2}
          \bool_if:NF \l_@@_enable_bool
            { \@@_width:Nn \l_@@_CJKglue_dim { \CJKglue } }
          \@@_single_aux:nn {#3}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_enable_bool}
%    \begin{macrocode}
\bool_new:N \l_@@_enable_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_CJKglue:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_CJKglue:
  { \skip_horizontal:n { \l_@@_hsep_tl } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\enablepinyin}
%    \begin{macrocode}
\NewDocumentCommand \enablepinyin { }
  {
    \bool_if:NF \l_@@_enable_bool
      {
        \tl_if_empty:NF \l_@@_hsep_tl
          {
            \cs_set_eq:NN \@@_save_CJKglue: \CJKglue
            \cs_set_eq:NN \CJKglue \@@_CJKglue:
          }
        \@@_width:Nn \l_@@_CJKglue_dim { \CJKglue }
        \@@_replace_CJKsymbol:
        \@@_restore_footnote:
        \bool_set_true:N \l_@@_enable_bool
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\disablepinyin}
%    \begin{macrocode}
\NewDocumentCommand \disablepinyin { }
  {
    \bool_if:NT \l_@@_enable_bool
      {
        \cs_if_eq:NNT \CJKglue \@@_CJKglue:
          { \cs_set_eq:NN \CJKglue \@@_save_CJKglue: }
        \@@_restore_CJKsymbol:
        \bool_set_false:N \l_@@_enable_bool
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_restore_footnote:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_restore_footnote:
  {
    \bool_if:NF \l_@@_footnote_bool
      { \tl_put_left:Nn \@parboxrestore { \l_@@_restore_footnote_tl } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_restore_footnote_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_restore_footnote_tl
\tl_set:Nn \l_@@_restore_footnote_tl
  {
    \int_compare:nNnT \tex_currentgrouptype:D = { 11 }
      { \disablepinyin }
  }
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_CJKglue_dim}
%    \begin{macrocode}
\dim_new:N \l_@@_CJKglue_dim
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_single_aux:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_single_aux:nn #1#2
  {
      \@@_replace_CJKsymbol_single:n {#2}
      #1
    \group_end:
  }
\cs_new_protected:Npn \@@_replace_CJKsymbol_single_aux:n #1
  {
    \bool_if:NF \l_@@_enable_bool { \@@_replace_CJKsymbol: }
    \cs_set_protected:Npn \CJKsymbol ##1
      { \@@_single_CJKsymbol:nn {##1} {#1} }
  }
\cs_new_protected:Npn \@@_single_CJKsymbol:nn #1#2
  {
    \@@_make_pinyin_box:xnn
      { \@@_to_unicode:n {#1} } {#1} { \@@_pinyin:n {#2} }
    \@@_save_CJKsymbol:n {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_replace_CJKsymbol_aux:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_replace_CJKsymbol_aux:
  {
    \cs_set_eq:NN \@@_save_CJKsymbol:n \CJKsymbol
    \cs_set_eq:NN \CJKsymbol \@@_CJKsymbol:n
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_restore_CJKsymbol_aux:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_restore_CJKsymbol_aux:
  { \cs_set_eq:NN \CJKsymbol \@@_save_CJKsymbol:n }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_select_font_xetex:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_select_font_xetex:
  {
    \cs_if_exist_use:cF { \l_@@_coor_tl }
      {
        \tl_set:Nx \l_@@_current_coor_tl { \l_@@_coor_tl }
        \@@_select_font_aux:
        \int_compare:nNnF { \tex_XeTeXfonttype:D \tex_font:D } = \c_zero_int
          {
            \exp_last_unbraced:NNV
            \cs_gset_eq:cN \l_@@_current_coor_tl \tex_font:D
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_select_font_aux:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_select_font_aux:
  {
    \fontsize
      { \l_@@_ratio_tl \tex_dimexpr:D \f@size pt \scan_stop: }
      { \f@baselineskip }
    \normalfont
    \l_@@_font_tl
    \selectfont
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_to_unicode_xetex:n}
%    \begin{macrocode}
\cs_new:Npn \@@_to_unicode_xetex:n #1
  { \int_to_arabic:n { `#1 } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_UTF_to_unicode:n,\@@_UTFchar_to_unicode:n}
%    \begin{macrocode}
\cs_new:Npn \@@_UTF_to_unicode:n #1
  {
    \int_to_arabic:n
      { \exp_args:No \int_from_hex:n { \CJK@plane } * "100 + #1 }
  }
\cs_new:Npn \@@_UTFchar_to_unicode:n #1
  { \int_to_arabic:n { \@@_UTF_viii_to_unicode:NNNw #1 \q_stop } }
\cs_new:Npn \@@_UTF_viii_to_unicode:NNNw #1#2#3#4 \q_stop
  {
    \tl_if_empty:nTF {#4}
      { ( `#1 - "E0 ) * "1000 + ( `#2 - "80 ) * "40 + ( `#3 - "80 ) }
      { ( `#1 - "F0 ) * "4000 + ( `#2 - "80 ) * "1000 + ( `#3 - "80 ) * "40 + ( `#4 - "80 ) }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_GBK_to_unicode:n,\@@_GBKchar_to_unicode:n}
%    \begin{macrocode}
\cs_new:Npn \@@_GBK_to_unicode:n
  { \CJKtu_sfd_map:nn { \CJK@plane } }
\cs_new:Npn \@@_GBKchar_to_unicode:n
  { \CJKtu_char_to_unicode:n }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_adjust_xeCJK_hook:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_adjust_xeCJK_hook:
  {
    \cs_new_eq:NN \@@_select_font:       \@@_select_font_xetex:
    \cs_new_eq:NN \@@_to_unicode:n       \@@_to_unicode_xetex:n
    \cs_new_eq:NN \@@_char_to_unicode:n  \@@_to_unicode:n
    \cs_new_eq:NN \@@_restore_CJKsymbol: \@@_restore_CJKsymbol_aux:
    \cs_new_eq:NN \@@_replace_CJKsymbol: \@@_replace_CJKsymbol_aux:
    \cs_new_eq:NN \@@_replace_CJKsymbol_single:n
                  \@@_replace_CJKsymbol_single_aux:n
    \tl_if_exist:NTF \l_xeCJK_current_font_tl
      {
        \tl_set:Nn \l_@@_coor_tl
          {
            ( \tl_to_str:N \l_@@_font_tl ) /
            \l_xeCJK_current_font_tl/\l_@@_ratio_tl
          }
      }
      {
        \tl_set:Nn \l_@@_coor_tl
          {
            ( \tl_to_str:N \l_@@_font_tl ) /
            \xeCJK@family/\f@series/\f@shape/\f@size/\l_@@_ratio_tl
          }
      }
    \cs_new_eq:NN \@@_leavevmode: \prg_do_nothing:
    \cs_new_protected:Npx \@@_CJKsymbol_hook:
      {
        \exp_not:N \makexeCJKinactive
        \cs_if_exist_use:NF \xeCJK_select_font:
          { \exp_not:N \xeCJK@setfont }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_adjust_CJK_hook:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_adjust_CJK_hook:
  {
    \bool_if:NTF \g_@@_GBK_bool
      {
        \cs_new_eq:NN \@@_to_unicode:n      \@@_GBK_to_unicode:n
        \cs_new_eq:NN \@@_char_to_unicode:n \@@_GBKchar_to_unicode:n
      }
      {
        \cs_new_eq:NN \@@_to_unicode:n      \@@_UTF_to_unicode:n
        \cs_new_eq:NN \@@_char_to_unicode:n \@@_UTFchar_to_unicode:n
      }
    \cs_new_eq:NN \@@_select_font:    \@@_select_font_aux:
    \cs_new_eq:NN \@@_leavevmode:     \mode_leave_vertical:
    \cs_new_eq:NN \@@_CJKsymbol_hook: \prg_do_nothing:
    \@ifpackageloaded { CJKpunct }
      { \@@_adjust_CJKpunct_hook: }
      {
        \cs_new_eq:NN \@@_restore_CJKsymbol: \@@_restore_CJKsymbol_aux:
        \cs_new_eq:NN \@@_replace_CJKsymbol: \@@_replace_CJKsymbol_aux:
        \cs_new_eq:NN \@@_replace_CJKsymbol_single:n
                      \@@_replace_CJKsymbol_single_aux:n
        \AtBeginDocument
          {
            \@ifpackageloaded { CJKpunct }
              {
                \cs_undefine:N \@@_restore_CJKsymbol:
                \cs_undefine:N \@@_replace_CJKsymbol:
                \cs_undefine:N \@@_replace_CJKsymbol_single:n
                \@@_adjust_CJKpunct_hook:
              } { }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_adjust_CJKpunct_hook:}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_adjust_CJKpunct_hook:
  {
    \cs_new_protected:Npn \@@_restore_CJKsymbol:
      {
        \int_compare:nNnTF { \CJKpunct@punctstyle } = { \CJKpunct@ps@plain }
          { \@@_restore_CJKsymbol_aux: }
          { \cs_set_eq:NN \CJKosymbol \@@_save_CJKsymbol:n }
      }
    \cs_new_protected:Npn \@@_replace_CJKsymbol:
      {
        \int_compare:nNnTF { \CJKpunct@punctstyle } = { \CJKpunct@ps@plain }
          { \@@_replace_CJKsymbol_aux: }
          {
            \cs_set_eq:NN \@@_save_CJKsymbol:n \CJKosymbol
            \cs_set_eq:NN \CJKosymbol \@@_CJKsymbol:n
          }
      }
    \cs_new_protected:Npn \@@_replace_CJKsymbol_single:n ##1
      {
        \int_compare:nNnTF { \CJKpunct@punctstyle } = { \CJKpunct@ps@plain }
          { \@@_replace_CJKsymbol_single_aux:n { ##1 } }
          {
            \bool_if:NF \l_@@_enable_bool
              { \cs_set_eq:NN \@@_save_CJKsymbol:n \CJKosymbol }
            \cs_set_protected:Npn \CJKosymbol ####1
              { \@@_single_CJKsymbol:nn { ####1 } { ##1 } }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\pinyin}
%    \begin{macrocode}
\NewDocumentCommand \pinyin { O { } m }
  {
    \group_begin:
    \keys_set:nn { xpinyin } {#1}
    \l_@@_font_tl
    \l_@@_format_tl { }
    \selectfont
    \c_@@_reset_UTF_cs_tl
    \@@_pinyin:n {#2}
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_pinyin:n}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_pinyin:n #1
  {
    \@@_pinyin_init:
    \bool_set_true:N \l_@@_first_bool
    \tl_set:Nn \l_@@_save_tl {#1}
    \@@_pinyin_aux:n #1 \q_recursion_tail \q_recursion_stop
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_pinyin_aux:n}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_pinyin_aux:n #1
  {
    \quark_if_recursion_tail_stop_do:nn {#1}
      {
        \bool_if:NTF \l_@@_first_bool { \l_@@_save_tl }
          { \tl_if_empty:NF \l_@@_item_tl { \l_@@_pysep_tl \l_@@_item_tl } }
      }
    \@@_if_number:nTF {#1}
      {
        \bool_if:NTF \l_@@_first_bool
          { \bool_set_false:N \l_@@_first_bool }
          { \l_@@_pysep_tl }
        \l_@@_pre_tl
        \@@_tone:Vn \l_@@_tone_tl {#1}
        \l_@@_post_tl
        \@@_pinyin_init:
      }
      {
        \int_compare:nNnTF
          { 0 \cs_if_exist_use:c { c_@@_ \tl_to_str:N \l_@@_tone_tl _tl } } >
          { 0 \cs_if_exist_use:c { c_@@_ \tl_to_str:n {#1} _tl } }
          { \tl_put_right:Nn \l_@@_post_tl {#1} }
          {
            \tl_set:Nn \l_@@_tone_tl {#1}
            \tl_set_eq:NN \l_@@_pre_tl \l_@@_item_tl
            \tl_clear:N \l_@@_post_tl
          }
        \tl_put_right:Nx \l_@@_item_tl { \@@_replace_v:n {#1} }
      }
    \@@_pinyin_aux:n
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tone:nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_tone:nn #1#2
  { \use:c { @@_num_to_tone_ #1 :Nn } {#1} {#2} }
\cs_generate_variant:Nn \@@_tone:nn { V }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_replace_v:n}
%    \begin{macrocode}
\cs_new:Npn \@@_replace_v:n #1
  {
    \str_if_eq:nnTF {#1} { v }
      {
        \str_case:onTF { \l_@@_item_tl }
          { { l } { } { n } { } { L } { } { N } { } }
          { \exp_not:n { ü } } { u }
      }
      { \exp_not:n {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_pinyin_init:}
%    \begin{macrocode}
\cs_new:Npn \@@_pinyin_init:
  {
    \tl_clear:N \l_@@_pre_tl   \tl_clear:N \l_@@_post_tl
    \tl_clear:N \l_@@_item_tl  \tl_clear:N \l_@@_tone_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_if_number:nTF}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_number:n #1 { TF }
  {
    \if_int_compare:w \c_one_int < 1 \tl_to_str:n {#1} \exp_stop_f:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_first_bool}
%    \begin{macrocode}
\bool_new:N \l_@@_first_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}
% {\c_@@_a_tl,\c_@@_o_tl,\c_@@_e_tl,\c_@@_i_tl,\c_@@_u_tl,\c_@@_v_tl}
%    \begin{macrocode}
\tl_const:Nn \c_@@_a_tl { 3 }
\tl_const:Nn \c_@@_o_tl { 2 }
\tl_const:Nn \c_@@_e_tl { 2 }
\tl_const:Nn \c_@@_i_tl { 1 }
\tl_const:Nn \c_@@_u_tl { 1 }
\tl_const:Nn \c_@@_v_tl { 1 }
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_num_to_tone:Nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_num_to_tone:Nn #1#2
  {
    \if_case:w \int_eval:n { #2 - 1 } \exp_stop_f:
      \= {#1}  \or: \'{#1}  \or: \v {#1}  \or: \` {#1}  \else: #1 \fi:
  }
\tl_map_inline:nn { a o e u }
  { \cs_new_eq:cN { @@_num_to_tone_ #1 :Nn } \@@_num_to_tone:Nn }
\cs_new:Npn \@@_num_to_tone_i:Nn #1#2
  {
    \if_case:w \int_eval:n { #2 - 1 } \exp_stop_f:
      ī  \or: í  \or: ǐ  \or: ì \else: i \fi:
  }
\cs_new_protected:Npn \@@_num_to_tone_v:Nn #1#2
  {
    \str_case:onTF { \l_@@_pre_tl }
      { { l } { }  { n } { }  { L } { } { N } { } }
      {
        \if_case:w \int_eval:n { #2 - 1 } \exp_stop_f:
          ǖ  \or: ǘ  \or: ǚ  \or: ǜ \else: ü \fi:
      }
      { \@@_num_to_tone:Nn u {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xpinyinsetup}
%    \begin{macrocode}
\NewDocumentCommand \xpinyinsetup { m } { \keys_set:nn { xpinyin } {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{ratio,vsep,hsep,pysep,font,format,multiple,footnote}
%    \begin{macrocode}
\clist_map_inline:nn
  { ratio , vsep , hsep , pysep , font , format , multiple }
  { \keys_define:nn { xpinyin } { #1 .tl_set:c = { l_@@_ #1 _tl } } }
\keys_define:nn { xpinyin }
  { footnote .bool_set:N = \l_@@_footnote_bool }
\keys_set:nn { xpinyin }
  {
    ratio   = .4 ,
    vsep    = 1 em ,
    pysep   = \c_space_tl ,
    font    = \normalfont ,
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\xpinyin_customary:nnn,\xpinyin_multiple:nnn}
%    \begin{macrocode}
\cs_new_protected:Npn \xpinyin_customary:nnn #1#2
  { \cs_gset_nopar:cpn { c_@@_ #2 _tl } }
\cs_new_protected:Npn \xpinyin_multiple:nnn #1#2
  { \cs_gset_nopar:cpn { c_@@_multiple_ #2 _clist } }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\group_begin:
  \cs_set_eq:NN \XPYU  \xpinyin_customary:nnn
  \cs_set_eq:NN \XPYUM \xpinyin_multiple:nnn
  \file_input:n { xpinyin-database.def }
\group_end:
%    \end{macrocode}
%
% \begin{macro}{\setpinyin}
%    \begin{macrocode}
\NewDocumentCommand \setpinyin { m m }
  {
    \tl_set:cn
      { c_@@_ \@@_char_to_unicode:n {#1} _tl }
      { \@@_pinyin:n {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\cs_if_exist:NTF \ProcessKeyOptions
  { \ProcessKeyOptions [ xpinyin ] }
  {
    \RequirePackage { l3keys2e }
    \ProcessKeysOptions { xpinyin }
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \section{\pkg{xpinyin.lua}}
%
%    \begin{macrocode}
%<*lua>
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin       = xpinyin or { }
local xpinyin = xpinyin
%    \end{macrocode}
%
% 计算时区\footnote{\url{http://lua-users.org/wiki/TimeZone}}。
%    \begin{macrocode}
xpinyin.tzoffset = "+0000"
do
  -- Compute the difference in seconds between local time and UTC.
  local function get_timezone()
    local now = os.time()
    return os.difftime(now, os.time(os.date("!*t", now)))
  end
  -- Return a timezone string in ISO 8601:2000 standard form (+hhmm or -hhmm)
  local function get_tzoffset(timezone)
    local h, m = math.modf(timezone / 3600)
    return string.format("%+.4d", 100 * h + 60 * m)
  end
  xpinyin.tzoffset = get_tzoffset(get_timezone())
end
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin = {
  uchar    = unicode.utf8.char,
  readings = { },
  fixreadings = {
%    \end{macrocode}
% 为汉字“〇”增加拼音。
%    \begin{macrocode}
    {"U+3007", "Mandarin", "líng"}
  },
%    \end{macrocode}
%
%    \begin{macrocode}
  database = {
    source  = "http://www.unicode.org/Public/UNIDATA/Unihan.zip",
    file    = "Unihan_Readings.txt",
    date    = "Date: 2014-05-09 18:17:02 GMT [JHJ]",
    version = "Unicode version: 7.0.0",
    dbfile  = "xpinyin.db"
  },
  preamble = [[
%<<COMMENT
%%
%%  Do not edit this file!
%%  Created from Unihan database:
%%
%%    $file
%%    $date
%%    $version
%%
%%  by "texlua xpinyin.lua" on ]]
           .. os.date("%Y-%m-%d %X ") .. xpinyin.tzoffset
           .. "\n%%"
%COMMENT
}
%    \end{macrocode}
%
%    \begin{macrocode}
local http_request = require("socket.http").request
local ltn12_sink_file = require("ltn12").sink.file
local zip_open = require("zip").open
%    \end{macrocode}
%
% 将 \file{Unihan_Readings.txt}^^A
% \footnote{\url{http://http://www.unicode.org/reports/tr38/}。} 保存到一张表里面。
%    \begin{macrocode}
function xpinyin.maketable (txt)
  local f = io.open(txt or xpinyin.database.file, "r")
  if not f then
    local source = xpinyin.database.source
    local zfilename = source:match("[^/]+$")
    local zfile = zip_open(zfilename)
    if not zfile then
      xpinyin.download(source, zfilename)
      zfile = assert(zip_open(zfilename))
    end
    f = assert(zfile:open(xpinyin.database.file))
    zfile:close()
  end
  local s, prop
  for line in f:lines() do
    s = line:explode("\t")
    if #s == 3 then
      prop = s[2]:sub(2)
      if prop == "Mandarin" or
         prop == "HanyuPinyin" or
         prop == "XHC1983" or
         prop == "HanyuPinlu" then
        xpinyin.insert(s[1], prop, s[3])
      end
    elseif line:find("Date") then
      xpinyin.database.date = line:match("^[#%s]*(.*)")
    elseif line:find("Unicode version:") then
      xpinyin.database.version = line:match("^[#%s]*(.*)")
    end
  end
  f:close()
  if xpinyin.fixreadings then
    for _, s in pairs(xpinyin.fixreadings) do
      xpinyin.insert(s[1], s[2], s[3])
    end
  end
end
%    \end{macrocode}
%
% 下载 \file{Unihan.zip}。
%    \begin{macrocode}
function xpinyin.download (source, zip)
  print("\nRetrieving Unihan Database from\n", source)
  local status, err = http_request{
    url  = source,
    sink = ltn12_sink_file(io.open(zip, "wb")) }
  if not status then
    error([[Download ']] .. zip .. [[' failed because of ]] .. err .. ".")
  end
end
%    \end{macrocode}
%
% 往拼音表中加入项目。
%    \begin{macrocode}
function xpinyin.insert (unicode, prop, value)
  local index = tonumber(unicode:match("%x+$"), 16)
  if not xpinyin.readings[index] then
    xpinyin.readings[index] = { }
  end
  xpinyin.readings[index][prop] = value
end
%    \end{macrocode}
%
% 输出需要的格式文件。
%    \begin{macrocode}
function xpinyin.output (db)
  local f = assert(io.open(db or xpinyin.database.dbfile, "w"))
  local preamble = xpinyin.preamble:gsub("%$(%w+)", xpinyin.database)
  f:write(preamble, "\n")
  local hanzi, pinyin
  local mt = { }
  for index, pyt in xpinyin.pairsByKeys(xpinyin.readings) do
    pinyin = assert(xpinyin.grep(pyt))
    hanzi = xpinyin.uchar(index)
    f:write("\\XPYU{", hanzi, "}{", index, "}{", pinyin, "}\n")
    pinyin = xpinyin.multiple(pyt)
    if pinyin then
      mt[#mt + 1] = "\\XPYUM{" .. hanzi .. "}{" .. index .. "}{" .. pinyin .."}"
    end
  end
  f:write(table.concat(mt, "\n"), "\n")
  f:close()
end
%    \end{macrocode}
%
% 将表按照索引排序，代码来源于
% \href{http://www.lua.org/pil/19.3.html}{\textit{Programming in Lua}}。
%    \begin{macrocode}
function xpinyin.pairsByKeys (t, f)
  local a = { }
  for n in pairs(t) do a[#a + 1] = n end
  table.sort(a, f)
  local i = 0 -- iterator variable
  return function () -- iterator function
    i = i + 1
    return a[i], t[a[i]]
  end
end
%    \end{macrocode}
%
% 按照 \texttt{Mandarin}、\texttt{XHC1983}、\texttt{HanyuPinyin}
% 的顺序选择最常用的拼音。\texttt{HanyuPinlu} 的质量较差，不采用。
%    \begin{macrocode}
function xpinyin.grep (pyt)
  if pyt.Mandarin then
    return pyt.Mandarin:match("%S+"), "Mandarin"
  elseif pyt.XHC1983 then
    return pyt.XHC1983:match(":(%S+)"), "XHC1983"
  elseif pyt.HanyuPinyin then
    return pyt.HanyuPinyin:match(":([^,%s]+)"), "HanyuPinyin"
  end
end
%    \end{macrocode}
%
% 根据 \texttt{XHC1983} 和 \texttt{HanyuPinyin} 选出多音字。
%    \begin{macrocode}
function xpinyin.multiple (pyt)
  if pyt.XHC1983 then
    local s = pyt.XHC1983:explode()
    if s[2] then
      local t = { }
      for i, v in ipairs(s) do
        t[#t + 1] = v:explode(":")[2]
      end
      return xpinyin.unique(t), "XHC1983"
    end
  elseif pyt.HanyuPinyin and pyt.HanyuPinyin:find("%D,") then
    local t = { }
    for _, v in ipairs(pyt.HanyuPinyin:explode()) do
      for _, py in ipairs(v:explode(":")[2]:explode(",")) do
        t[#t + 1] = py
      end
    end
    return xpinyin.unique(t), "HanyuPinyin"
  end
end
%    \end{macrocode}
%
% 删除掉数组中的重复元素。
%    \begin{macrocode}
function xpinyin.unique (t)
  local rt = xpinyin.remove_duplicate(t)
  if #rt > 1 then
    return table.concat(rt, ",")
  end
end
function xpinyin.remove_duplicate (t)
  local ht = { }
  local nt = { }
  for i, v in ipairs(t) do
    if not ht[v] then
      nt[#nt + 1] = v
      ht[v] = true
    end
  end
  return nt
end
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin.maketable()
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin.output()
%    \end{macrocode}
%
%    \begin{macrocode}
%</lua>
%    \end{macrocode}
%
% \end{implementation}
%
% \Finale
%
\endinput
