% 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 <http://www.gnu.org/licenses/>.
%
{\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}}}
