2

Assume you have \macroA in a way where \macroA{⟨TeX number-quantity denoting amount⟩} produces an amount of characters A.
Assume you have \macroB in a way where \macroB{⟨TeX number-quantity denoting amount⟩} produces an amount of characters B.

May look like this:

%\overfullrule=0pt
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\macroA#1{%
  \expandafter\Aloop\expandafter{\romannumeral\number\number#1 000}{}%
}%
\def\Aloop#1#2{%
  \ifx\relax#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {\leavevmode#2}{%
    \expandafter\ifx\expandafter\relax\firstoftwo{}#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
    {%
      % Don't use \discretionary with the last A to avoid
      % \baselineskip after the last line in case that line being
      % too long:
      \expandafter\Aloop\expandafter{\firstoftwo{}#1}{#2A}%
    }{%
      % use \discretionary to allow linebreaks
      \expandafter\Aloop\expandafter{\firstoftwo{}#1}{#2A\discretionary{}{}{}}%
    }%
  }%
}%
\def\macroB#1{%
  \expandafter\Bloop\expandafter{\romannumeral\number\number#1 000}{}%
}%
\def\Bloop#1#2{%
  \ifx\relax#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {\leavevmode#2}{%
    \expandafter\ifx\expandafter\relax\firstoftwo{}#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
    {%
      % Don't use \discretionary with the last B to avoid
      % \baselineskip after the last line in case that line being
      % too long:
      \expandafter\Bloop\expandafter{\firstoftwo{}#1}{#2B}%
    }{%
      % use \discretionary to allow linebreaks
      \expandafter\Bloop\expandafter{\firstoftwo{}#1}{#2B\discretionary{}{}{}}%
    }%
  }%
}%

\hrule\kern\dp\strutbox

\macroA{240}

\macroB{240}

\kern\dp\strutbox\hrule

\bye

As you can see on the picture,

enter image description here

, and on the terminal,

This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2019/dev/Debian) (preloaded format=pdftex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
Overfull \hbox (0.24593pt too wide) in paragraph at lines 26--27
[]\tenrm AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|

Overfull \hbox (2.74597pt too wide) in paragraph at lines 26--27
\tenrm AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|

Overfull \hbox (2.74597pt too wide) in paragraph at lines 26--27
\tenrm AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|

Overfull \hbox (3.57997pt too wide) in paragraph at lines 28--29
[]\tenrm BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB|

Overfull \hbox (4.83005pt too wide) in paragraph at lines 28--29
\tenrm BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB|

Overfull \hbox (4.83005pt too wide) in paragraph at lines 28--29
\tenrm BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB|
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] )
(see the transcript file for additional information)</usr/share/texlive/texmf-d
ist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on test.pdf (1 page, 9746 bytes).
Transcript written on test.log.

, this can produce a lot of overfull \hboxes, i.e., a lot of lines of text that are a little too wide and that therefore protrude into the margin at the right.

Assume you apply \macroA and \macroB within the same paragraph, i.e., you do:

\macroA{240}\macroB{240}\bye .

You will get a single paragraph, consisting of a sequence of letters A followed by a sequence of letters B—lines may still be a little too wide:

enter image description here

Questions:

How can you achieve within the same paragraph that lines that end with a B coming from \macroB are never too wide but may be a little too short and therefore never protrude into the margin at the right while lines that end with an A coming from \macroA may be a little too wide and therefore may protrude into the margin at the right?

I.e., how to modify the definition(s) of \macroA and/or \macroB for achieving the following output,

enter image description here

, without doing line-breaks "manually" but leaving line-breaking to TeX's own algorithms for (non-restricted) horizontal mode?

How to make it possible for the user to select how to handle the case that a B coming from \macroB is followed by an A coming from \macroA in a place where breaking the line is -eh- imminent, so that the line would be just a little too short if being broken right after that B and would be just a little too wide if being broken right after that A ;-)



The solution I come up with by now is producing horizontal whitespace of the width of the letter B followed by a discardable "negative" \hskip of the same width, followed by the letter B—this way you still get overfull \hbox-warnings with the Bs, but this way they are due to the whitespace at the end of the line triggering the line-break, not due to visible material protruding into the margin at the right:

