6

Let's say we have a lot of text to be typeset contained in a single macro, let's call it \myverylongtext

\newcommand{\myverylongtext}{
    This is some really long text: \blindtext[4].

    And it continues even more: \blindtext[1].
}

For simplicity, let's say there is no sectioning/ newpage command in \myverylongtext, but it can have line-breaking commands, and multiple paragraphs (paragraph-breaking commands?). Also, assume that font size & leading remain constant throughout, though all other LaTeX typesetting commands (\textbf, etc) are allowed within the text & should take effect.

Is it possible to define an efficient* macro that (when encountered) gets the next chunk of text of specified dimension? For example:

\newcommand{\getnextchunk}[4]{
    % #1 is name of macro (in our case \myverylongtext) 
    % #2 width of text box
    % #3 height of text box in lines
    % #4 draftmode: optional boolean argument that, when set, instead of typesetting contents returns the actual height (in lines), and width (in length units) of the chunk. (useful in making dynamic layout decisions)
    % returns/typesets a box of shape width * height
}

New calls to this macro \getnextchunk should get next chunk of text, as if the text contained in \myverylongtext reflowed from previous chunk to this chunk.

* This \myverylongtext can be very long, and there can be numerous such chunks, so efficiency is needed. You cannot typeset remaining portion of \myverylongtext multiple times. One vision I have of solution for such problem is that a box of size 0 is filled iteratively till it grows to the specified metric: width (in length dimension), height (in lines), and required justification; though I lack understanding of low-level tex/luatex/expl3 to write it myself.

Also, either justification commands like raggedright/raggedleft/full-justified specified in \myverylongtext should magically take effect, or we can set a restriction that we won't have such commands in the \myverylongtext & instead pass it as a 4th argument to \getnextchunk (I think latter is preferable, and would make it either easier and/or cleaner to code/use).

Another way to visualize the problem is imagine that we have multiple rectangular text boxes (of different width and height) that flow into each other sequentially as we encounter them in our document (don't worry about placement of these text boxes, *tex already has multiple elegant solutions for that). Also, we don't know when and how many such text boxes will be encountered in the document upfront. In my opinion, from years of using *tex, solving just this one problem will open tons of possibilities, and make tex unbeatable (against InDesign, etc) in value as a typographic tool for design-oriented layouts. Tools like InDesign, and Affinity Publisher allow linking text boxes that eventually flow into each other as a stream, and is something indispensable for a typographic system that envisions to cater to modern expressive typography, and plurality of designs. That can open up *tex as a typesetter to a much broader typesetting/design community.

9
  • 1
    The \vsplit primitive does that. I'll check whether there is a nice user interface. Dec 9, 2019 at 3:44
  • Yep, \vsplit indeed does that, though afaik width of a split cannot be changed. I tried unvboxing to change the width, it doesn't work because at that point its a collection of hboxes with newly inserted hyphens. If you are thinking of typesetting a vbox, and continuously splitting it, it has to respect the new width of new chunk & ideally not retypest the entire remaining part of \myverylongtext, as typesetting a vbox is more than O(n^2) complexity in my experiments.
    – codepoet
    Dec 9, 2019 at 3:49
  • 1
    Is LuaTeX possible? Dec 9, 2019 at 3:52
  • Yes LuaTex is allowed :)
    – codepoet
    Dec 9, 2019 at 3:53
  • magaz.sty does two or three typesettings to measure (count chunks). Also see truncate.sty Apr 1, 2020 at 10:54

1 Answer 1

2
+100

My solution is not exactly what you want but all information is managed at one place. I hope, that it is not problem to accept three passes over parameters.

The data should look like this:

%          lines  width
\declchunk    5     10cm ;
\declchunk    6     12cm ;
\declchunk   12     13cm ;
\declchunk    9     10cm ;
\declchunk    9     10cm ;
\declchunk    9     10cm ;
\declchunk    7      8cm ;
\declchunk    5     10cm ;

\formatchunks \myverylongtext

\setbox101 = \getnextchunk
\setbox102 = \getnextchunk  \rebox 102:{\raggedright}
\setbox103 = \getnextchunk
\setbox104 = \getnextchunk  \rebox 104:{\raggedleft} 
\setbox105 = \getnextchunk

% you can place these boxes everywhere. the following code is only for testing:

box 101: \par \box101 \bigskip
box 102: \par \box102 \bigskip
box 103: \par \box103 \bigskip
box 104: \par \box104 \bigskip
box 105: \par \box105 \bigskip

\bye

First, we dcelare dimensions of all chunks (by number of lines for heights and by dimensions for widths). Second, we format the given text. Finally, we can get a pieces of the data and save them to boxes, for example. They are justified to blocks by default. If we need to do another setting, then we can re-box these boxes. Our example shows that second box is ragged right and fourth box is ragged left. Moreover, you can do

