大致思路
竖直排版在 upTeX
系列的编译引擎中是天然支持的,而在 xelatex/pdflatex
中却不见得那么容易。由于汉字字形优美,方方正正的,所以断行分页算法都相对较容易实现。为此打算使用 tikz
来实现,步骤如下:
- 首先是将所有需要排版的文字都存到一个 token 或者其他数据类型中
- 每一个文字应该对应一个坐标,假设我们准备以 的布局来排版,那么第 8 个文字的坐标应该是 ,第 16 个文字的坐标应该是
- 每一个点应该映射到页面上唯一的坐标
- 遍历每一个汉字,然后完成渲染
- 处理标点符号
- ...
页面框线
使用下面代码可以在每一页都绘制好框线
latex
\AddToHook{shipout/background}{\put(1in, -1in){%
\begin{tikzpicture}[remember picture, overlay]
\draw[line width = 2pt] ([shift = {(1.8, -1.8)}]current page.north west) rectangle ([shift = {(-1.8, 1.8)}]current page.south east);
\draw[thick] ([shift = {(2, -2)}]current page.north west) rectangle ([shift = {(-2, 2)}]current page.south east);
\foreach \x in {0.1, 0.2, ..., 0.9} {
\draw[thick] ([shift = {(2cm + \x*\textwidth, -2cm)}]current page.north west) --++ (0, -\textheight);
}
\end{tikzpicture}
}}
token 列表
latex3
\NewDocumentEnvironment{page}{+b}{
\titlepage
\tl_set:Nn \l__words_tl {#1}
}
{
\endtitlepage
}
使用
latex
\begin{page}
那甄家丫鬟擷了花,方欲走時,猛抬頭見窗內有人,敝巾舊服,雖是貧窘,然生得腰圓背厚,麵闊口方,更兼劍眉星眼,直鼻權腮。這丫鬟忙轉身回避,心下乃想,這人生的這樣雄壯,卻又這樣襤褸,想他定是我家主人常説的什麼賈雨村了,每有意幫助週濟,隻是沒甚機會。我家並無這樣貧窘親友,想定是此人無疑了。
\end{page}
就可以将
latex
那甄家丫鬟擷了花,方欲走時,猛抬頭見窗內有人,敝巾舊服,雖是貧窘,然生得腰圓背厚,麵闊口方,更兼劍眉星眼,直鼻權腮。這丫鬟忙轉身回避,心下乃想,這人生的這樣雄壯,卻又這樣襤褸,想他定是我家主人常説的什麼賈雨村了,每有意幫助週濟,隻是沒甚機會。我家並無這樣貧窘親友,想定是此人無疑了。
存到 \l__words_tl
这个变量里面了
文字坐标
latex3
\cs_new_protected:Npn \__get_next_coordinate:NN #1#2
{
\int_incr:N \l_tmpb_int
\int_compare:nT {\l_tmpb_int > 12} {
\int_zero:N \l_tmpb_int
\int_incr:N \l_tmpa_int
}
\int_set_eq:NN #1 \l_tmpa_int
\int_set_eq:NN #2 \l_tmpb_int
}
使用这个函数将会得到下一个文字的坐标,例如
latex3
\__get_next_coordinate:NN \l__tmp_x_int \l__tmp_y_int
将会得到
latex3
\l__tmp_x_int = 0, \l__tmp_y_int = 0
再次使用
latex3
\__get_next_coordinate:NN \l__tmp_x_int \l__tmp_y_int
将会得到
latex3
\l__tmp_x_int = 0, \l__tmp_y_int = 1
坐标映射
latex3
\cs_new_protected:Npn \__eval_position_with_page:NNNN #1#2#3#4
{
\dim_set:Nn #3 { -2cm - 0.05\textwidth - #1\textwidth / 10 }
\dim_set:Nn #4 { -2cm - 0.08\textwidth - #2\textwidth / 10 }
}
这个函数接受四个参数,前两个是输入坐标,后两个是返回的映射坐标(实际上的页面右上角的偏移向量)
遍历汉字
latex3
\int_step_inline:nn { \tl_count:N \l__words_tl }
{
\exp_args:NNx \tl_if_in:NnTF \g__ignore_punct_tl {\tl_item:Nn \l__words_tl {##1}}
{
\dim_add:Nn \l__tmp_x_dim {0.025\textwidth}
\dim_add:Nn \l__tmp_y_dim {-0.025\textwidth}
\tikz[remember~picture, overlay]
\node[scale = 2, font = \kaishu, text = red] at ([shift = {
(\dim_use:N \l__tmp_x_dim, \dim_use:N \l__tmp_y_dim)
}]current~page.north~east) {\tl_item:Nn \l__words_tl {##1}};
}
{
\__eval_position_with_page:NNNN \l__tmp_x_int \l__tmp_y_int \l__tmp_x_dim \l__tmp_y_dim
\int_log:N \l__tmp_y_int
\tikz[remember~picture, overlay]
\node[scale = 2, font = \kaishu] at ([shift = {
(\dim_use:N \l__tmp_x_dim, \dim_use:N \l__tmp_y_dim)
}]current~page.north~east) {\tl_item:Nn \l__words_tl {##1}};
\__get_last_coordinate:NN \l__tmp_x_int \l__tmp_y_int
}
}
这一行是处理标点符号的分支
latex3
\exp_args:NNx \tl_if_in:NnTF \g__ignore_punct_tl {\tl_item:Nn \l__words_tl {##1}}
其它基本上就没啥了.
完整实现
latex3
\documentclass{ctexart}
\usepackage[margin = 2cm]{geometry}
\pagestyle{empty}
\usepackage{tikz}
\AddToHook{shipout/background}{\put(1in, -1in){%
\begin{tikzpicture}[remember picture, overlay]
\draw[line width = 2pt] ([shift = {(1.8, -1.8)}]current page.north west) rectangle ([shift = {(-1.8, 1.8)}]current page.south east);
\draw[thick] ([shift = {(2, -2)}]current page.north west) rectangle ([shift = {(-2, 2)}]current page.south east);
\foreach \x in {0.1, 0.2, ..., 0.9} {
\draw[thick] ([shift = {(2cm + \x*\textwidth, -2cm)}]current page.north west) --++ (0, -\textheight);
}
\end{tikzpicture}
}}
\ExplSyntaxOn
\tl_new:N \l__words_tl
\int_new:N \l__tmp_x_int
\int_new:N \l__tmp_y_int
\dim_new:N \l__tmp_x_dim
\dim_new:N \l__tmp_y_dim
\tl_new:N \g__ignore_punct_tl
\tl_gset:Nn \g__ignore_punct_tl {,。}
\NewDocumentCommand{\ignorepunct}{m}{\tl_gset:Nn \g__ignore_punct_tl{#1}}
\cs_new_protected:Npn \__get_next_coordinate:NN #1#2 {
\int_incr:N \l_tmpb_int
\int_compare:nT {\l_tmpb_int > 12} {
\int_zero:N \l_tmpb_int
\int_incr:N \l_tmpa_int
}
\int_set_eq:NN #1 \l_tmpa_int
\int_set_eq:NN #2 \l_tmpb_int
}
\cs_new_protected:Npn \__eval_position_with_page:NNNN #1#2#3#4 {
\dim_set:Nn #3 { -2cm - 0.05\textwidth - #1\textwidth / 10 }
\dim_set:Nn #4 { -2cm - 0.08\textwidth - #2\textwidth / 10 }
}
\NewDocumentEnvironment{page}{+b}{
\titlepage
\tl_set:Nn \l__words_tl {#1}
\int_step_inline:nn { \tl_count:N \l__words_tl }
{
\exp_args:NNx \tl_if_in:NnTF \g__ignore_punct_tl {\tl_item:Nn \l__words_tl {##1}}
{
\dim_add:Nn \l__tmp_x_dim {0.025\textwidth}
\dim_add:Nn \l__tmp_y_dim {-0.025\textwidth}
\tikz[remember~picture, overlay]
\node[scale = 2, font = \twkaiti, text = red] at ([shift = {
(\dim_use:N \l__tmp_x_dim, \dim_use:N \l__tmp_y_dim)
}]current~page.north~east) {\tl_item:Nn \l__words_tl {##1}};
}
{
\__eval_position_with_page:NNNN \l__tmp_x_int \l__tmp_y_int \l__tmp_x_dim \l__tmp_y_dim
\int_log:N \l__tmp_y_int
\tikz[remember~picture, overlay]
\node[scale = 2, font = \twkaiti] at ([shift = {
(\dim_use:N \l__tmp_x_dim, \dim_use:N \l__tmp_y_dim)
}]current~page.north~east) {\tl_item:Nn \l__words_tl {##1}};
\__get_next_coordinate:NN \l__tmp_x_int \l__tmp_y_int
}
}
}
{
\endtitlepage
}
\ExplSyntaxOff
\begin{document}
\ignorepunct{,。}
\begin{page}
那甄家丫鬟擷了花,方欲走時,猛抬頭見窗內有人,敝巾舊服,雖是貧窘,然生得腰圓背厚,麵闊口方,更兼劍眉星眼,直鼻權腮。這丫鬟忙轉身回避,心下乃想,這人生的這樣雄壯,卻又這樣襤褸,想他定是我家主人常説的什麼賈雨村了,每有意幫助週濟,隻是沒甚機會。我家並無這樣貧窘親友,想定是此人無疑了。
\end{page}
\begin{page}
須臾茶畢,早已設下盃盤,那美酒佳餚自不必説。二人歸坐,先是款斟漫飲,次漸談至興濃,不覺飛觥限斝起來。當時街坊上家家簫管,戶戶絃歌,當頭一輪明月,飛彩凝輝,二人愈添豪興,酒到盃幹。雨村此時已有七八分酒意,狂興不禁,乃對月寓懷,口號一絶雲。時逢三五便團圓,滿把晴光護玉欄。陋室空堂,當年笏滿床,衰草枯楊。
\end{page}
\end{document}