% This file is part of the CTAN package named plain-grid. % % plaingridT.tex: macros to type straight text in a baseline grid % Version 1.0, 05.05.2026 % % Copyright (C) 2026 Udo Wermuth (author) % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see . % {\catcode`@=11 \let\\=\noexpand \xdef\gridrestoreparams{\vsize=\the\vsize \baselineskip=\the\baselineskip \lineskip=\the\lineskip \lineskiplimit=\the\lineskiplimit \parskip=\the\parskip \ifr@ggedbottom \\\raggedbottom \else \\\normalbottom\fi \topskip=\the\topskip \interlinepenalty=\the\interlinepenalty \clubpenalty=\the\clubpenalty \widowpenalty=\the\widowpenalty \brokenpenalty=\the\brokenpenalty \let\\\pagebody=\\\GRIDpagebody}} \def\gridlog{ 1 }% 1,2,3: level of verbosity \def\GRIDlog#1#2{% #1: level; #2: message \ifnum#1=0 \immediate\write16{#2}% terminal \else\ifnum#1=1 \wlog{#2}\else% rest to log \ifnum#1>\gridlog\else \wlog{#2}\fi\fi\fi} \skip255=0pt \edef\GRIDnext{\the\skip255} \skip255=\baselineskip \advance\skip255 by -1\skip255 \edef\GRIDtmp{\the\skip255} \ifx\GRIDtmp\GRIDnext\else % make it a dimen \baselineskip=1\baselineskip\GRIDlog0{GRID: \baselineskip is now \the\baselineskip}\fi \newdimen\GRIDgrid \GRIDgrid=\baselineskip \normalbottom \topskip=\GRIDgrid \dimendef\GRIDdim=255 \countdef\GRIDcnt=255 \GRIDdim=\vsize \divide\vsize by \GRIDgrid \GRIDcnt=\vsize % save the number of lines \edef\GRIDlines{\the\GRIDcnt\space} \multiply\vsize by \GRIDgrid % new vsize \ifdim\GRIDdim=\vsize \else \GRIDlog0{GRID: \vsize changed; new value \the\vsize\space (\number\GRIDlines\space lines)}\fi \interlinepenalty=0 \brokenpenalty=0 \clubpenalty=0 \widowpenalty=0 \lineskip=0pt % no minimal interline glue \lineskiplimit=0pt % reassure plain's value \def\GRIDprevgrid{% go to previous baseline \begingroup\ifvmode \GRIDdim=\prevdepth \ifdim\GRIDdim<0pt \GRIDdim=0pt \fi \loop \ifdim\GRIDdim>\GRIDgrid \advance\GRIDdim by -\GRIDgrid \repeat \kern-\GRIDdim \penalty0 \fi \endgroup \ifvmode \prevdepth=0pt \fi} \parskip=0pt \def\gridskiponeline{% create an empty line \par\GRIDprevgrid\kern\GRIDgrid\penalty0 } \def\gridbackoneline{% backspace one line \par\GRIDprevgrid\kern-\GRIDgrid\penalty0 } \newcount\GRIDfreelines % lines left on page \def\GRIDlinesavailable{% set \GRIDfreelines \ifdim\pagegoal=\maxdimen % nothing on page \GRIDfreelines=\GRIDlines \else % mid-page; compute \GRIDfreelines \GRIDfreelines=\pagegoal \advance\GRIDfreelines by -\pagetotal \divide\GRIDfreelines by \GRIDgrid \ifnum\GRIDfreelines<0 %page break delayed \advance\GRIDfreelines by \GRIDlines \fi \fi \GRIDlog2{GRID line \the\inputlineno: available lines = \the\GRIDfreelines}} \def\GRIDvskip#1{% #1: lines to skip \ifnum#1<1 \else \GRIDcnt=#1\relax \gridskiponeline % handle \prevdepth first \loop \advance\GRIDcnt by -1 \ifnum\GRIDcnt>0 \line{\hfil}% \repeat \fi} \def\gridskipmax#1{% #1: skip <= #1 lines \par \penalty0 \GRIDlinesavailable \ifnum\GRIDfreelines=\GRIDlines% empty page \else\ifnum#1>\GRIDfreelines \GRIDvskip\GRIDfreelines % skip available \else \GRIDvskip{#1}\fi\fi \par} \def\grideject{\gridskipmax{1000}\eject} \def\GRIDsetGRIDsf{% save \spacefactor \let\GRIDsf=\empty \ifhmode \edef\GRIDsf{% \spacefactor=\the\spacefactor}\/\fi} \newwrite\GRIDenfile % file for endnotes \def\gridenfilename{endnotes}% stem for name \def\GRIDfileno{0 }% number for file names \newif\ifGRIDendnotes % true: file is open \def\GRIDnewenfile{{% create a new file name \GRIDcnt=\GRIDfileno % get current file no \xdef\GRIDcurfile{% new name: stem plus no \gridenfilename\romannumeral\GRIDcnt.tex}% \advance\GRIDcnt by 1 % store next number \xdef\GRIDfileno{\number\GRIDcnt\space}}} \def\GRIDencnt{1 }% the counter for endnotes \def\griduseendnotes{% init endnotes \gdef\GRIDencnt{1 }% reset endnotes counter \ifGRIDendnotes \GRIDlog0{GRID: file for endnotes not yet output: \GRIDcurfile}% \immediate\closeout\GRIDenfile \fi \global \GRIDendnotestrue \GRIDnewenfile \immediate \openout\GRIDenfile=\GRIDcurfile \relax} \def\gridprintendnotes{% output endnotes \ifGRIDendnotes \global\GRIDendnotesfalse \immediate\closeout\GRIDenfile % close the \input \GRIDcurfile % file and input it \else \GRIDenerr \fi} \def\GRIDenerr{\errhelp{Call the macro \gridendnote only between \griduseendnotes and \gridprintendnotes (and this one only once after the first).}\errmessage{Missing \string\griduseendnotes}} \def\gridendnote{% create an endnote \GRIDsetGRIDsf$^{\number\GRIDencnt}$\GRIDsf \ifGRIDendnotes \let\GRIDnext=\GRIDprepen \else\let\GRIDnext=\GRIDenerr\fi \GRIDnext} \let\gridnoteend=\relax % end a \gridendnote \def\gridenmark{Endnotes for page/chapter} \def\GRIDprepen{\immediate\write\GRIDenfile {\noexpand\par\mark{\gridenmark}% \noexpand\textindent{\number\GRIDencnt.}}% {\GRIDcnt=\GRIDencnt \advance\GRIDcnt by 1 \xdef\GRIDencnt{\number\GRIDcnt\space}}% \begingroup \def\do##1{\catcode`##1=12}% \dospecials \obeylines % change catcodes \let\GRIDnext=\GRIDcopy \GRIDcopy} {\catcode`/=0 \catcode`\\=12 % end with line /gdef/GRIDhalt{\gridnoteend}}% \gridnoteend {\obeylines \gdef\GRIDcopy#1 {\def\GRIDtmp{#1}\ifx\GRIDtmp\GRIDhalt % \def\GRIDnext{\endgroup\space}% add space \else\ifx\GRIDtmp\empty \immediate\write % \GRIDenfile{\noexpand\par\indent}\else % \immediate\write\GRIDenfile{\GRIDtmp}% \fi\fi \GRIDnext}} \newif\ifgridhidesectiontitles % true: hide \def\GRIDmessage#1{\ifgridhidesectiontitles \GRIDlog1{#1}\else \GRIDlog0{#1}\fi} \newif\ifgridnewpageOK % true: accept a move \def\GRIDmakeavailable#1{% #1 required lines \par\penalty0 \GRIDlinesavailable \ifnum\GRIDfreelines<#1 \ifgridnewpageOK \gridnewpageOKfalse \else \GRIDlog0{GRID: unexpected page ejection}\fi \grideject \GRIDfreelines=\GRIDlines\fi\ignorespaces} \outer\def\gridbeginsection#1\par{% #1 title \GRIDmakeavailable6 % mid: 2 above/1 below \ifnum\GRIDfreelines=\GRIDlines % top: 0/1 \else \GRIDvskip2\fi \GRIDmessage{#1}% \leftline{\bf#1}\gridskiponeline \noindent} \def\gridkeepspace{ 1 }% 0, or 1, or 2 % new page 0: 0/1.0; 1: 0.5/0.5; 2: 1.5/0.5 \outer\def\gridstartsection#1\par{% #1 title \GRIDmakeavailable5 \GRIDcnt=\gridkeepspace \ifnum\GRIDfreelines=\GRIDlines % new page: \ifnum\GRIDcnt>1 \line{\hfil}\fi% 2: + 1 \ifnum\GRIDcnt>0 \line{\hfil}% 1, 2: + 1 \kern-0.5\GRIDgrid \fi % 1, 2: - 0.5 \else \gridskiponeline % mid-page: 1.5/0.5 \kern0.5\GRIDgrid \GRIDcnt=2 % acts like 2 \fi \GRIDmessage{#1}\leftline{\bf#1}% \gridskiponeline \ifnum\GRIDcnt=0 \else % 1 \kern-0.5\GRIDgrid\fi \noindent}% >0: - 0.5 \outer\def\gridbye{\gridskipmax{1000}% \supereject\end} \newbox\GRIDoutputbox % save page contents \newif\ifgridshowlinenos% true: show numbers \newbox\GRIDlnos % box for line numbers \font\sixrm=cmr6 % font for line numbers \let\GRIDpagebody=\pagebody % save for undo \def\pagebody{% plain & output line numbers \setbox\GRIDoutputbox=\vbox to\vsize{% \boxmaxdepth=\maxdepth \pagecontents}% \vbox{\ifgridshowlinenos \vbox to 0pt{% \rlap{\kern\hsize\copy\GRIDlnos}\vss}\fi \box\GRIDoutputbox}} \def\GRIDsetboxlnos{\GRIDcnt=1 \setbox\GRIDlnos=\vbox to\vsize{\hsize=12pt \parfillskip=0pt \prevdepth=0pt \noindent \sixrm \loop \null\hfil \number\GRIDcnt \ifnum\GRIDlines>\GRIDcnt \penalty-10000 \advance\GRIDcnt by 1 \repeat \endgraf}}% finish the paragraph \GRIDsetboxlnos % create the line number box \def\gridsnaptonextbaseline{% add a skip to \par\GRIDprevgrid % go to the next baseline \begingroup \dimen0=\pagetotal \divide\dimen0 by \GRIDgrid \multiply\dimen0 by \GRIDgrid \advance\dimen0 by -\pagetotal \ifdim\dimen0<0pt \advance\dimen0 by \GRIDgrid\fi \kern\dimen0\penalty0 % skip \GRIDlog1{GRID line \the\inputlineno: \string\gridsnaptonextbaseline\space uses \the\dimen0}\endgroup} \def\gridskiphalfaline{\par\GRIDprevgrid \kern0.5\GRIDgrid \penalty0 }% use as pair \newbox\GRIDsbox % used instead of \strutbox \setbox\GRIDsbox=\copy\strutbox % fix values \def\gridrule#1(#2){% #1: raise, #2: ht & wd \noindent \def\GRIDtmp{#1}% #1 optional \ifx\GRIDtmp\empty \else \raise \ifdim#1>\ht\GRIDsbox \ht\GRIDsbox % limit \else\ifdim#1<-\dp\GRIDsbox -\dp\GRIDsbox \else #1\fi\fi\fi \hbox{\vbox to 0pt{\vss \hrule width 20pt #2 depth 0pt}}}