Unexpected behavior

From Wiki
Revision as of 06:03, 14 February 2020 by Henri (talk | contribs) (→‎Assignments)
Jump to navigation Jump to search

If ConTeXt appears to behave in a counterintuitive way, chances are that it’s actually your intuitions that lack calibration.

Controlling Page Break Before Headings

If you add the option page=yes to a heading setup, this will cause ConTeXt to break the page. Ordinarily this works as expected, unless, however, the heading immediately follows a previous section heading with no text in between. The rationale behind this is that consecutive headings are conceived of collectively as one single structural element. For example, the following code will generate only a single page break although the unit \section appears twice.

\setuphead[section][page=yes] %% toggle page breaking

\starttext
  \chapter{foo} %% structural inhibits breaking
  \section{bar} \input knuth    %% -> no break!
  \section{baz} \input dawkins  %% -> break
\stoptext

In order to get the page break at the section regardless of surrounding structurals elements, you need to unset the heading parameter continue as well. [1]

\setuphead[section][page=yes,continue=no]

\starttext
  \chapter{foo}
  \section{bar} \input knuth    %% -> break
  \section{baz} \input dawkins  %% -> break
\stoptext

(For the curious, the explanation can be found in the definition of the macro \strc_sectioning_handle_page_nop in strc-sec.mkiv).


Unsolicited Vertical Mode

Some commands like \framed cause line breaks to happen if used in vertical mode, e.g. at the beginning of a line. Example:

\starttext
\framed{foo} bar.
\stoptext

The explanation according to the corresponding FAQ item is that before the token \frame is encountered, TEX is in vertical mode. This state is preserved until after TEX finishes typesetting the \framed macro. Consequently, horizontal mode (where paragraphs are made) is entered only at the tokens bar.

To have the frame begin the paragraph instead, horizontal mode will have to be initiated explicitly. There are a couple macros for this purpose: the very basic \leavevmode (syst-ini.mkiv; inserts an empty box, same as in the Plain format) and the more familiar \dontleavehmode (syst-aux.mkiv). Just use the chosen macro immediately before the \framed macro and everything should be fine:

\starttext
\leavevmode     \framed{foo} bar.\par
\dontleavehmode \framed{foo} bar.
\stoptext

Disappearing Crop Marks

Crop marks, activated as \setuplayout[marking=on], are a useful feature that Context supports out of the box. They have a peculiarity, though, which my make them useless in specific cases: They are, by definition, located outside the page dimension. This means that they show up iff the paper size exceeds the page size.

Thus if you need auxiliary lines for cutting but have the pages fit the paper size exactly, you can instead resort to enabling the page background frame. This example sums up the solutions posted by Wolfgang and Marco on [http://archive.contextgarden.net/message/20120605.202113.0989aa34.en.html the Context Mailing List].

%% the paper size will be a multiple of the page sizes
\setuppapersize [A6,landscape] [A3] 
\setuplayout    [nx=2,ny=4]%, marking=on] 
%% enable the page frame
\setupbackgrounds   [page] [frame=on]
%% some further display setups
\setuppagenumbering [location=] 
\setupbackgrounds   [page]      [background=color, backgroundcolor=gray] 
\setupbodyfont      [sans,58pt] 
%% testing ...
\starttext 
\dorecurse{4} 
 {\null\vfill\centerline\recurselevel\vfill\null\page} 
\stoptext

Left and Right

When it comes to the justification of paragraphs, do not trust your intuitions about handedness. This is a FAQ item.

Footnotes: The Difference between \setupnotation and \setupnote

There is a bit of terminology mess concerning notes.

Instruction Goal
\setupnotation This command configures the note insert, i.e. the textual content that will usually be placed at the bottom (with footnotes) or the end of the text (with endnotes). (The control sequence used to be \setupnotedefinition.)
\setupnote Configure the note environment where the inserts will be located. Inherits some parameters from \framed.
\setupnote[textstyle=,textcommand=] Configure the note symbol as appears in the main text, where the note macro is called.
Plural forms \setupnotes, \setupnotations These are synonyms for their singular forms.
\setupfootnotes This is equivalent to \setupnote[footnote].

