diff options
Diffstat (limited to 'Master/texmf-dist/doc/context/sources/general/manuals/lowlevel/lowlevel-paragraphs.tex')
-rw-r--r-- | Master/texmf-dist/doc/context/sources/general/manuals/lowlevel/lowlevel-paragraphs.tex | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/context/sources/general/manuals/lowlevel/lowlevel-paragraphs.tex b/Master/texmf-dist/doc/context/sources/general/manuals/lowlevel/lowlevel-paragraphs.tex new file mode 100644 index 00000000000..3098333f013 --- /dev/null +++ b/Master/texmf-dist/doc/context/sources/general/manuals/lowlevel/lowlevel-paragraphs.tex @@ -0,0 +1,948 @@ +% language=us + +\environment lowlevel-style + +\startdocument + [title=paragraphs, + color=middlecyan] + +\startsection[title=Introduction] + +This manual is mostly discussing a few low level wrappers around low level \TEX\ +features. Its writing is triggered by an update to the \METAFUN\ and \LUAMETAFUN\ +manuals where we mess a bit with shapes. It gave a good reason to also cover some +more paragraph related topics but it might take a while to complete. Remind me if +you feel that takes too much time. + +Because paragraphs and their construction are rather central to \TEX, you can +imagine that the engine exposes dealing with them. This happens via commands +(primitives) but only when it's robust. Then there are callbacks, and some +provide detailed information about what we're dealing with. However, intercepting +node lists can already be hairy and we do that a lot in \CONTEXT. Intercepting +and tweaking paragraph properties is even more tricky, which is why we try to +avoid that in the core. But \unknown\ in the following sections you will see that +there are actually a couple of mechanism that do so. Often new features like this +are built in stepwise and enabled locally for a while and when they seem okay +they get enabled by default. \footnote {For this we have \type +{\enableexperiments} which one can use in \type {cont-loc.mkxl} or \type +{cont-exp.mkxl}, files that are loaded runtime when on the system. When you use +them, make sure they don't interfere; they are not part of the updates, contrary +to \type {cont-new.mkxl}.} + +\stopsection + +\startsection[title=Paragraphs] + +Before we demonstrate some trickery, let's see what a paragraph is. Normally a +document source is formatted like this: + +\starttyping[option=TEX] +some text (line 1) +some text (line 2) + +some more test (line 1) +some more test (line 2) +\stoptyping + +There are two blocks of text here separated by an empty line and they become two +paragraphs. Unless configured otherwise an empty line is an indication that we +end a paragraph. You can also explicitly do that: + +\starttyping[option=TEX] +some text (line 1) +some text (line 2) +\par +some more test (line 1) +some more test (line 2) +\stoptyping + +When \TEX\ starts a paragraph, it actually also does something think of: + +\starttyping[option=TEX] +[\the\everypar]some text (line 1) some text (line 2) \par +[\the\everypar]some more test (line 1) some more test (line 2) \par +\stoptyping + +or more accurate: + +\starttyping[option=TEX] +[\the\everypar]some text some text \par +[\the\everypar]some more test some more test \par +\stoptyping + +because the end|-|of|-|line character has become a space. As mentioned, +an empty line is actually the end of a paragraph. But in \LUAMETATEX\ +we can cheat a bit. If we have this: + +\startbuffer +line 1 + +line 2 +\stopbuffer + +\typebuffer[option=TEX] + +We can do this (watch how we need to permit overloading a primitive when we have +enabled \type {\overloadmode}): + +\startbuffer +\pushoverloadmode +\def\linepar{\removeunwantedspaces !\ignorespaces} +\popoverloadmode +line 1 + +line 2 +\stopbuffer + +\typebuffer[option=TEX] + +This comes out as: + +\start \getbuffer \stop + +I admit that since it got added (as part of some cleanup halfway the overhaul of +the engine) I never saw a reason to use it, but it is a cheap feature. The \type +{\linepar} primitive is undefined (\type {\undefined}) by default so no user sees +it anyway. Just don't use it unless maybe for some pseudo database trickery (I +considered using it for the database module but it is not needed). In a similar +fashion, just don't redefine \type {\par}: it's asking for troubles and \quote +{not done} in \CONTEXT\ anyway. + +Back to reality. In \LUATEX\ we get a node list that starts with a so called +\type {localpar} node and ends with a \type {\parfillskip}. The first node is +prepended automatically. That list travels through the system: hyphenation, +applying font properties, break the effectively one line into lines, wrap them +and add them to a vertical list, etc. Each stage can be intercepted via +callbacks. + +When the paragraph is broken into lines hanging indentation or a so called par +shape can be applied, and we will see more of that later, here we talk \type +{\par} and show another \LUAMETATEX\ trick: + +\startbuffer +\def\foo{{\bf test:} \ignorepars} + +\foo + +line +\stopbuffer + +\typebuffer[option=TEX] + +The macro typesets some text and then skips to the next paragraph: + +\start \getbuffer \stop + +Think of this primitive as being a more powerful variant of \type +{\ignorespaces}. This leaves one aspect: how do we start a paragraph. Technically +we need to force \TEX\ into so called horizontal mode. When you look at plain +\TEX\ documents you will notice commands like \type {\noindent} and \type +{\indent}. In \CONTEXT\ we have more high level variants, for instance we have +\type {\noindentation}. + +A robust way to make sure that you get in horizontal mode is using \type +{\dontleavehmode} which is a wink to \type {\leavevmode}, a command that you +should never use in \CONTEXT, so when you come from plain or \LATEX, it's one of +the commands you should wipe from your memory. + +When \TEX\ starts with a paragraph the \type {\everypar} token list is expanded +and again this is a primitive you should not mess with yourself unless in very +controlled situations. If you change its content, you're on your own with respect +to interferences and side effects. + +One of the things that \TEX\ does in injecting the indentation. Even when there +is none, it gets added, not as skip but as an empty horizontal box of a certain +width. This is easier on the engine when it constructs the paragraph from the one +liner: starting with a skip demands a bit more testing in the process (a nice +trick so to say). However, in \CONTEXT\ we enable the \LUAMETATEX\ feature that +does use a skip instead of a box. It's part of the normalization that is +discussed later. Instead of checking for a box with property indent, we check for +a skip with such property. This is often easier and cleaner. + +A bit off topic is the fact that in traditional \TEX\ empty lines or \type {\par} +primitives can trigger an error. This has to do with the fact that the program +evolved in a time where paper terminals were used and runtime could be excessive. +So, in order to catch a possible missing brace, a concept of \type {\long} +macros, permitting \type {\par} or equivalents in arguments, was introduced as +well as not permitting them in for instance display math. In \CONTEXT\ \MKII\ +most macros that could be sensitive for this were defined as \type {\long} so +that users never had to bother about it and probably were not even aware of it. +Right from the start in \LUATEX\ these error|-|triggers could be disabled which +of course we enable in \CONTEXT\ and in \LUAMETATEX\ these features have been +removed altogether. I don't think users will complain about this. + +If you want to enforce a newline but not a new paragraph you can use the \type +{\crlf} command. When used on its own it will produce an empty line. Don't use +this to create whitespace between lines. + +If you want to do something after so called par tokens are seen you can do this: + +\startbuffer +\def\foo{{\bf >>>> }} +\expandafterpars\foo + +this is a new paragraph ... + +\expandafterpars\foo +\par\par\par\par +this is a new paragraph ... +\stopbuffer + +\typebuffer[option=TEX] + +This not to be confused with \type {\everypar} which is a token list that \TEX\ +itself injects before each paragraph (also nested ones). + +\getbuffer + +This is typically a primitive that will only be used in macros. You can actually +program it using macros: pickup a token, check and push it back when it's not a +par equivalent token. The primitive is is just nicer (and easier on the log when +tracing is enabled). + +\stopsection + +\startsection[title=Properties] + +A paragraph is just a collection of lines that result from one input line that +got broken. This process of breaking into lines is influenced by quite some +parameters. In traditional \TEX\ and also in \LUAMETATEX\ by default the values +that are in effect when the end of the paragraph is met are used. So, when you +change them in a group and then ends the paragraph after the group, the values +you've set in the group are not used. + +However, in \LUAMETATEX\ we can optionally store them with the paragraph. When +that happens the values current at the start are frozen. You can still overload +them but that has to be done explicitly then. The advantage is that grouping no +longer interferes with the line break algorithm. The magic primitive is \type +{\snapshotpar} which takes a number made from categories mentioned below: + +\starttabulate[|l|l|r|] +\BC variable \BC category \BC code \NC \NR +\NC \type {\hsize} \NC hsize \NC 0x\uchexnumbers\frozenhsizecode \NC \NR +\NC \type {\leftskip} \NC skip \NC 0x\uchexnumbers\frozenskipcode \NC \NR +\NC \type {\rightskip} \NC skip \NC 0x\uchexnumbers\frozenskipcode \NC \NR +\NC \type {\hangindent} \NC hang \NC 0x\uchexnumbers\frozenhangcode \NC \NR +\NC \type {\hangafter} \NC hang \NC 0x\uchexnumbers\frozenhangcode \NC \NR +\NC \type {\parindent} \NC indent \NC 0x\uchexnumbers\frozenindentcode \NC \NR +\NC \type {\parfillleftskip} \NC par fill \NC 0x\uchexnumbers\frozenparfillcode \NC \NR +\NC \type {\parfillrightskip} \NC par fill \NC 0x\uchexnumbers\frozenparfillcode \NC \NR +\NC \type {\adjustspacing} \NC adjust \NC 0x\uchexnumbers\frozenadjustcode \NC \NR +\NC \type {\adjustspacingstep} \NC adjust \NC 0x\uchexnumbers\frozenadjustcode \NC \NR +\NC \type {\adjustspacingshrink} \NC adjust \NC 0x\uchexnumbers\frozenadjustcode \NC \NR +\NC \type {\adjustspacingstretch} \NC adjust \NC 0x\uchexnumbers\frozenadjustcode \NC \NR +\NC \type {\protrudechars} \NC protrude \NC 0x\uchexnumbers\frozenprotrudecode \NC \NR +\NC \type {\pretolerance} \NC tolerance \NC 0x\uchexnumbers\frozentolerancecode \NC \NR +\NC \type {\tolerance} \NC tolerance \NC 0x\uchexnumbers\frozentolerancecode \NC \NR +\NC \type {\emergencystretch} \NC stretch \NC 0x\uchexnumbers\frozenstretchcode \NC \NR +\NC \type {\looseness} \NC looseness \NC 0x\uchexnumbers\frozenloosenesscode \NC \NR +\NC \type {\lastlinefit} \NC last line \NC 0x\uchexnumbers\frozenlastlinecode \NC \NR +\NC \type {\linepenalty} \NC line penalty \NC 0x\uchexnumbers\frozenlinepenaltycode \NC \NR +\NC \type {\interlinepenalty} \NC line penalty \NC 0x\uchexnumbers\frozenlinepenaltycode \NC \NR +\NC \type {\interlinepenalties} \NC line penalty \NC 0x\uchexnumbers\frozenlinepenaltycode \NC \NR +\NC \type {\clubpenalty} \NC club penalty \NC 0x\uchexnumbers\frozenclubpenaltycode \NC \NR +\NC \type {\clubpenalties} \NC club penalty \NC 0x\uchexnumbers\frozenclubpenaltycode \NC \NR +\NC \type {\widowpenalty} \NC widow penalty \NC 0x\uchexnumbers\frozenwidowpenaltycode \NC \NR +\NC \type {\widowpenalties} \NC widow penalty \NC 0x\uchexnumbers\frozenwidowpenaltycode \NC \NR +\NC \type {\displaywidowpenalty} \NC display penalty \NC 0x\uchexnumbers\frozendisplaypenaltycode \NC \NR +\NC \type {\displaywidowpenalties} \NC display penalty \NC 0x\uchexnumbers\frozendisplaypenaltycode \NC \NR +\NC \type {\brokenpenalty} \NC broken penalty \NC 0x\uchexnumbers\frozenbrokenpenaltycode \NC \NR +\NC \type {\adjdemerits} \NC demerits \NC 0x\uchexnumbers\frozendemeritscode \NC \NR +\NC \type {\doublehyphendemerits} \NC demerits \NC 0x\uchexnumbers\frozendemeritscode \NC \NR +\NC \type {\finalhyphendemerits} \NC demerits \NC 0x\uchexnumbers\frozendemeritscode \NC \NR +\NC \type {\parshape} \NC shape \NC 0x\uchexnumbers\frozenshapecode \NC \NR +\NC \type {\baselineskip} \NC line \NC 0x\uchexnumbers\frozenlinecode \NC \NR +\NC \type {\lineskip} \NC line \NC 0x\uchexnumbers\frozenlinecode \NC \NR +\NC \type {\lineskiplimit} \NC line \NC 0x\uchexnumbers\frozenlinecode \NC \NR +\NC \type {\hyphenationmode} \NC hyphenation \NC 0x\uchexnumbers\frozenhyphenationcode \NC \NR +\stoptabulate + + +As you can see here, there are more paragraph related parameters than in for +instance \PDFTEX\ and \LUATEX\ and these are (to be) explained in the +\LUAMETATEX\ manual. You can imagine that keeping this around with the paragraph +adds some extra overhead to the machinery but most users won't notice that +because is is compensated by gains elsewhere. + +This is pretty low level and there are a bunch of helpers that support this but +these are not really user level macros. As with everything \TEX\ you can mess +around as much as you like, and the code gives plenty of examples but when you do +this, you're on your own because it can interfere with \CONTEXT\ core +functionality. + +In \LMTX\ taking these snapshots is turned on by default and because it thereby +fundamentally influences the par builder, users can run into compatibility issues +but in practice there has been no complaints (and this feature has been in use +quite a while before this document was written). One reason for users not +noticing is that one of the big benefits is probably handled by tricks mentioned +on the mailing list. Imagine that you have this: + +\starttyping[option=TEX] +{\bf watch out:} here is some text +\stoptyping + +In this small example the result will be as expected. But what if something magic +with the start of a paragraph is done? Like this: + +\starttyping[option=TEX] +\placefigure[left]{A cow!}{\externalfigure[cow.pdf]} + +{\bf watch out:} here is some text ... of course much more is needed to + get a flow around the figure! +\stoptyping + +The figure will hang at the left side of the paragraph but it is put there when +the text starts and that happens inside the bold group. It means that the +properties we set in order to get the shape around the figure are lost as soon as +we're at \quote{\type {here is some text}} and definitely is wrong when the +paragraph ends and the par builder has to use them to get the shape right. We get +text overlapping the figure. A trick to overcome this is: + +\starttyping[option=TEX] +\dontleavehmode {\bf watch out:} here is some text ... of course much + more is needed to get a flow around the figure! +\stoptyping + +where the first macro makes sure we already start a paragraph before the group is +entered (using a \type {\strut} also works). It's not nice and I bet users have +been bitten by this and by now know the tricks. But, with snapshots such fuzzy +hacks are not needed any more! The same is true with this: + +\starttyping[option=TEX] +{\leftskip 1em some text \par} +\stoptyping + +where we had to explicitly end the paragraph inside the group in order to retain +the skip. I suppose that users normally use the high level environments so they +never had to worry about this. It's also why users probably won't notice that +this new mechanism has been active for a while. Actually, when you now change a +parameter inside the paragraph its new value will not be applied (unless you +prefix it with \type {\frozen} or snapshot it) but no one did that anyway. + +\stopsection + +\startsection[title=Wrapping up] + +In \CONTEXT\ \LMTX\ we have a mechanism to exercise macros (or content) before a +paragraph ends. This is implemented using the \type {\wrapuppar} primitive. The +to be wrapped up material is bound to the current paragraph which in order to +get this done has to be started when this primitive is used. + +Although the high level interface has been around for a while it still needs a +bit more testing (read: use cases are needed). In the few cases where we already +use it application can be different because again it relates to snapshots. This +because in the past we had to use tricks that also influenced the user interface +of some macros (which made them less natural as one would expect). So the +question is: where do we apply it in old mechanisms and where not. + +{\em todo: accumulation, interference, where applied, limitations} + +% \vbox {vbox : \wrapuppar{1}test\par x\wrapuppar{2}test}\blank +% \vtop {vtop : \wrapuppar{1}test\par x\wrapuppar{2}test}\blank +% \vcenter{vcenter : \wrapuppar{1}test\par x\wrapuppar{2}test}\blank +% $$x = \vcenter{vcenter : \wrapuppar{1}test\par x\wrapuppar{2}test}$$\blank +% x\vadjust{vadjust : \wrapuppar{1}test\par x\wrapuppar{2}test}x\blank + +\stopsection + +\startsection[title=Hanging] + +There are two mechanisms for getting a specific paragraph shape: rectangular +hanging and arbitrary shapes. Both mechanisms work top|-|down. The first +mechanism uses a combination of \type {\hangafter} and \type {\hangindent}, and +the second one depends on \type {\parshape}. In this section we discuss the +rectangular one. + +\startbuffer[demo-5] +\hangafter 4 \hangindent 4cm \samplefile{tufte} \page +\hangafter -4 \hangindent 4cm \samplefile{tufte} \page +\hangafter 4 \hangindent -4cm \samplefile{tufte} \page +\hangafter -4 \hangindent -4cm \samplefile{tufte} \page +\stopbuffer + +\typebuffer[demo-5][option=TEX] + +As you can see in \in {figure} [fig:hang], the four cases are driven by the sign +of the values. If you want to hang into the margin you need to use different +tricks, like messing with the \type {\leftskip}, \type {\rightskip} or \type +{\parindent} parameters (which then of course can interfere with other mechanisms +uses at the same time). + +\startplacefigure[title=Hanging indentation,reference=fig:hang] +\startcombination[nx=2,ny=2] + {\typesetbuffer[demo-5][page=1,width=.4\textwidth,frame=on]} {\type{\hangafter +4 \hangindent +4cm}} + {\typesetbuffer[demo-5][page=2,width=.4\textwidth,frame=on]} {\type{\hangafter -4 \hangindent +4cm}} + {\typesetbuffer[demo-5][page=3,width=.4\textwidth,frame=on]} {\type{\hangafter +4 \hangindent -4cm}} + {\typesetbuffer[demo-5][page=4,width=.4\textwidth,frame=on]} {\type{\hangafter -4 \hangindent -4cm}} +\stopcombination +\stopplacefigure + +\stopsection + +\startsection[title=Shapes] + +In \CONTEXT\ we don't use \type {\parshape} a lot. It is used in for instance +side floats but even there not in all cases. It's more meant for special +applications. This means that in \MKII\ and \MKIV\ we don't have some high level +interface. However, when \METAFUN\ got upgraded to \LUAMETAFUN, and the manual +also needed an update, one of the examples in that manual that used shapes also +got done differently (read: nicer). And that triggered the arrival of a new low +level shape mechanism. + +One important property of the \type {\parshape} mechanism is that it works per +paragraph. You define a shape in terms of a left margin and width of a line. The +shape has a fixed number of such pairs and when there is more content, the last +one is used for the rest of the lines. When the paragraph is finished, the shape +is forgotten. \footnote {Not discussed here is a variant that might end up in +\LUAMETATEX\ that works with the progression, i.e.\ takes the height of the +content so far into account. This is somewhat tricky because for that to work +vertical skips need to be frozen, which is no real big deal but has to be done +careful in the code.} + +The high level interface is a follow up on the example in the \METAFUN\ manual and +uses shapes that carry over to the next paragraph. In addition we can cycle over +a shape. In this interface shapes are defined using keyword. Here are some +examples: + +\starttyping[option=TEX] +\startparagraphshape[test] + left 1mm right 1mm + left 5mm right 5mm +\stopparagraphshape +\stoptyping + +This shape has only two entries so the first line will have a 1mm margin while +later lines will get 5mm margins. This translates into a \type {\parshape} like: + +\starttyping[option=TEX] +\parshape 2 + 1mm \dimexpr\hsize-1mm\relax + 5mm \dimexpr\hsize-5mm\relax +\stoptyping + +Watch the number \type {2}: it tells how many specification lines follow. As you +see, we need to calculate the width. + +\starttyping[option=TEX] +\startparagraphshape[test] + left 1mm right 1mm + left 5mm right 5mm + repeat +\stopparagraphshape +\stoptyping + +This variant will alternate between 1mm and 5mm margins. The repeating feature is +translated as follows. Maybe at some point I will introduce a few more options. + +\starttyping[option=TEX] +\parshape 2 options 1 + 1mm \dimexpr\hsize-1mm\relax + 5mm \dimexpr\hsize-5mm\relax +\stoptyping + +A shape can have some repetition, and we can save keystrokes by copying the last +entry. The resulting \type {\parshape} becomes rather long. + +\starttyping[option=TEX] +\startparagraphshape[test] + left 1mm right 1mm + left 2mm right 2mm + left 3mm right 3mm + copy 8 + left 4mm right 4mm + left 5mm right 5mm + left 5mm hsize 10cm +\stopparagraphshape +\stoptyping + +Also watch the \type {hsize} keyword: we don't calculate the hsize from the \type +{left} and \type {right} values but explicitly set it. + +\starttyping[option=TEX] +\startparagraphshape[test] + left 1mm right 1mm + right 3mm + left 5mm right 5mm + repeat +\stopparagraphshape +\stoptyping + +When a \type {right} keywords comes first the \type {left} is assumed to be zero. +In the examples that follow we will use a couple of definitions: + +\startbuffer[setup-0] +\startparagraphshape[test] + both 1mm both 2mm both 3mm both 4mm both 5mm both 6mm + both 7mm both 6mm both 5mm both 4mm both 3mm both 2mm +\stopparagraphshape +\stopbuffer + +\startbuffer[setup-0-repeat] +\startparagraphshape[test-repeat] + both 1mm both 2mm both 3mm both 4mm both 5mm both 6mm + both 7mm both 6mm both 5mm both 4mm both 3mm both 2mm + repeat +\stopparagraphshape +\stopbuffer + +\typebuffer[setup-0,setup-0-repeat][option=TEX] + +The last one could also be defines as: + +\starttyping[option=TEX] +\startparagraphshape[test-repeat] + \rawparagraphshape{test} repeat +\stopparagraphshape +\stoptyping + +In the previous code we already introduced the \type {repeat} option. This will +make the shape repeat at the engine level when the shape runs out of specified +lines. In the application of a shape definition we can specify a \type {method} +to be used and that determine if the next paragraph will start where we left off +and discard afterwards (\type {shift}) or that we move the discarded lines up +front so that we never run out of lines (\type {cycle}). It sounds complicated +but just keep in mind that \type {repeat} is part of the \type {\parshape} and +act within a paragraph while \type {shift} and \type {cycle} are applied when a +new paragraph is started. + +\startbuffer[demo-1] +\startshapedparagraph[list=test] + \dorecurse{8}{\showparagraphshape\samplefile{tufte} \par} +\stopshapedparagraph +\stopbuffer + +\startbuffer[demo-1-repeat] +\startshapedparagraph[list=test-repeat] + \dorecurse{8}{\showparagraphshape\samplefile{tufte} \par} +\stopshapedparagraph +\stopbuffer + +In \in {figure} [fig:shape:discard] you see the following applied: + +\typebuffer[demo-1,demo-1-repeat][option=TEX] + +\startplacefigure[title=Discarded shaping,reference=fig:shape:discard] +\startcombination[nx=2,ny=2] + {\typesetbuffer[setup-0,demo-1] [page=1,width=.4\textwidth,frame=on]} {discard, finite shape, page 1} + {\typesetbuffer[setup-0,demo-1] [page=2,width=.4\textwidth,frame=on]} {discard, finite shape, page 2} + {\typesetbuffer[setup-0,demo-1-repeat][page=1,width=.4\textwidth,frame=on]} {discard, repeat in shape, page 1} + {\typesetbuffer[setup-0,demo-1-repeat][page=2,width=.4\textwidth,frame=on]} {discard, repeat in shape, page 2} +\stopcombination +\stopplacefigure + +In \in {figure} [fig:shape:shift] we use this instead: + +\startbuffer[demo-2] +\startshapedparagraph[list=test,method=shift] + \dorecurse{8}{\showparagraphshape\samplefile{tufte} \par} +\stopshapedparagraph +\stopbuffer + +\startbuffer[demo-2-shift] +\startshapedparagraph[list=test-repeat,method=shift] + \dorecurse{8}{\showparagraphshape\samplefile{tufte} \par} +\stopshapedparagraph +\stopbuffer + +\typebuffer[demo-2,demo-2-repeat][option=TEX] + +\startplacefigure[title=Shifted shaping,reference=fig:shape:shift] +\startcombination[nx=2,ny=2] + {\typesetbuffer[setup-0,demo-2][page=1,width=.4\textwidth,frame=on]} {shift, finite shape, page 1} + {\typesetbuffer[setup-0,demo-2][page=2,width=.4\textwidth,frame=on]} {shift, finite shape, page 2} + {\typesetbuffer[setup-0-repeat,demo-2-shift][page=1,width=.4\textwidth,frame=on]} {shift, repeat in shape, page 1} + {\typesetbuffer[setup-0-repeat,demo-2-shift][page=2,width=.4\textwidth,frame=on]} {shift, repeat in shape, page 2} +\stopcombination +\stopplacefigure + +Finally, in \in {figure} [fig:shape:cycle] we use: + +\startbuffer[demo-3] +\startshapedparagraph[list=test,method=cycle] + \dorecurse{8}{\showparagraphshape\samplefile{tufte} \par} +\stopshapedparagraph +\stopbuffer + +\startbuffer[demo-3-cycle] +\startshapedparagraph[list=test-repeat,method=cycle] + \dorecurse{8}{\showparagraphshape\samplefile{tufte} \par} +\stopshapedparagraph +\stopbuffer + +\typebuffer[demo-3,demo-3-repeat][option=TEX] + +\startplacefigure[title=Cycled shaping,reference=fig:shape:cycle] +\startcombination[nx=2,ny=2] + {\typesetbuffer[setup-0,demo-3][page=1,width=.4\textwidth,frame=on]} {cycle, finite shape, page 1} + {\typesetbuffer[setup-0,demo-3][page=2,width=.4\textwidth,frame=on]} {cycle, finite shape, page 2} + {\typesetbuffer[setup-0-repeat,demo-3-cycle][page=1,width=.4\textwidth,frame=on]} {cycle, repeat in shape, page 1} + {\typesetbuffer[setup-0-repeat,demo-3-cycle][page=2,width=.4\textwidth,frame=on]} {cycle, repeat in shape, page 2} +\stopcombination +\stopplacefigure + +These examples are probably too small to see the details but you can run them +yourself or zoom in on the details. In the margin we show the values used. Here +is a simple example of (non) poetry. There are other environments that can be +used instead but this makes a good example anyway. + +\startbuffer +\startparagraphshape[test] + left 0em right 0em + left 1em right 0em + repeat +\stopparagraphshape + +\startshapedparagraph[list=test,method=cycle] + verse line 1.1\crlf verse line 2.1\crlf + verse line 3.1\crlf verse line 4.1\par + verse line 1.2\crlf verse line 2.2\crlf + verse line 3.2\crlf verse line 4.2\crlf + verse line 5.2\crlf verse line 6.2\par +\stopshapedparagraph +\stopbuffer + +\typebuffer[option=TEX] + +\start \getbuffer \stop + +Because the idea for this feature originates in \METAFUN, we will now kick in +some \METAPOST. The following code creates a shape for a circle. We use a +2mm offset here: + +\startbuffer +\startuseMPgraphic{circle} + path p ; p := fullcircle scaled TextWidth ; + build_parshape(p, + 2mm, 0, 0, + LineHeight, StrutHeight, StrutDepth, StrutHeight + ) ; +\stopuseMPgraphic +\stopbuffer + +\typebuffer[option=TEX] + +\start \getbuffer \stop + +We plug this into the already described macros: + +\startbuffer +\startshapedparagraph[mp=circle]% + \setupalign[verytolerant,stretch,last]% + \samplefile{tufte} + \samplefile{tufte} +\stopshapedparagraph +\stopbuffer + +\typebuffer[option=TEX] + +And get ourself a circular shape. Watch out, at this moment the shape environment +does not add grouping so when as in this case you change the alignment it can +influence the document. + +\start \getbuffer \stop + +\startbuffer[framed] +\framed[align=normal,width=\textwidth,offset=2mm,strut=no]\bgroup + \getbuffer +\egroup +\stopbuffer + +Assuming that the shape definition above is in a buffer we can do this: + +\typebuffer[option=TEX] + +The result is shown in \in {figure} [fig:shape:circle]. Because all action +happens in the framed environment, we can also use this definition: + +\starttyping[option=TEX] +\startuseMPgraphic{circle} + path p ; p := fullcircle scaled \the\dimexpr\framedwidth+\framedoffset*2\relax ; + build_parshape(p, + \framedoffset, 0, 0, + LineHeight, StrutHeight, StrutDepth, StrutHeight + ) ; + draw p ; +\stopuseMPgraphic +\stoptyping + +\startplacefigure[title=A framed circular shape,reference=fig:shape:circle] + \getbuffer[framed] +\stopplacefigure + +A mechanism like this is often never completely automatic in the sense that you +need to keep an eye on the results. Depending on user demands more features can +be added. With weird shapes you might want to set up the alignment to be \type +{tolerant} and have some \type {stretch}. + +The interface described in the \METAFUN\ manual is pretty old, the time stamp of +the original code is mid 2000, but the principles didn't change. The examples in +\type {meta-imp-txt.mkxl} can now be written as: + +\startuseMPgraphic{test 1} + begingroup ; + save p ; path p ; p := fullcircle scaled 6cm ; + lmt_parshape [ + path = p, + offset = BodyFontSize/2, + dx = 0, % default + dy = 0, % default + lineheight = LineHeight, % default + strutheight = StrutHeight, % default + strutdepth = StrutDepth, % default + topskip = StrutHeight, % default + ] ; + draw p withpen pencircle scaled 1pt ; + endgroup ; +\stopuseMPgraphic + +\startuseMPgraphic{test 2} + begingroup ; + save p ; path p ; p := fullsquare rotated 45 scaled 5cm ; + lmt_parshape [ + path = p, + offset = BodyFontSize/2, + trace = true, + ] ; + draw p withpen pencircle scaled 1pt ; + endgroup ; +\stopuseMPgraphic + +\startuseMPgraphic{test 3} + begingroup ; + save w, h, p ; path p ; w := h := 6cm ; + p := (.5w,h) -- ( 0, h) -- (0,0) -- (w,0) & + ( w,0) .. (.75w,.5h) .. (w,h) & (w,h) -- cycle ; + lmt_parshape [ + path = p, + offset = BodyFontSize/2, + ] ; + draw p withpen pencircle scaled 1pt ; + endgroup ; +\stopuseMPgraphic + +\startuseMPgraphic{test 4} + begingroup ; + save d, p, q ; path p, q ; d := BodyFontSize/2; + vardef shape(expr w, h, o) = + (o,o) -- (w-o,o) & (w-o,o) .. (.75w-o,.5h) .. + (w-2o,h-o) & (w-2o,h-o) -- (o,h-o) -- cycle + enddef ; + p := shape(6cm, 6cm, d) ; q := shape(6cm, 6cm, 0) ; + lmt_parshape [ + path = p, + offsetpath = q, + dx = d, + dy = d, + trace = true, + ] ; + draw q withpen pencircle scaled 1pt ; + endgroup ; +\stopuseMPgraphic + +\defineoverlay[test 1][\useMPgraphic{test 1}] +\defineoverlay[test 2][\useMPgraphic{test 2}] +\defineoverlay[test 3][\useMPgraphic{test 3}] +\defineoverlay[test 4][\useMPgraphic{test 4}] + +\startbuffer + \startshapetext[test 1,test 2,test 3,test 4] + \setupalign[verytolerant,stretch,normal]% + \samplefile{douglas} % Douglas R. Hofstadter + \stopshapetext + \startcombination[2*2] + {\framed[offset=overlay,frame=off,background=test 1]{\getshapetext}} + {test 1} + {\framed[offset=overlay,frame=off,background=test 2]{\getshapetext}} + {test 2} + {\framed[offset=overlay,frame=off,background=test 3]{\getshapetext}} + {test 3} + {\framed[offset=overlay,frame=off,background=test 4]{\getshapetext}} + {test 4} + \stopcombination +\stopbuffer + +\typebuffer[option=TEX] + +In \in {figure} [fig:shapes:chain] we see the result. Watch how for two shapes +we have enabled tracing. Of course you need to tweak till all fits well but we're +talking of special situations anyway. + +\startplacefigure[Title=Multiple shapes,reference=fig:shapes:chain] + \getbuffer +\stopplacefigure + +Here is a bit more extreme example. Again we use a circle: + +\startbuffer +\startuseMPgraphic{circle} + lmt_parshape [ + path = fullcircle scaled 136mm, + offset = 2mm, + bottomskip = - 1.5LineHeight, + ] ; +\stopuseMPgraphic +\stopbuffer + +\typebuffer[option=TEX] + +But we output a longer text: + +\startbuffer +\startshapedparagraph[mp=circle,repeat=yes,method=cycle]% + \setupalign[verytolerant,stretch,last]\dontcomplain + {\darkred \samplefile{tufte}}\par + {\darkgreen \samplefile{tufte}}\par + {\darkblue \samplefile{tufte}}\par + {\darkcyan \samplefile{tufte}}\par + {\darkmagenta \samplefile{tufte}}\par +\stopshapedparagraph +\stopbuffer + +\typebuffer[option=TEX] + +We get a multi|-|page shape: + +\start \getbuffer \stop + +Compare this with: + +\startbuffer +\startshapedparagraph[mp=circle,repeat=yes,method=cycle]% + \setupalign[verytolerant,stretch,last]\dontcomplain + {\darkred \samplefile{tufte}} + {\darkgreen \samplefile{tufte}} + {\darkblue \samplefile{tufte}} + {\darkcyan \samplefile{tufte}} + {\darkmagenta \samplefile{tufte}} +\stopshapedparagraph +\stopbuffer + +\typebuffer[option=TEX] + +Which gives: + +\start \getbuffer \stop + +Here the \type {bottomskip} takes care of subtle rounding issues as well as +discarding the last line in the shape so that we get nicer continuation. There is +no full automated solution for all you can come up with. + +Mixing a \METAPOST\ specification into a regular one is also possible. The next +example demonstrates this as well as the option to remove some lines from a +specification: + +\starttyping[option=TEX] +\startparagraphshape[test] + left 0em right 0em + left 1em right 0em + metapost {circle} + delete 3 + metapost {circle,circle,circle} + delete 7 + metapost {circle} + repeat +\stopparagraphshape +\stoptyping + +You can combine a shape with narrowing a paragraph. Watch the \type {absolute} +keyword in the next code. The result is shown in \in {figure} [fig:shape:skips]. + +\startbuffer[demo-4] +\startuseMPgraphic{circle} + lmt_parshape [ + path = fullcircle scaled TextWidth, + bottomskip = - 1.5LineHeight, + ] ; +\stopuseMPgraphic + +\startparagraphshape[test-1] + metapost {circle} repeat +\stopparagraphshape + +\startparagraphshape[test-2] + absolute left metapost {circle} repeat +\stopparagraphshape + +\startparagraphshape[test-3] + absolute right metapost {circle} repeat +\stopparagraphshape + +\startparagraphshape[test-4] + absolute both metapost {circle} repeat +\stopparagraphshape + +\showframe + +\startnarrower[4*left,2*right] + \startshapedparagraph[list=test-1,repeat=yes,method=repeat]% + \setupalign[verytolerant,stretch,last]\dontcomplain + \dorecurse{3}{\samplefile{thuan}} + \stopshapedparagraph + \page + \startshapedparagraph[list=test-2,repeat=yes,method=repeat]% + \setupalign[verytolerant,stretch,last]\dontcomplain + \dorecurse{3}{\samplefile{thuan}} + \stopshapedparagraph + \page + \startshapedparagraph[list=test-3,repeat=yes,method=repeat]% + \setupalign[verytolerant,stretch,last]\dontcomplain + \dorecurse{3}{\samplefile{thuan}} + \stopshapedparagraph + \page + \startshapedparagraph[list=test-4,repeat=yes,method=repeat]% + \setupalign[verytolerant,stretch,last]\dontcomplain + \dorecurse{3}{\samplefile{thuan}} + \stopshapedparagraph +\stopnarrower +\stopbuffer + +\typebuffer[demo-4][option=TEX] + +\startplacefigure[title=Skip compensation,reference=fig:shape:skips] +\startcombination[nx=2,ny=2] + {\typesetbuffer[demo-4][page=1,width=.4\textwidth,frame=on]} {test 1} + {\typesetbuffer[demo-4][page=2,width=.4\textwidth,frame=on]} {test 2, left} + {\typesetbuffer[demo-4][page=3,width=.4\textwidth,frame=on]} {test 3, right} + {\typesetbuffer[demo-4][page=4,width=.4\textwidth,frame=on]} {test 4, both} +\stopcombination +\stopplacefigure + +\stopsection + +% \startsection[title=Linebreaks] +\startsection[title=Modes] + +% \ruledvbox{1\ifhmode\writestatus{!}{HMODE 1}\fi} % hsize +% \ruledvbox{\hbox{\strut 2}\ifhmode\writestatus{!}{HMODE 2}\fi} % fit +% \ruledvbox{\hbox{\strut 3}\hbox{\strut 3}\ifhmode\writestatus{!}{HMODE 3}\fi} % fit +% \ruledvbox{\hbox{\strut 4}4\ifhmode\writestatus{!}{HMODE 4}\fi} % hsize +% \ruledvbox{\hbox{\strut 5}5\hbox{\strut 5}\ifhmode\writestatus{!}{HMODE 5}\fi} % hsize +% \ruledvbox{6\hbox{\strut 6}\ifhmode\writestatus{!}{HMODE 6}\fi} % hsize + +{\em todo: some of the side effects of so called modes} + +\stopsection + +\startsection[title=Normalization] + +{\em todo: users don't need to bother about this but it might be interesting anyway} + +\stopsection + +\stopdocument + +\everyhbox \everyvbox : useless unless one resets +\parattr +\snapshotpar +\wrapuppar + +% \parfillleftskip + +% I rewarded myself after writing a section by watching the video "Final Thing On +% My Mind", The Pineapple This, Live, 2020, the usual perfect GH performance, +% wondering if live would turn to normal so that we could go to such concerts once +% again given successive covids. Writing manuals can do with a distraction. +% +% Gavin Harrison: Soundcheck, Drummerworld Jan 27, 2021 ... I wish I could make +% something called a check into pefect solo. Okay, another section and I'll check +% out the latest Simon Phillips and other favourite dummer uploads. |