%\overfullrule=0pt
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\macroA#1{%
  \expandafter\Aloop\expandafter{\romannumeral\number\number#1 000}{}%
}%
\def\Aloop#1#2{%
  \ifx\relax#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {\leavevmode#2}{%
    \expandafter\ifx\expandafter\relax\firstoftwo{}#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
    {%
      % Don't use \discretionary with the last A to avoid
      % \baselineskip after the last line in case that line being
      % too long:
      \expandafter\Aloop\expandafter{\firstoftwo{}#1}{#2A}%
    }{%
      % use \discretionary to allow linebreaks
      \expandafter\Aloop\expandafter{\firstoftwo{}#1}{#2A\discretionary{}{}{}}%
    }%
  }%
}%
\def\macroB#1{%
  \expandafter\Bloop\expandafter{\romannumeral\number\number#1 000}{}%
}%
\def\Bloop#1#2{%
  \ifx\relax#1\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {\leavevmode#2}{%
    \expandafter\Bloop\expandafter{\firstoftwo{}#1}{#2\phantom{B}\hbox{B}{\setbox1=\lastbox\hskip-\wd1 \box1}}%
  }%
}%

\hrule\kern\dp\strutbox

\macroA{240}\macroB{240}

\macroA{240}

\macroB{240}

\kern\dp\strutbox\hrule

\bigskip

About the edge case:

\bigskip

\hrule\kern\dp\strutbox

The case of the 64$^{\hbox{th}}$ B not fitting in the line any more:

\macroB{64}

The edge-case of 63 B being followed by an A:

\macroB{63}\macroA{1}

\kern\dp\strutbox\hrule

\bye

enter image description here

I don't take this solution for elegant.

I suppose there are better approaches.

6
  • @egreg Yes,that seems to do the trick. Thank you.;-) I don't know what I was thinking... :-( Why did you delete your answer? May 13, 2020 at 22:05
  • Because it sends the A to the next line in the last case.
    – egreg
    May 13, 2020 at 22:10
  • 1
    If you leave TeX the opportunity to add infinite glue, it will, so a line with the result of \macroB in it would never be overfull. You can replace the \phantom and the \lastbox with \hbox to \fontcharwd\font`B{}\hskip-\fontcharwd\font`B if you allow e-TeX.
    – egreg
    May 13, 2020 at 22:16
  • Why don't you want all the lines to fit in the margins? Overfull lines are errors. Making an entire paragraph fit is as easy as setting "sloppy" tolerance and emergencystretch. May 14, 2020 at 0:00
  • 1
    Why do you need the expandable loop here? Moreover, the loop with such poor and confused implementation?
    – wipet
    May 14, 2020 at 6:00

3 Answers 3

2

First: I have to simplify your expand-only loop, because it repeats 256 m, 255 m, 254 m in its argument again and again. Much more effective is to put these 256 m to the input queue only once.

Second: I suggest to insert negative skip, penalty 0, positive skip for As and vice versa for Bs.

Third: I suggest to make \rightskip stretchable in order to avoid overfull boxes.

\def\expandrepeat #1{\expandafter\expandrepeatA\romannumeral #1000\relax}
\def\expandrepeatA #1\relax#2{\expandrepeatB{#2}#1\relax}
\def\expandrepeatB #1#2{\ifx#2\relax \else #1\afterfi \expandrepeatB{#1}\fi}
\def\afterfi#1\fi{\fi#1}

\rightskip=0em minus1em

\expandrepeat{250}{A\hskip-1em\penalty0\hskip1em}%
\expandrepeat{250}{B\hskip1em\penalty0\hskip-1em}

% for testing, where is the margin?
\vskip-4cm \hfill \vrule height4cm

\end
3

What do you mean by

leaving line-breaking to TeX's own algorithms for (non-restricted) horizontal mode ?

Are you allowed to modify paramters of that algorithm? Like \leftskip or \rightskip or \tolerance or \hfuzz ?

If yes:

  • To allow TeX to make lines which are bad because they are a little too short you can set \tolerance to 10000.
  • To suppress warnings and overfull-rules, increase \hfuzz. This will not prevent lines to become too long. This prevents only to be informed about it. This is what I would do for specific paragraphs.

A sequence of A or B does not contain glue. Things like \emergencystretch seem not interesting. Also they apply for entire paragraphs not just some lines of a paragraph.

Repeated penalties between repeated A or repeated B are always the same and do not change which A or B close to line end to favour for breaking the line.

Must the macros also work within LaTeX's \centering (which ist not same as TeX's \centering) or center-environment?

Whatever.

You need breakpoints between the A or B. You can intersperse with things that form breakpoints: \penalty0 or \hskip0pt or \discretionary{}{}{}. (Or you could make A or B hyphenchar of font temporarily, but not for the last A or B.)

The question hides the problem of replicating tokens.

A generic macro \replicatethis would be nice where you specify what to repeat and what to use for intersperse.

With intersperse you need to look at last step because with last step no intersperse needed.

