%% This is file `novel-TextMacros.sty', part of `novel' document class. %% Copyright 2017-2024 Robert Allgeyer. %% %% This file may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, version 1.3c. %% License URL: https://www.latex-project.org/lppl/lppl-1-3c/ %% \ProvidesFile{novel-TextMacros.sty}% [2024/04/26 v2.2 LaTeX file (text macros usable within document body)] %% %% This file contains various commands usable within the document body, %% for styling and otherwise manipulating text. %% These are not commands used in Preamble for setup, although a few of them %% can be used in Preamble for setup, then again in body for change. %% The following commands are BANNED. %% ---------------------------------------------------------------------------- % The `novel' class disallows these commands, or neutralizes them by simply % repeating their arguments. In some cases, they request functionality % not implemented in `novel'. In other cases, the function is part of core, % but would interfere with the `novel' emphasis on constant line skip. % You may re-define the commands via your own code in Preamble (discouraged). % %% Standard LaTeX font sizes are ineffective. Use novel's own methods. % If you write one of these size commands, it will silently be ignored: % \small, \footnotesize, \scriptsize, \tiny, % \ large, \Large, \LARGE, \huge, \Huge, \HUGE, \textls % %% Internal subdivisions are ineffective. % The command will merely echo its argument, without any functionality: % \part, \chapter, \section, \subsection, \subsubsection, % \paragraph, \subparagraph. % %% Some commands will generate an error, so that you know they cannot be used: % \maketitle, \makeindex, \tableofcontents, \listoftables, \listoffigures, % %% You can only use \includepdf (package: pdfpages) for pdf files pre-processed %% by novel-scripts. % %% You cannot use \includegraphics. Instead use novel's own image commands. % %% Commands normally provided in other classes, pertaining to floats, figures, % and tables, are not defined in `novel' class. % If you attempt to use them, an error will result. %% %% AND NOW TO THE GOOD STUFF %% ---------------------------------------------------------------------------- %% \gdef\straightquote{{\addfontfeature{Ligatures=ResetAll}'}} \gdef\straightdblquote{{\addfontfeature{Ligatures=ResetAll}"}} %% %% Acronyms often look too big in regular capitals, but too small in % small caps. Command \midcase{} provides an intermediate size: \gdef\midcase#1{% \if@HasDecoFont% {\decofont{#1}}% \else% #1% simply echo the argument, if no decofont \fi% }% %% %% Deco glyphs are decorative elements chosen from the decofont by code. % They are documented in file NovelDeco-glyphs.pdf, in the `extras' folder. \gdef\decoglyph#1{% \if@HasDecoFont% \IfBeginWith{#1}{n}{% normal weight symbol (same as regular) \StrBehind{#1}{n}[\@tempdecoglyph]{\@decoglyph\symbol{\@tempdecoglyph}}% }{}% \IfBeginWith{#1}{r}{% regular weight symbol \StrBehind{#1}{r}[\@tempdecoglyph]{\@decoglyph\symbol{\@tempdecoglyph}}% }{}% \IfBeginWith{#1}{l}{% light weight symbol \StrBehind{#1}{l}[\@tempdecoglyph]{\@decoglypha\symbol{\@tempdecoglyph}}% }{}% \IfBeginWith{#1}{e}{% extra light weight symbol \StrBehind{#1}{e}[\@tempdecoglyph]{\@decoglyphb\symbol{\@tempdecoglyph}}% }{}% \IfBeginWith{#1}{t}{% thin weight symbol \StrBehind{#1}{t}[\@tempdecoglyph]{\@decoglyphc\symbol{\@tempdecoglyph}}% }{}% \else% ~% non-breaking space, if no decofont \fi% }% %% %% \memo{} does not print or save its argument. % Useful when you wish to put a note to yourself in the *.tex document. % Not the same as % comment, because anything after the braces will print. \long\gdef\memo#1{} %% %% \stake is like \strut, but does not occupy uch vertical space. \gdef\stake{\rule{0pt}{1pt}} % placeholder %% %% \smcp{} and \textsc{} small caps (Open Type) and \allsmcp{}: \DeclareDocumentCommand \smcp { +m } {% lowercase to small caps {\addfontfeature{Letters=SmallCaps}#1}% } \ExplSyntaxOn \DeclareDocumentCommand \allsmcp { +m } {% lowercase+uppercase to small caps \fontspec_if_feature:nTF {c2sc}{% {\addfontfeatures{Letters=UppercaseSmallCaps, Letters=SmallCaps}#1}% }{% {\addfontfeature{Letters=SmallCaps}\MakeLowercase{#1}}% }% } \ExplSyntaxOff \let\textsc\smcp\relax % unified \let\oldscshape\scshape\relax % in case needed later \let\scshape\smcp\relax % unified \providecommand\FirstLineFont{\smcp} % package `magaz' \renewcommand\FirstLineFont{\smcp} %% %% `novel' class normally allows only black or grayscale text on white paper. %% In draft mode, option `shademargins` provides light gray margins, %% but still black text on white paper. %% Just in case `xcolor' re-defines its internals, to prevent code bomb: \ProvideDocumentCommand\@declaredcolor { m } {} \ProvideDocumentCommand\@undeclaredcolor { o m } {} % For convenience: \definecolor[named]{gray1}{gray}{0.15} % ten percent gray, etc. \definecolor[named]{gray2}{gray}{0.25} \definecolor[named]{gray3}{gray}{0.33} \definecolor[named]{gray4}{gray}{0.4} \definecolor[named]{gray5}{gray}{0.5} \definecolor[named]{gray6}{gray}{0.6} \definecolor[named]{gray7}{gray}{0.68} \definecolor[named]{gray8}{gray}{0.75} \definecolor[named]{gray9}{gray}{0.87} %% % This code is slightly modified from package `etextools' by Florent Chervet. % Its license: "This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 of this % license or (at your option) any later version." % My rationale for not simply loading the package: Other parts of the package % generate warning messages, but the warnings do not apply in `novel' % Also, I might wish to hack the code at some point. \newcount\nov@ettl@fter \newrobustcmd\nov@AfterGroup{% \@ifstar{\nov@ettl@AfterGroup\@firstofone}{\nov@ettl@AfterGroup\unexpanded}% } \newrobustcmd\nov@ettl@AfterGroup[2]{% \csxdef{nov@ettl@fterGroup\number\numexpr\the\nov@ettl@fter+1}% {\global\csundef{nov@ettl@fterGroup\number\numexpr\the\nov@ettl@fter+1}#1{#2}}% \global\advance\nov@ettl@fter\@ne% \expandafter\aftergroup\csname nov@ettl@fterGroup\the\nov@ettl@fter\endcsname% } %% End code from `etextools`. %% \flexbox based on code by Werner at tex.stackexchange.com, CC-by-sa-3.0: % Optional first argument is minimum width of box. % Second argument is contents of box. % Box width will be the greater of #1 or the width of #2. \newcommand{\flexbox}[2][\parindent]{% \settowidth{\@tempLength}{#2}% \ifdimcomp{\@tempLength}{<}{#1}{\makebox[#1][l]{#2}}{#2}% } %% %% \bigemdash inspired by code by RTBarnard at stackoverflow.com, CC-by-sa-3.0: % Allows a rule of variable length to fill available space at its right. % There must be a "right-hand limit" imposed by something, such as by enclosing % the rule in a \makebox of known width, or by an immediate \par. % Without this limit, rule will be missing (zero width) or excessively long, % or an error will result (referring to \leaders). % Takes a single optional argument: Comma-separated raise, line thickness. % If no argument or empty, defaults to [1,1]. % Without comma, argument defaults to [argument,1]. % First number: Scales raise/down. 1 = raise of emdash in typical fonts. % Second number: Scales line thickness. 1 = emdash in typical fonts. % NOTE: Built-in TeX PDF viewer might show the line at slightly incorrect % width and height. Best seen in a "real" PDF viewer. \DeclareDocumentCommand \bigemdash { O{1,1} } {% \ifthenelse{\equal{#1}{}\OR\equal{#1}{\space}}{% \def\@myargsi{1,1}}{\def\@myargsi{#1}% }% \StrDel{\@myargsi}{\space}[\@myargs]% \StrCut{\@myargs}{,}{\@myraisei}{\@mythicki}% \ifthenelse{\equal{\@myraisei}{}\OR\equal{\@myraisei}{\space}}{% \def\@myraise{1}}{\def\@myraise{\@myraisei}% }% \ifthenelse{\equal{\@mythicki}{}\OR\equal{\@mythicki}{\space}}{% \def\@mythick{1}}{\def\@mythick{\@mythicki}% }% \FPmul{\@myraise}{\@myraise}{0.56}% ex, typical font emdash raise \FPmul{\@mythick}{\@mythick}{0.049}% em, typical font emdash thickness {\leaders\hbox{\rule[\@myraise ex]{1pt}{\@mythick em}}\hfill\stake}% } %% %% \charscale[scale,x,y]{text} and starred version % This is an "in-line" means of adjusting font size % and position, without changing the baselineskip. % If the optional argument is unused or empty, then the text is not scaled % or moved, but equivalent to \stake\smash{text}. % Up to 3 arguments may be used in the single optional argument. % They are separated by commas. Spaces ignored. % First argument is scale. Number>=0.5. Default 1. Relative to what the size % would otherwise be. May multiply with an already-scaled font. % Second and third arguments are x-offset and y-offset, respectively. % Offsets are lengths, best measured in em or \nfs for horizontal, % and em or \nfs or \nbs for vertical. Default 0pt. Can abbreviate as 0. % Posiive offsets are horizontal right, vertical up. % Text is "smashed" and may go anywhere, even overlying other text or image. % The unstarred command occupies width equal to the scaled text, % thus leaving a "hole" where the text would have been. % The starred command occupies zero width, thus no hole. Consecutive starred % commands, without intervening space, displace from same origin. % The text must be on a single line, without break or wrap. % Do not use \\ or \par inside \charscale. Sometimes you need \par after it. % If the text is in a font other than usual, place the font command % outside \charscale, not inside. \DeclareDocumentCommand \charscale { s O{1} m } {% % Allows empty or blank to be used, and still provide default values: \ifthenelse{\equal{#2}{}\OR\equal{#2}{\space}}{% \def\@myargs{1}% }{% \def\@myargs{#2}% }% \StrDel{\@myargs}{\space}[\@myargsns]% needs {\space} not { } \StrCut{\@myargsns}{,}{\@myscalet}{\@mycoord}% \ifthenelse{\equal{\@myscalet}{}}{% \def\@myscale{1}% }{% \def\@myscale{\@myscalet}% }% \FPmax{\@myscale}{\@myscale}{0.5}% not permitted below this scale \StrCut{\@mycoord}{,}{\@myxt}{\@myyt}% \ifthenelse{\equal{\@myxt}{}\OR\equal{\@myxt}{0}}{% \def\@myx{0pt}% }{% \def\@myx{\@myxt}% }% \ifthenelse{\equal{\@myyt}{}\OR\equal{\@myyt}{0}}{% \def\@myy{0pt}% }{% \def\@myy{\@myyt}% }% \IfBooleanTF{#1}% {% starred \ifvmode\else\unskip\fi% \stake\makebox[0pt][l]{% \smash{\hspace{\@myx}\raisebox{\@myy}{\scalebox{\@myscale}{#3}}}% }% }{% unstarred \stake\makebox[0pt][l]{% \smash{\hspace{\@myx}\raisebox{\@myy}{\scalebox{\@myscale}{#3}}}% }% \smash{\phantom{\scalebox{\@myscale}{#3}}}% }% }% end \charscale %% % Note that \savepos is a luatex primitive, not a macro from gridset package. % Length \TotalYpos is measured from the very bottom of the page, % upward to the baseline of non-existent "line zero" in main text. % The first printed main text line is line 1. % Neither header nor footer influence this. \newlength\TotalYpos \gdef\@GetInitialYpos{% called by `novel.cls' \AtEndPreamble \begingroup% \savepos% \protected@write\@auxout{}{% \protect\@TotalYpos{\noexpand\number\lastypos}% }% \endgroup% } % \gdef\@TotalYpos#1{\gsetlength\TotalYpos{#1sp}} % %% \newlength\PosTolerance % How close is close enough? \setlength\PosTolerance{0.0625pt} % should be good enough %% %% Environment parascale. Scales one or more paragraphs. % Warns when (as is often the case) the line after \end{parascale} % will be off grid. Then, add some \vspace just before \end{parascale}. \newenvironment{parascale}[1][1]{% optional argument is scale, default 1. \FPmax{\@mytempDetailsN}{#1}{0.5}% must be at least half normal. \FPmul{\@mytempDetailsFontN}{\@mytempDetailsN}{\strip@pt\@SetFontSize}% \FPround{\@mytempDetailsFontN}{\@mytempDetailsFontN}{2}% \FPmul{\@mytempDetailsSkipN}{\@mytempDetailsN}{\strip@pt\baselineskip}% \FPround{\@mytempDetailsSkipN}{\@mytempDetailsSkipN}{2}% \FPsub{\@mytempSkipN}{\strip@pt\baselineskip}{\@mytempDetailsSkipN}% \vspace{\@mytempSkipN pt}% puts first scaled line at normal baseline \begingroup% \fontsize{\@mytempDetailsFontN pt}{\@mytempDetailsSkipN pt}\selectfont% \ignorespaces% }{% \par\endgroup% \vspace{-\nbs}% \leavevmode\getParapos\par% detects if following line will be off-grid \vspace{0.002\nbs plus 0pt minus 0.2pt}% math fudge factor, in case of roundoff } % \gdef\getParapos{% \begingroup% \savepos% \protected@write\@auxout{}{% \protect\@getParapos{\noexpand\number\lastypos}{\thepage}% }% \endgroup% } % \newlength\CurrentParapos \gdef\@getParapos#1#2{} % initialized to nothing, when reading aux at beginning \gdef\@RedefineParapos{ % called by `novel.cls' \AtBeginDocument \gdef\@getParapos##1##2{% numerical position sp, page \gsetlength\CurrentParapos{##1sp}% measured up from very bottom of page. % Now change it, to measure downward from "line zero" of main text: \gsetlength\CurrentParapos{\TotalYpos-\CurrentParapos}% calc package. \FPdiv{\ParaHowdown}{\strip@pt\CurrentParapos}{\strip@pt\nbs}% package fp \FPtrunc{\ParaLinesdown}{\ParaHowdown}{0}% integer number of lines down \FPsub{\ParaResidual}{\ParaHowdown}{\ParaLinesdown}% portion of line \FPdiv{\@PosTolerance}{\strip@pt\PosTolerance}{\strip@pt\nbs}% \gdef\ParaComplain{yes}% \FPifgt{\@PosTolerance}{\ParaResidual}% \gdef\ParaComplain{no}% within tolerance \fi% \FPsub{\ParaDeficit}{\strip@pt\nbs}{\ParaResidual}% \FPifgt{\@PosTolerance}{\ParaDeficit}% \gdef\ParaComplain{no}% \fi% \FPsub{\ParaResidual}{1}{\ParaResidual}% \FPifgt{\@PosTolerance}{\ParaResidual}% \gdef\ParaComplain{no}% within tolerance \fi% \FPifgt{\@PosTolerance}{\ParaDeficit}% \gdef\ParaComplain{no}% \fi% \FPround{\ParaResidual}{\ParaResidual}{3}% \FPclip{\ParaResidual}{\ParaResidual}% \ifthenelse{\equal{\ParaComplain}{yes}}{% \ClassWarning{novel}{Line after parascale may be off-grid, page ##2. ^^J% Appears to be \ParaResidual\string\nbs\space higher than expected.}% }{}% } } % end @RedefineParapos %% %% Environment toc (alternative table of contents): % optional argument: additional vspace (\nbs) after each \tocitem entry. % required: LR margin increase, to narrow the table (0pt = full textwidth). \newlength\@tocnumwid \newlength\@tocskip \newenvironment{toc}[2][0]{% \begin{adjustwidth}{#2}{#2}% \begingroup% \setlength\parindent{0pt}% local \setlength\@tocnumwid{\widthof{00.~}}% local \setlength\@tocskip{#1\nbs}% local }{% \endgroup% \end{adjustwidth}\par% }% % \tocitem[number]{description}{page} provides a one-line table entry in toc. % optional number might be chapter number. Can use ~ to offset. % unstarred: distance between description and page is not decorated. % starred (better): distance between description and page has dotted line. \newcommand\tocitem{\@ifstar\@tocitemst\@tocitemns} \newcommand\@tocitemst[3][]{% \ifthenelse{\equal{#1}{}}{}{% \ifthenelse{\equal{#1}{~}}{% \makebox[\@tocnumwid][l]{~}% }{% \makebox[\@tocnumwid][l]{#1.}% }% }% #2\,% \leaders\hbox to 0.3em{\hfil.\hfil}\hfill% Thanks to user daniel-j via GitHub. \,#3\par\vspace{\@tocskip}% }% % \newcommand\@tocitemns[3][]{% \ifthenelse{\equal{#1}{}}{}{% \ifthenelse{\equal{#1}{~}}{% \makebox[\@tocnumwid][l]{~}% }{% \makebox[\@tocnumwid][l]{#1.}% }% }% #2% \hfill% #3\par\vspace{\@tocskip}% }% %% end toc and \tocitem. %% legalese environment. Certain info, such as Copyright page, customarily is % neither justified nor hyphenated. Can be used wherever desired, of course. \newenvironment{legalese} {\raggedright\hyphenpenalty=10000\exhyphenpenalty=10000} {} % \gdef\hangleft#1{% places content to left of usual position. \setlength\@tempLength{\widthof{#1}}% local \stake\hspace{-\@tempLength}#1% } %% %% \showlength[decimal places]{length}{units} % Returns length as a string with the units, but does not change the length. % length is any existing length % units may be pt, bp, mm, cm, in % decimal places for rounding (0=integer, 1=one decimal point, etc.) % Example: \showlength[3]{17bp}{in} returns 0.236in \newcommand\showlength[3][]{% \setlength\@tempLength{#2}% \def\@tempLengthN{\strip@pt\@tempLength}% \ifthenelse{\equal{#3}{bp}}{% \FPmul{\@tempLengthN}{\@tempLengthN}{0.99626401}% }{}% \ifthenelse{\equal{#3}{mm}}{% \FPmul{\@tempLengthN}{\@tempLengthN}{0.351459804}% }{}% \ifthenelse{\equal{#3}{cm}}{% \FPmul{\@tempLengthN}{\@tempLengthN}{0.0351459804}% }{}% \ifthenelse{\equal{#3}{in}}{% \FPmul{\@tempLengthN}{\@tempLengthN}{0.013837}% }{}% \ifthenelse{\equal{#1}{}}{}{\FPround{\@tempLengthN}{\@tempLengthN}{#1}}% \@tempLengthN{#3}% } % %% end \showlength %% \lnum for lining numbers. \gdef\lnum#1{{\addfontfeature{Numbers=Lining}#1}} %% %% For developer use: prints a number of yada yada lines. % Starred version adds extra smashed lines above and below. \newcount\yadacurrentcount \newcount\yadaendcount \DeclareDocumentCommand\novelyadayada { s O{1} }{% \yadacurrentcount=1% \yadaendcount=#2% \ifthenelse{\equal{#2}{0}}{}{% \IfBooleanTF{#1}{% \def\@yadadblup{\charscale*[1,\normalparindent,2\nbs]{Dbl Raised yada}}% \def\@yadaup{\charscale*[1,\normalparindent,\nbs]{Raised yada}}% \def\@yadadown{% \ifnum\yadacurrentcount=\yadaendcount% \charscale*[1,\normalparindent,-\nbs]{Dropped yada}% \fi% }% \def\@yadadbldown{% \ifnum\yadacurrentcount=\yadaendcount% \charscale*[1,\normalparindent,-2\nbs]{Dropped yada}% \fi% }% }{% \def\@yadadblup{}% \def\@yadaup{}% \def\@yadadown{}% \def\@yadadbldown{}% }% \loop% \noindent\@yadadblup\@yadaup\@yadadown\@yadadbldown% \makebox[\normalparindent][l]{\the\yadacurrentcount.}% Lotta yada.\par \def\@yadaup{}% \def\@yadadblup{}% \advance\yadacurrentcount 1% \ifnum\yadacurrentcount<\yadaendcount% \repeat% }% } %% %% Two columns, cannot cross page break. %% \sidebyside[a,b,c,d]{column 1 content}{column 2 content} % a,b,c,d lengths % a = left margin to left edge of left column (left indent) % b = left margin to right edge of left column ( = a + column width) % c = left margin to left edge of right column ( = a + b + gap) % d = left margin to right edge of right column ( = textwidth - right indent) % Thus 0pt <= a < b < c < d <= \textwidth. % Default: no indents, equal column widths, separated by \normalparindent. \DeclareDocumentCommand\sidebyside { O{} +m +m } {% no ExplSyntax, no more %: \StrDel{#1}{\space}[\@argsns] \StrCut{\@argsns}{,}{\@argsone}{\@argsothera} \StrCut{\@argsothera}{,}{\@argstwo}{\@argsotherb} \StrCut{\@argsotherb}{,}{\@argsthree}{\@argsfour} \ifthenelse{\equal{\@argsone}{}}{% \def\@sbsll{0pt} }{% \def\@sbsll{\@argsone} }% \setlength\@tempLength{0.5\textwidth-0.5\normalparindent} \ifthenelse{\equal{\@argstwo}{}}{% \edef\@sbslr{\the\@tempLength} }{% \edef\@sbslr{\@argstwo} }% \setlength\@tempLength{0.5\textwidth+0.5\normalparindent} \ifthenelse{\equal{\@argsthree}{}}{% \edef\@sbsrl{\the\@tempLength} }{% \edef\@sbsrl{\@argsthree} }% \ifthenelse{\equal{\@argsfour}{}}{% \edef\@sbsrr{\the\textwidth} }{% \edef\@sbsrr{\@argsfour} }% \null\vspace{-\baselineskip} \begin{adjustwidth}{\@sbsll}{\dimexpr\textwidth-\@sbsrr} \noindent\begin{minipage}[t]{\dimexpr\@sbslr-\@sbsll} \strut #2\strut \end{minipage}\hspace{\normalparindent}\begin{minipage}[t]{\dimexpr\@sbsrr-\@sbsrl} \strut #3\strut \end{minipage} \end{adjustwidth} \null\vspace{-\baselineskip} } % %% end \sidebyside %% Added in v.1.82: \newcommand\myparindent[1]{\gdef\myown@parindent{#1}} \@onlypreamble\myparindent %% %% Some things must wait until after layout calculations: \gdef\@ActivateTextLengths{% called by `novel.cls' \AtEndPreamble % Store some normal values (main font) for global use in document body: \newlength\nfs % abbreviation for normal font point size \gsetlength\nfs{\@SetFontSize} \newlength\nbs % abbreviation for normal baselineskip \gsetlength\nbs{\baselineskip} \ifdefined\myown@parindent \gsetlength\parindent{\myown@parindent} \else \gsetlength\parindent{1.5em} \fi \newlength\normalparindent \gsetlength\normalparindent{\parindent} \newlength\normalxheight % height of lowercase x \gsetlength\normalxheight{\heightof{x}} \newlength\normalXheight % height of uppercase X \gsetlength\normalXheight{\heightof{X}} \newlength\normalscxheight % height of small cap x \gsetlength\normalscxheight{\heightof{\textsc{x}}} \newlength\normalAringheight % height of Å (usually tallest in Latin-1) \gsetlength\normalAringheight{\heightof{Å}} \newlength\normaldescender % depth of lowercase gjpqy \gsetlength\normaldescender{\depthof{gjpqy}} }% end \@ActivateTextLengths %% %% More things that must wait: \gdef\@ActivateTextMacros{% called by `novel.cls' \AtBeginDocument % \forceindent and \backindent perform or remove indent, using \hspace{}. % Rationale: Sometimes \indent and \noindent neutralized by another command. \gdef\forceindent{\ifvmode\else\unskip\fi\stake\hspace{\normalparindent}} \gdef\backindent{\ifvmode\else\unskip\fi\hspace{-\normalparindent}} }% end \@ActivateTextMacros %% %% \endinput %% %% End of file `novel-TextMacros.sty'.