Summary: to setup a blue note, you would first need to define and configure the insert via \setupnotation:

\definenote [bluenote] [footnote]
\setupnotation [bluenote] [
  color=blue,
  style=bf,
]
 
\starttext foo\bluenote{bar} baz \stoptext

Now adjust the container where the blue notes will reside at the bottom of the page (\setupnote):

\definenote [bluenote] [footnote]
\setupnote [bluenote] [
  frame=on,          %% frame containing all inserts
  framecolor=blue,
  background=screen,
  rulecolor=blue,    %% the line above the inserts
  rulethickness=1pt, %% both the frame and line width
]

\starttext foo\bluenote{bar} baz \bluenote{xyzzy} \stoptext

Finally, direct your attention to the note indicator, most commonly a number or a symbol. For precise control over the placement define a monadic macro and hook it into textcommand.

\setupnote [bluenote] [
  textstyle=\tx\sans\bold\blue,
  textcommand=\myfootnotecommand,
]
\define[1]\myfootnotecommand{\rotate[rotation=42]{#1}}

\starttext foo\bluenote{bar} baz \bluenote{xyzzy} \stoptext

(For more definite answers concerning the notes mechanism, use the source, Luke: strc-not.mkvi.)


Float Insertion Issues

Floating objects can be tricky. Deciding where they fit best is hard enough, actually getting them there may be a lot tougher. Inserting a float will force a line break where the object is referenced in the source code. Thus, very long paragraphs may not leave an opportunity to inject the float if they cover the entire page. [2]

\starttext
  Cows make
  \placefigure[top]{A genuine Dutch cow.}{\externalfigure[cow]}
  great pets.
\stoptext

In these cases the postponing mechanism offers a reliable way out (\startpostponing). It lets you specify an offset (in pages) by which the content of the postponing environment will be delayed. In order to place a floating object at the top of the nth page from the location it is encountered, its argument has needs to be [+n]. (Absolute pages can be specified as simply [n].) E. g. to put the foat on the following page:

\starttext
  \startpostponing[+1]
    \placefigure[top]{A genuine Dutch cow.}{\externalfigure[cow.pdf]}
  \stoppostponing
  \input knuth
  \page
  Cows make great pets.
\stoptext

Another solution can be the less-known hangaround environment (\starthangaround, cf. Hangaround). It lets the text of a given paragraph (the content of the environment) flow around a box (the first argument). In contrast to real floats it does not place a caption. This poses a problem in MkIV as the new code will not allow placing captions arbitrarily. [[3]] (If you desparately need separate captions please send a feature request to one of the ConTeXt_Mailing_Lists#Mailing Lists).

\starttext
\input dawkins

\starthangaround{
  \framed[align=right,frame=off,width=.3\textwidth]{
    \externalfigure [cow] [width=.3\textwidth]\crlf
    %% NB the fake caption works *only in mkii*
    \placefloatcaption
      [figure]
      [ref:acow]
      {A smiling Dutch cow.
       {\italic Bos primigenius taurus}}
  }
}
\input dawkins
\stophangaround

\input dawkins
\stoptext

internal error: copy error etG3I7/cropped.pdf

The “paragraph in a group” problem

Another common issue with sidefloats is starting a paragraph in a group.

\placefigure[right,none]{}{\externalfigure[cow][width=0.25\textwidth]}
Lorem ipsum dolor sit amet, consectetur adipiscing elit.

{\bf Oeps} \samplefile{lorem}

As you can see the paragraph starting with “Oeps” just overflows into the picture. That is a well-known problem and there are posts about it on the mailing list every once in a while. The sidefigure mechanism uses \parshape to make the paragraph flow around the figure. The \parshape primitive only applies to a single paragraph, so ConTeXt communicates the current \parshape settings to the next paragraph using \everypar. However, when a new paragraph starts in a group, i.e.

{\bf Oeps} ...

TeX inserts the \everypar tokens inside that group because only the first letter starts the paragraph. That is to say is effectively looks like this

% \everypar inserted inside the group
% ~~~v
{\bf \the\everypar Opes} ...
% ~~~~~~~~~~~~~~~~~~~~~^
% Setting of \everypar are discarded again when leaving the group and \parshape is lost.

You have to explicitly start a new paragraph before opening a group. The easiest way to do this is

\dontleavehmode{\bf Oeps} ...

Now the \everypar tokens are inserted directly after \dontleavehmode outside the group and the problem goes away.

Syntax

Assignments

Space before commas or before the closing ] is not ignored in ConTeXt. That is a common pitfall and leads to errors that can be confusing for beginner, especially those coming from LaTeX, where this is the default behaviour. Formatting your keys like this might look nice but leads to spurious spaces:

\getparameters[test]
    [ foo=bar
    , hello=world
    ]\testfoo” “\testhello

Instead it is recommended to adhere to the style that is used in the ConTeXt source to avoid problems with trailing spaces:

\getparameters[test]
    [foo=bar,
     hello=world]\testfoo” “\testhello

In the most common form of key-value type arguments, ConTeXt will accept trailing commas or even empty items. For example, the following code shows this tolerance for the command \definehighlight.

\definehighlight [dontmiss] [
  color=red,
  ,,            %% empty option: do nothing
  style=bold,   %% trailing delimiter
]

This is the syntax accepted e.g. by the majority of setups and \getparameters. (For the parser code cf. syst-aux.mkiv.)

There are, however, exceptions to this rule. A known case where options are processed in a less tolerant fashion is \setuplabeltext. [4] Thus the following snippet will not compile, forcing ConTeXt to fail with a cryptic Argument of ... error message.

\setuplabeltext
 [Nomen=nomen,
  Est=est,
  Omen=est,] %% <= fails!
\setuplabeltext [Nomen=nomen, Est=est, Omen=omen] %% <= works
\starttext
\labeltext{Nomen}
\labeltext{Est}
\labeltext{Omen}
\stoptext

(A similar exception concerning \definepalet has been migrated to the standard argument model but may still show the earlier behavior in not so recent installations. [5])


Treacherous Dimensions

At some places dimension-lookalikes are expected but they do not behave exactly as in TEX per se because they are evaluated differently. A very common example is the command \setupbodyfont that is possibly called, albeit implicitly, in every production document. Its argument, the font size, although it may look like an ordinary dimension expression, will not have the same result if it has zeros prepended or decimal places are specified.

Suppose you want to set the main font to a very large size:

\setupbodyfont[42pt]
\starttext foobar \stoptext

this works out well as long as you do not mistake the expression 42pt to be a real dimension.

In TEX dimensions (which are really just integers) are treated the same irrespective of leading or trailing zeros used in the variable assignment. Thus the following are all equivalent:

\newdimen\fortytwo\fortytwo=42pt
\newdimen\fortytoo\fortytoo=042pt
\newdimen\fortytww\fortytww=42.00pt

\starttext
  \the\fortytwo\ \ifnum\fortytwo=\fortytoo\m{\top}\else\m{\bot}\fi\par
  \the\fortytoo\ \ifnum\fortytwo=\fortytww\m{\top}\else\m{\bot}\fi\par
  \the\fortytww\ \ifnum\fortytoo=\fortytww\m{\top}\else\m{\bot}\fi\par
\stoptext

However, this does not hold for the Context commands \setupbodyfont and \switchtobodyfont. With the latter macros, only the pure-integer specification (42pt) can be used reliably, everything else, even though it denotates the same TEX dimension, will result in a warning:

fonts           > bodyfont 42.0pt is defined (can better be done global)

Additionally, if the code in question relies on font switching a lot (e. g. Lua snippets that calculcate font sizes from floating point numbers), there will be a huge perfomance penalty because everytime a font switch occurs, the font will be reloaded entirely. The reason for this is that the process of defining a font for the main text is accompanied by a myriad of secondary definitions for different relative font sizes (\tfx, \tfa and the likes) and shapes – the creation of the body font environment. If the “size” requested by \setupbodyfont cannot be found in the internal table, this environment will be rebuilt on the spot.

It is also not possible to catch these cases by predefining the corresponding bodyfont environments by placing \definebodyfontenvironment statements in the preamble. So if you have, say:

\definebodyfontenvironment[42.0pt]
\definebodyfontenvironment[042pt]
\definebodyfontenvironment[042.0pt]
\starttext
  \setupbodyfont[42.0pt]  foo bar
  \setupbodyfont[042pt]   foo bar
  \setupbodyfont[042.0pt] foo bar
\stoptext

the warning and slowdown will occur regardless. The only real option is to serve \setupbodyfont and the likes natural integers only.

Alternatively you can always employ raw font definitions, if there isn’t any need for the environment in the first place:

\starttext
  \definedfont[file:iwona-regular.otf at 42.0pt]  foo bar
  \definedfont[file:iwona-regular.otf at 042pt]   foo bar
  \definedfont[file:iwona-regular.otf at 042.0pt] foo bar
\stoptext

Here the dimensions are real.

For further information see this and this thread on the mailing list.


Multipass

In some circumstances, portions of code are evaluated two or more times. This can be disruptive in contexts where the order of actions is important.

Trialtypesetting

Often the size of elements must be calculated prior to determining the optimal placements. Probably the most common example are tables: In order to align cells correctly the dimensions of later parts of the document must already be determined before anything is typeset. The stage during which this takes place is called *trial typesetting*.

The system mode trialtypesetting allows fine-grained control over what code should be executed during what stage. For instance, the naive definition of a macro that increments and prints a counter register will behave erratically in tables:

\def \inccount {%
  \incrementnumber [somecount]%
  \getnumber [somecount]%
}
\definenumber [somecount]
\setnumber    [somecount] [42]

\starttext

  before \inccount

  \startplacetable[location=here]
    \bTABLE
      \bTR \bTD \inccount \eTD \bTD \inccount \eTD \eTR
    \eTABLE
  \stopplacetable

  after \inccount

\stoptext

To bypass the extra evaluation, wrap the incrementation part in an \iftrialtypesetting conditional.

\def \inccount {%
  \iftrialtypesetting \else
    \incrementnumber [somecount]%
  \fi
  \getnumber [somecount]%
}

\definenumber [somecount]
\setnumber    [somecount] [42]

\starttext

  before \inccount

  \startplacetable[location=here]
    \bTABLE
      \bTR \bTD \inccount \eTD \bTD \inccount \eTD \eTR
    \eTABLE
  \stopplacetable

  after \inccount

\stoptext


Note that annoying as it may appear, the trial typesetting phase is essential for certain features to work, so take extra care when omitting it. In the example above the number itself must be present as placeholder during the first pass even though it has not been updated at this point.

\def \inccount {%
  \iftrialtypesetting \else
    \incrementnumber [somecount]%
    \getnumber [somecount]%         <= wrong!
  \fi
}


Tables

In addition to trial typesetting Context also knows a table state:

\def \tablecheck {\ifintable In table. \else Outside table. \fi}

\starttext
  \tablecheck
  \startplacetable[location=here]
    \bTABLE
      \bTR \bTD \tablecheck \eTD \bTD \tablecheck \eTD \eTR
    \eTABLE
  \stopplacetable
  \tablecheck %% decrement
\stoptext

Metapost

As with TeX, Metapost sometimes requires multiple passes, especially when processing text. In below snippet, the Metapost tracer reveals that the code is actually evaluated twice:

\enabletrackers[metapost.showlog]
\starttext
  \startMPcode
    show "This gets printed twice.";
    label (btex Some label text etex, (0,0));
  \stopMPcode
\stoptext

NB removing the label statement will result in the second pass being omitted.

In Metafun, the default Metapost format in Context, the booleans mfun_first_run and mfun_trial_run allow detecting the individual stages:

\enabletrackers[metapost.showlog]
\starttext
  \startMPcode
    if mfun_trial_run :
      show "This gets printed during the trial pass.";
    else :
      show "This gets printed during the final pass.";
    fi;
    label (btex Some label text etex, (0,0));
  \stopMPcode
\stoptext