So I use generic macro similar to Wipet's but some modifications:

  1. Two \relax appended so you can detect last step.
  2. No \afterfi but \firstotwo/\secondoftwo because \afterfi-approach will fail at producing a sequence of things with unmatched \if.. or \fi.
  3. \romannumeral0\number\number#1 000 works with alphabetic constants `\a and \count24 also while \romannumeral#1000 fails with this.
  4. Replicated things are collected in argument and at the end of the loop all is delivered at once. If generic \replicatethis must be save with unmatched \if/\fi, must be done this way so all unmatched \if/\fi in the end appear at once. Disadvantage: This is slower and amount of repetitions is restricted by amount of tokens that fits in argument.

 

%\overfullrule=0pt
% / prevent nagging:
{\setbox1=\hbox{A}\expandafter}\expandafter\hfuzz\expandafter=\the\wd1
%\tolerance=10000

\long\def\permutetwoarguments#1#2{#2#1}%
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
% /
% / Generic macro \repeatthis{<<number> denoting repetitions>}{<what to repeat>}{<interspersing>}
% /
\long\def\replicatethis#1#2#3{%
  % / #1 repetitions
  % / #2 what to repeat
  % / #3 interspersing
  \romannumeral0% / <-- I *think* you may like this.  ;-)
  \expandafter\permutetwoarguments
  \expandafter{\romannumeral\number\number#1 000}%
              {\replicatethisloop{#2}{#3}{}}%
  \relax\relax
}%
\long\def\replicatethisloop#1#2#3#4#5%
  {%
    % / #1 what to repeat
    % / #2 interspersing
    % / #3 what you got so far.
    % / #4 other-m or \relax. If \relax done.
    % / #5 other-m or \relax. If \relax last step or done.
    \ifx#4\relax\expandafter\secondoftwo\else\expandafter\firstoftwo\fi
    {%
      \ifx#5\relax\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
      {\replicatethisloop{#1}{#2}{#3#1}}%
      {\replicatethisloop{#1}{#2}{#3#1#2}}%
      #5%
    }{ #3}%
  }%
% /
% / \macroA and \macroB
% /
\def\macroA#1%
  % / Intersperse with zero-\hskip breakpoint
  {%
    \leavevmode
    \replicatethis{#1}{A}{\hskip0pt}%
  }%
\def\macroB#1%
  % / If the \phantom causes line to be too long, then the \hskip 
  % / as breakpoint is used for breaking line and discarded.
  {%
    \leavevmode
    \replicatethis{#1}{\phantom{B}\hbox{B}{\setbox1=\lastbox\hskip-\wd1}B}{}%
  }%

% / Play game with unbalanced \if \fi.
\expandafter\expandafter\expandafter
\permutetwoarguments
\expandafter\expandafter\expandafter
{\replicatethis{12}{\fi}{}}
{\replicatethis{12}{\iffalse}{\else}}

% / That is the margins:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\noindent\vrule\vbox to\vsize{\advance\hsize-.8pt\leavevmode\hrule\vfill\hrule}\vrule
\par \kern-\vsize \nobreak\vskip-\baselineskip\nobreak\vskip\topskip\par 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\macroA{240}\macroB{240}

\macroA{240}

\macroB{240}

\kern\dp\strutbox\hrule

\bigskip

About the edge case:

\bigskip

\hrule\kern\dp\strutbox

The case of the 64$^{\hbox{th}}$ B not fitting in the line any more:

\macroB{64}

\smallskip

The edge-case of 63 B being followed by an A:

\macroB{63}\macroA{1}

\smallskip

The edge-case of 60A being followed by a B:

\macroA{60}\macroB{1}

\smallskip

The edge-case of an 1pt-kern and 59A being followed by a B:

\leavevmode\kern1pt\macroA{59}\macroB{1}

\smallskip

The edge-case of 59A being followed by a B:

\macroA{59}\macroB{1}

\kern\dp\strutbox\hrule

\vfill \break

\bye

enter image description here

1
  • 1
    Why does this "you have earned badge"-thing pop up all the time? It is frightening! I want sites without (dangerous?) background-action... Bad enough I had to enable JavaScript for this...
    – Jewdokija
    May 15, 2020 at 20:00
1

I didn't see the deleted answer but it probably went something like this. In \Aloop change \discretionary{}{}{} into

\nobreak\hskip0ptminus9pt\penalty1000\hskip0ptminus-9pt\relax

and in \Bloop change \discretionary{}{}{} into

\nobreak\hskip0ptplus9pt\penalty1000\hskip0ptplus-9pt\relax

The initial \nobreak could be omitted to allow ordinary flush line breaks, when it fits the line very well.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .