
鞭打着ai搞出来个用latex生成带格子的作文的代码,但是一些细节上经不起推敲,比如标点符号的断点,两个中文标点符号连续使用时应该放一个格子中。比如中英文、中文数字混排时需要让两个英文、数字占一个格子。请问该如何修改呢。ai似乎能力到这里了。
% !TeX program = xelatex
% =====================================================================
% 小学作文稿纸「套打」模板(双面稿纸,奇偶页镜像)
% 实测:纸 210×297;格 7×8mm;空行 2.48mm;
% 装订侧 20.5 / 上 24 / 外侧 14.5 / 下 19 mm;25 格 × 24 行
% 编译:xelatex zuowen.tex (需编译两遍,remember picture 才生效)
% =====================================================================
\documentclass[twoside]{article} % 恢复 twoside:奇偶页镜像
\usepackage{fontspec}
\usepackage{xeCJK}
\usepackage{geometry}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{eso-pic}
\usepackage{ocgx2}
% ==================== 1. 字体 ====================
\setCJKmainfont{SimSun}
\setmainfont{Source Han Serif SC}
% ==================== 2. 稿纸参数(实测值)====================
\newcommand{\cellWmm}{7} % 格子宽
\newcommand{\cellHmm}{8} % 格子高
\newcommand{\gapVmm}{2.48} % 空行高
\newcommand{\marginBindingMm}{20.5} % 装订侧:网格 → 纸边(奇数页=左侧,偶数页=右侧)
\newcommand{\marginOuterMm}{14.5} % 外侧:网格 → 纸边
% 自洽验证:20.5 + 25×7 + 14.5 = 210 ✓
\newcommand{\marginTopMm}{24}
\newcommand{\marginBottomMm}{19}
% 自洽验证:24 + 24×8 + 25×2.48 + 19 = 297 ✓
\newcommand{\cols}{25}
\newcommand{\rows}{24}
% ==================== 3. 推导(不改)====================
\newlength{\cellW} \setlength{\cellW}{\cellWmm mm}
\newlength{\cellH} \setlength{\cellH}{\cellHmm mm}
\newlength{\gapV} \setlength{\gapV}{\gapVmm mm}
\newlength{\marginTop} \setlength{\marginTop}{\marginTopMm mm}
\newlength{\marginBottom}\setlength{\marginBottom}{\marginBottomMm mm}
\newlength{\marginBinding}\setlength{\marginBinding}{\marginBindingMm mm}
\newlength{\rowPitch} \setlength{\rowPitch}{\dimexpr\cellH+\gapV\relax}
\fontsize{\cellW}{\rowPitch}\selectfont
\newlength{\baselineInCell}
\setlength{\baselineInCell}{\dimexpr0.5\cellH+0.38\cellW\relax}
\newlength{\firstLineHeight}
\setbox0=\hbox{口}
\setlength{\firstLineHeight}{\ht0}
% twoside:inner = 装订侧 20.5mm(奇数页在左、偶数页在右,几何自动镜像)
\geometry{
paperwidth=210mm,
paperheight=297mm,
top=\dimexpr\marginTop+\gapV-\firstLineHeight+\baselineInCell\relax,
bottom=\marginBottom,
inner=\marginBinding, % 装订侧 20.5mm
textwidth=\dimexpr\cols\cellW\relax, % 175mm;外侧自动 = 210-20.5-175 = 14.5mm ✓
}
\xeCJKsetup{CJKglue={\hskip 0pt plus 0pt minus 0pt}}
\xeCJKsetup{PunctStyle = plain}
\ExplSyntaxOn
\cs_set_protected:Npn \xeCJK_no_break: { \tex_penalty:D \c_zero_int }
\cs_set_protected:Npn \__xeCJK_nobreak_zero_glue: { \skip_horizontal:N \c_zero_skip }
\ExplSyntaxOff
\setlength{\topskip}{0pt}
\setlength{\parskip}{0pt}
\setlength{\parindent}{2\cellW}
\AtBeginDocument{\fontsize{\cellW}{\rowPitch}\selectfont}
% ==================== 4. 网格(非打印图层)====================
% 结构:外框两条竖线贯穿全高;水平线画出每行上下边;内部竖线只画在
% 每行的格子高度内,不穿过首行前/末行后/行间的空行 —— 空行由
% 上下横线 + 左右外框围成封闭横条,中间无竖线。
\newcommand{\drawgrid}{%
\ifodd\value{page}%
\def\gridXmm{\marginBindingMm}% 奇数页:网格距左边缘 20.5mm
\else%
\def\gridXmm{\marginOuterMm}% 偶数页:网格距左边缘 14.5mm
\fi%
\pgfmathsetmacro{\gridH}{\cellHmm*\rows + \gapVmm*(\rows+1)}%
\pgfmathsetmacro{\gridW}{\cellWmm*\cols}%
\begin{tikzpicture}[remember picture, overlay, gray, line width=0.3pt]
% --- 外框:左右两条竖线贯穿整个网格高度(封闭所有空行)---
\draw ($(current page.north west)+(\gridXmm mm, -\marginTopMm mm)$) -- ++(0, -\gridH mm);
\pgfmathsetmacro{\gxR}{\gridXmm + \gridW}%
\draw ($(current page.north west)+(\gxR mm, -\marginTopMm mm)$) -- ++(0, -\gridH mm);
% ★★★ 在这里补充:外框的顶边和底边(形成完整矩形)★★★
\draw ($(current page.north west)+(\gridXmm mm, -\marginTopMm mm)$) -- ++(\gridW mm, 0);
\draw ($(current page.north west)+(\gridXmm mm, -\marginTopMm mm - \gridH mm)$) -- ++(\gridW mm, 0);
% --- 水平行线:每行的上边线和下边线 ---
\foreach \i in {0,...,\numexpr\rows-1\relax} {
\pgfmathsetmacro{\gy}{\marginTopMm + \gapVmm + \i*(\cellHmm+\gapVmm)}%
\draw ($(current page.north west)+(\gridXmm mm, -\gy mm)$) -- ++(\gridW mm, 0);
\draw ($(current page.north west)+(\gridXmm mm, -\gy mm - \cellHmm mm)$) -- ++(\gridW mm, 0);
}
% --- 内部竖线:只画在每行格子内(不穿过空行)---
\foreach \i in {0,...,\numexpr\rows-1\relax} {
\pgfmathsetmacro{\gyT}{\marginTopMm + \gapVmm + \i*(\cellHmm+\gapVmm)}%
\foreach \j in {1,...,\numexpr\cols-1\relax} {
\pgfmathsetmacro{\gx}{\gridXmm + \j*\cellWmm}%
\draw ($(current page.north west)+(\gx mm, -\gyT mm)$) -- ++(0, -\cellHmm mm);
}
}
\end{tikzpicture}%
}
\newif\ifshowgrid
\showgridfalse % 校准时打开;套打正式版改为 \showgridfalse
\AddToShipoutPictureBG{%
\begin{ocg}[printocg=never]{网格}{grid}{1}%
\drawgrid
\end{ocg}%
}
% ==================== 5. 半角字符占一格 ====================
\ExplSyntaxOn
\NewDocumentCommand{\fullwidth}{m}
{ \tl_map_inline:nn {#1} { \makebox[\cellW][c]{##1} } }
\ExplSyntaxOff
% ==================== 6. 校准页(奇偶各一页)====================
\newcommand{\calibrationpage}{%
\begingroup
\parindent=0pt \parskip=0pt
\foreach \r in {1,...,\rows}{%
\noindent
\foreach \c in {1,...,\cols}{\makebox[\cellW][c]{口}}%
\par
}%
\endgroup
}
% ==================== 7. 标题 / 称呼 / 落款 / 日期 ====================
% 每个字符(含半角数字、字母、标点)一律装入一格;
% 标题居中、称呼顶格、落款/日期右对齐,均按整格计算,不错格。
\ExplSyntaxOn
\int_new:N \l_zw_n_int
\int_new:N \l_zw_lead_int % 前导空格数(整数格)
% 前导空格:floor((25-n)/2) 格(标题居中)
\cs_new_protected:Npn \zw_lead_center:n #1
{
\int_set:Nn \l_zw_n_int { \tl_count:n {#1} }
% expl3 整数表达式中 / 即截断除法;n 为奇数时向左偏半格
\int_set:Nn \l_zw_lead_int { ( 25 - \l_zw_n_int ) / 2 }
\skip_horizontal:n { \dimexpr \cellW * \l_zw_lead_int \relax }
}
% 前导空格:25-n 格(右对齐,末字落在第 25 格右缘)
\cs_new_protected:Npn \zw_lead_right:n #1
{
\int_set:Nn \l_zw_n_int { \tl_count:n {#1} }
\int_set:Nn \l_zw_lead_int { 25 - \l_zw_n_int }
\skip_horizontal:n { \dimexpr \cellW * \l_zw_lead_int \relax }
}
% 每字符占一格
\cs_new_protected:Npn \zw_cells:n #1
{ \tl_map_inline:nn {#1} { \makebox[\cellW][c]{##1} } }
% 标题:居中,独占一行
\NewDocumentCommand{\biaoti}{m}
{
\group_begin:
\noindent
\zw_lead_center:n {#1}
\zw_cells:n {#1}
\group_end:
\par
}
% 称呼:顶格,独占一行(冒号、感叹号等请写进参数里)
\NewDocumentCommand{\chenghu}{m}
{
\noindent
\zw_cells:n {#1}
\par
}
% 落款、日期:各占一行、各自右对齐(落款在上、日期在下);
% 留空 {} 则跳过该行
\NewDocumentCommand{\luokuan}{mm}
{
\tl_if_empty:nF {#1}
{ \noindent \zw_lead_right:n {#1} \zw_cells:n {#1} \par }
\tl_if_empty:nF {#2}
{ \noindent \zw_lead_right:n {#2} \zw_cells:n {#2} \par }
}
\ExplSyntaxOff
% =====================================================================
\begin{document}
\calibrationpage % 第 1 页(奇数页:网格在左 20.5mm)
\newpage
\calibrationpage % 第 2 页(偶数页:网格在左 14.5mm)
\newpage
\biaoti{给妈妈的一封信}
\chenghu{亲爱的妈妈:}
您好!很久没有给您写信了,……
祝您身体健康!
1测试23测试456测试1a测试1a2测试。
测试行末的标点符号是否会换行显示,再写七个汉字。
果然换行了!再测试下标点符号是否能显示在同一个格子中:“这个引号真好看”。“《活着》”!
这里冒号应该留在上一行的呀呀呀呀呀呀呀呀呀呀:“呀呀呀”。这里的冒号和分号的断点倒是正确。
\luokuan{您的孩子 小明}{2026年10月20日}
\end{document}