3

I would like to make a hanzi grid for practicing Chinese characters, like this one: http://www.hanzigrids.com/version/1328584298/example-pdfs/hanzigrids-yingbikaishu.pdf

I am able to make the table with the arydshln package, like this:

\begin{tabular}{|c:c|c:c|c:c|c:c|}
  \hline
  & & & & & & & \\
  \hdashline
  & & & & & & & \\
  \hline
  & & & & & & & \\
  \hdashline
  & & & & & & & \\
  \hline
\end{tabular}

but how can I write the characters over the table?

1

This answer is inspired by qingkuan@bbs.ctex.org.

When I saw your question yesterday, three possible solutions came into my mind. They are:

  • Select a font whose chars has a rice-shaped grid (米字格).
  • Use LaTeX tabular to draw these grids, and then fill Chinese chars into them.
  • Get the position and size of each Chinese chars, and then, draw the grids with TikZ.

The first possible solution is direct but lack of portability, and the second one is not a good choice if one wish to make line-breaking automatically (also page-breaking). Hence, the last one seems to be the only choice.

The package soul insert things after every possible hyphenation (see here), which could help us finding the line-break point. However, the package soul is sometimes fragile, since it cannot work well with the package xeCJK which is an important Chinese supporting package.

Qingkuan@bbs.ctex.org helped me replenishing the already-off trail.

Testing every chars in .tex source file, xeCJK is able to figure out if a specific char is a CJK (Chinese, Japanese and Korean) char. Hence, we are now able to get the position of every Chinese char's position.

Output

Let's see the output first.

enter image description here