\vbox to<dimension>{\unvbox103} \vtop to12mm{\unvbox105} etc. 

for example, because there is a stretchability and shrinkability between lines.

Implementation: we get all \parshape parameters by the list of \declchunks, then we format text using these parameters (only once) to \vbox\allchunks and then we do \vsplit when \getnextcunk is called. Finally we can do a little re-boxing if user need somewhat different form block justification.

My implementation works with plain TeX and classical TeX only. We needn't any TeX extension and (of course) we need not LaTeX:).

\newcount\tmpnum
\newcount\shapenum
\newcount\globpar
\newbox\allchunks
\newif\ifrepeat

\splittopskip=\baselineskip

\def\formatchunks#1{\setbox\allchunks=\vbox{%
   \def\par{\ifhmode\shapepar\fi}
   \def\shapepar{\prevgraf=\globpar 
      \parshape\shapenum\shapelist \endgraf
      \globpar=\prevgraf
      \ifnum\prevgraf>\shapenum \let\par=\endgraf \fi}
   \dimen0=\baselineskip \baselineskip=\dimen0 plus2pt minus2pt
   \widowpenalty=0 \clubpenalty=0 \brokenpenalty=0
   \penalty0 #1\vfil}
   \setbox0=\vsplit\allchunks to0pt % \splittopskip added
   \expandafter \renewsize \sizelist \relax
}

\def\shapelist{} \def\sizelist{}
\splittopskip=\baselineskip

\def\shapelist{} \def\sizelist{}

\def\declchunk #1 #2;{\edef\sizelist{\sizelist#1\space}
   \tmpnum=0
   \loop  \advance\tmpnum by1 \advance\shapenum by1
          \edef\shapelist{\shapelist 0pt#2}%
          \ifnum\tmpnum<#1 \repeat
}
\def\getnextchunk{\vsplit\allchunks to\size
   \ifx\sizelist\empty \def\size{\baselineskip}%
   \else \expandafter \renewsize \sizelist \relax \fi 
}
\def\renewsize #1 #2\relax{%
   \def\size{#1\baselineskip}\def\sizelist{#2}%
}

\def\rebox#1:#2{\setbox#1=\vbox{%
   \setbox0=\hbox{}%
   \repeattrue
   \def\raggedright{\rightskip=0pt plus1fill minus1em \relax}%
   \def\raggedleft{\leftskip=0pt plus1fill minus1em \relax}%
   \hsize=\wd#1 
   \unvbox#1
   \loop
      \setbox2=\lastbox
      \ifvoid2 \repeatfalse
      \else \setbox0=\hbox{\hbox{\unhbox2}\penalty-10000 \unhbox0 }
            \unskip\unskip\unpenalty
      \fi
      \ifrepeat \repeat
   \null
   #2\noindent \hfil \unhbox0 \par
}}

\def\myverylongtext{%
  Lorem ipsum dolor sit amet, consectetuer
  ...
  purus eget enim. Nunc vitae tortor. Proin tempus nibh sit amet nisl.
  Vivamus quis tortor vitae risus porta vehicula.
}
15
  • Thanks for your other idea, I copied both pieces of your code (definitions precede calls to macros) into a .tex file and ran it using luatex/xetex/tex. All gave the same error: ! Undefined control sequence. \declchunk ...elist {\sizelist #1\space } \tmpnum =0 \loop \advance \tmpnum .... Can you please mention what exact command are you using? And if I need to add anything to it?
    – codepoet
    Apr 3, 2020 at 5:47
  • @reportaman It was my fault: I missed first five lines from my code (\new... declarations) when I copied the code to this web site. Sorry. I correted it righ now.
    – wipet
    Apr 3, 2020 at 8:12
  • Note: the copy of ! Undefined control sequence error message cannot be done in comments here because it is compressed to one line. But the key information "what control sequence" is written at the edge of two lines in such message. In this case, the \tmpnum was the undefined control sequence.
    – wipet
    Apr 3, 2020 at 8:16
  • 1
    I looked into the file latex.tlx and I found that LaTeX redefines \par and \parshape parameters when qoute environment is used. I recommend: don't use LaTeX! Keep \global before \globpar=\prevgraf and try use plain TeX constructtion {\leftskip=2em \rightskip=2em text...\par}.
    – wipet
    Apr 6, 2020 at 21:06
  • 2
    I wrote (in these corona days) 24 pages summary of TeX principles. The document name is "TeX in nutshell" and it can be found at ctan.org/pkg/tex-nutshell .
    – wipet
    Apr 7, 2020 at 4:13

You must log in to answer this question.

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