Requirements

  • Encoding: UTF-8
  • Engine: XeTeX (start command line with xelatex)
  • Packages: xeCJK (later than v3.2.8, 2013/12/05), ctex
  • Font: Adobe Chinese-simp fonts (click here to start downloading automatically, these four fonts are free, don't worry about the copyright)

Code

%!TEX program = xelatex
\documentclass[UTF8, adobefonts]{ctexart}

\usepackage{tikz}
\usepackage{xcolor}
\usepackage{geometry}
\geometry{margin = 2.1cm, papersize = {16cm, 13cm}}
\pagestyle{empty}
\linespread{0.833333}

\makeatletter
\ExplSyntaxOn
\box_new:N \l_@@_grid_box % l means local
\coffin_new:N \l_@@_grid_coffin
\coffin_new:N \l_@@_charater_coffin
\int_const:Nn \c_@@_test_char { "4E00 }
\cs_new_protected_nopar:Npn \@@_update_grid_box:
  {
    \hbox_set:Nn \l_@@_grid_box
      { \XeTeXuseglyphmetrics = \c_zero \c_@@_test_char }
    \use:x
      {
        \@@_update_grid_box:nnn
          { \dim_use:N \box_wd:N \l_@@_grid_box } % width
          { \dim_use:N \box_ht:N \l_@@_grid_box } % height
          { \dim_use:N \box_dp:N \l_@@_grid_box } % depth
      }
    \coffin_attach:NnnNnnnn
      \l_@@_charater_coffin { hc } { vc }
      \l_@@_grid_coffin     { hc } { vc }
      { \c_zero_dim } { \c_zero_dim }
    \hbox_set:Nn \l_@@_grid_box
      {
        \coffin_typeset:Nnnnn \l_@@_charater_coffin
          { H } { l } { \c_zero_dim } { \c_zero_dim }
      }
    \box_set_wd:Nn \l_@@_grid_box { \c_zero_dim }
    \box_set_ht:Nn \l_@@_grid_box { \c_zero_dim }
    \box_set_dp:Nn \l_@@_grid_box { \c_zero_dim }
  }
\cs_new_protected_nopar:Npn \@@_update_grid_box:nnn #1#2#3
  {
    \hbox_set:Nn \l_@@_grid_box { }
    \box_set_wd:Nn \l_@@_grid_box {#1}
    \box_set_ht:Nn \l_@@_grid_box {#2}
    \box_set_dp:Nn \l_@@_grid_box {#3}
    \hcoffin_set:Nn \l_@@_charater_coffin { \box_use:N \l_@@_grid_box }
    \@@_draw_grid:nn {#1} { (#2) + (#3) }
  }
\cs_new_protected_nopar:Npn \@@_draw_grid:nn #1#2
  {
    \use:x
      {
        \@@_draw_grid:nnnn { \dim_eval:n {#1} } { \dim_eval:n {#2} }
          { \dim_eval:n { (#1) / 2 } } { \dim_eval:n { (#2) / 2 } }
      }
  }
\cs_new_protected_nopar:Npn \@@_draw_grid:nnnn #1#2#3#4
  {
    \hcoffin_set:Nn \l_@@_grid_coffin
      {
        \begin{tikzpicture}
          \draw[help~lines] (0,#4) -- (#1,#4) (#3,0) -- (#3,#2);
          \draw[help~lines,dashed] (0,0) -- (#1,#2) (0,#2) -- (#1,0);
          \draw[help~lines, red, thick] (0,0) rectangle (#1,#2);
        \end{tikzpicture}
      }
  }
\cs_new_protected_nopar:Npn \@@_grid_CJKsymbol:n
  { \box_use:N \l_@@_grid_box \@@_grid_CJKsymbol:n }
\cs_new_protected_nopar:Npn \@@_grid_CJKpunctsymbol:n
  { \box_use:N \l_@@_grid_box \@@_grid_CJKpunctsymbol:n }
\keys_define:nn { @@ }
  {
    format .tl_set:N  = \l_@@_formal_tl ,
    format .initial:n = { \normalfont }
  }
\cs_new_protected:Npn \@@_active_grid:n #1
  {
    \xeCJKsetup{PunctStyle=plain,CJKglue=\allowbreak,AllowBreakBetweenPuncts}
    \keys_set:nn { @@ } {#1}
    \tl_use:N \l_@@_formal_tl
    \@@_update_grid_box:
    \xeCJK_swap_cs:NN \CJKsymbol \@@_grid_CJKsymbol:n
    \xeCJK_swap_cs:NN \CJKpunctsymbol \@@_grid_CJKpunctsymbol:n
    \__xeCJK_add_to_shipout:n
      {
        \xeCJK_swap_cs:NN \CJKsymbol \@@_grid_CJKsymbol:n
        \xeCJK_swap_cs:NN \CJKpunctsymbol \@@_grid_CJKpunctsymbol:n
      }
  }
\NewDocumentCommand \CJKgrid { +O { } +m }
  { \group_begin: \@@_active_grid:n {#1} #2 \relax \group_end: }
\NewDocumentEnvironment { CJKGrid } { +O { } }
  { \@@_active_grid:n {#1} } { \par }
\ExplSyntaxOff

\begin{document}
\setlength{\parindent}{0pt}


\zihao{0}
\CJKgrid{\kaishu 我\color{gray!20}我我我我我我\color{white}我}

\CJKgrid{\kaishu 喜\color{gray!20}喜喜喜喜喜喜\color{white}喜}

\CJKgrid{\kaishu 歡\color{gray!20}歡歡歡歡歡歡\color{white}歡}

\CJKgrid{\kaishu 寫\color{gray!20}寫寫寫寫寫寫\color{white}寫}

\CJKgrid{\kaishu 漢\color{gray!20}漢漢漢漢漢漢\color{white}漢}

\CJKgrid{\kaishu 字\color{gray!20}字字字字字字\color{white}字}


\end{document}

EDIT

The Adobe fonts is not really necessary, since they are used here for displaying. One can replace the option adobefonts by nofonts, and then reset fonts by control sequences provided by xeCJK (See its documentation for details).

Not the answer you're looking for? Browse other questions tagged or ask your own question.