Skip to content

Commit

Permalink
docs(manual): Add section on measurements and lengths
Browse files Browse the repository at this point in the history
Add "Measurements and lengths" section.
Refactor "Boxes, glue, and penalties" section.
Move "Kerns" (sub)section closer to the above.
Split "The typesetter" section apart.
In all the above, add a bit more of autodoc's magic here and there.
  • Loading branch information
Omikhleia authored and Didier Willis committed Apr 9, 2023
1 parent 5bc372a commit 08f59bc
Showing 1 changed file with 133 additions and 108 deletions.
241 changes: 133 additions & 108 deletions documentation/c09-concepts.sil
Expand Up @@ -2,71 +2,152 @@
\include[src=documentation/macros.sil]
\chapter{The Nitty Gritty}

\noindent{}We are finally at the bottom of our delve into SILE’s commands
and settings. Here are the basic building blocks out of which all of the
other operations in SILE are created.
We are finally at the bottom of our delve into SILE’s commands and settings.
Here are the basic building blocks out of which all of the other operations in SILE are created.

\note{At this point, it is expected that
you are a class designer, and will be able to follow the details of how
SILE implements these commands and features; we will also explain how to
interact with these components at the Lua level.}
\note{At this point, it is expected that you are a class or package designer, and will be able to follow the details of how SILE implements these commands and features;
we will also explain how to interact with these components at the Lua level.}

\section{Measurements and lengths}

Before dabbling into more advanced topics, let’s introduce “measurements” and “lengths” in SILE,
the two available Lua constructs for representing dimensions.

Measurements are specified in terms of \code{SILE.measurement} objects.
It is a basic construct with an amount and a unit. Let us illustrate two common ways for creating such an object in Lua (from a string, with same syntax as in command parameters; or from a Lua table).

\begin[type=autodoc:codeblock]{raw}
local m1 = SILE.measurement("10pt")
local m2 = SILE.measurement({ amount = 10, unit = "pt" })
\end{raw}

SILE also provides a more advanced construct specified in terms of \code{SILE.length} objects;
these are “three-dimensional” dimensions, in that they consist in a base measurement plus stretch and shrink measurements.
They are therefore composed of three \code{SILE.measurement}.

\begin[type=autodoc:codeblock]{raw}
local l1 = SILE.length("10pt plus 2pt minus 1pt")
local l2 = SILE.length({ length = "1Opt", stretch = "2pt", shrink = "1pt" })
\end{raw}

Both of these are used for various purposes.
In many cases, they are nearly interchangeable.
Casting from one to the other is straightforward:
casting a length to a measurement returns just the base measurement and discards the stretch and shrink properties;
casting a measurement to a length sets its stretch and shrink properties to zero.

\begin[type=autodoc:codeblock]{raw}
local l3 = SILE.length(SILE.measurement("10pt")) -- 10pt, without strech and shrink
local m3 = SILE.measurement(SILE.length("10pt plus 2pt minus 1pt")) -- 10pt
\end{raw}

Proper casting is important, for your code to remain portable across the various versions of the Lua language.

\section{Boxes, glue, and penalties}

SILE’s job, looking at it in very abstract terms, is all about arranging
little boxes on a page. Some of those boxes have letters in them, and
those letters are such-and-such a number of points wide and
such-and-such a number of points high;
SILE’s job, looking at it in very abstract terms, is all about arranging little boxes on a page.
Some of those boxes have letters in them, and those letters are such-and-such a number of points wide and such-and-such a number of points high;
some of the boxes are empty but are there just to take up space.
When a horizontal row of boxes has been decided (i.e., when a line break is determined)
then the whole row of boxes is put into another box and the vertical list
of boxes are then arranged to form a page.
When a horizontal row of boxes has been decided (i.e., when a line break is determined) then the whole row of boxes is put into another box and the vertical list of boxes are then arranged to form a page.

Conceptually, then, SILE knows about a few different basic components:
horizontal boxes (such as a letter); horizontal glue (the stretchable or shrinkable
space between words); vertical boxes (a line of text); vertical glue (the space
between lines and paragraphs); and penalties (information about where and when
not to break lines and pages).\footnote{Additionally there are three more types of box
that SILE cares about: N-nodes, unshaped nodes, and discretionaries.}

\begin{itemize}
\item{Horizontal boxes (such as a letter)}
\item{Horizontal glue (such as the stretchable or shrinkable space between words)}
\item{Vertical boxes (typically, a line of text)}
\item{Vertical glue (such as the space between lines and paragraphs)}
\item{Penalties (information about where and when not to break lines and pages)}
\end{itemize}

Additionally, horizontal boxes are further specialized.\footnote{The math support in SILE also defines additional types of boxes, not discussed here.}

\begin{itemize}
\item{Discretionaries (special construct used when a word is hyphenated)}
\item{N-nodes and unshaped nodes (text content shaped according to a certain font, or not yet shaped and measured)}
\item{Migrating boxes (such as foonote content)}
\end{itemize}

The most immediately useful of these are horizontal and vertical glue.
Horizontal and vertical glue can be explicitly added into SILE‘s processing
stream using the \code{\\glue} and \code{\\skip} commands. These take a
\code{width} and a \code{height} parameter, respectively, both of which are
glue dimensions. For instance, the \code{\\smallskip} command is
the equivalent of \code{\\skip[height=3pt plus 1pt minus 1pt]}; \code{\\thinspace}
is defined as being \code{\\glue[width=0.16667em]}.

Similarly, there is a \code{\\penalty} command for inserting penalty nodes;
\code{\\break} is defined as \code{\\penalty[penalty=-10000]} and \code{\\nobreak}
is \code{\\penalty[penalty=10000]}.

You can also create horizontal and vertical boxes from within SILE. One
reason for doing so would be to explicitly avoid material being broken up by a
page or line break; another reason for doing so would be that once you box some
material up, you then know how wide or tall it is. The \code{\\hbox} and \code{\\vbox}
commands put their contents into a box; when called from Lua, they also \em{return}
the new box.

\subsection{From Lua}

SILE’s Lua interface contains a \code{nodefactory} for creating boxes and glue.
Before we get into that, however, you need to know that glue measurements in SILE
should always be specified in terms of \code{SILE.length} objects; these are
“three-dimensional” lengths, in that they consist of a base length plus stretch and
shrink. To construct a \code{SILE.length}:
Horizontal and vertical glue can be explicitly added into SILE‘s processing stream using the \autodoc:command{\glue} and \autodoc:command{\skip} commands.
These take a \autodoc:parameter{width} and a \autodoc:parameter{height} parameter, respectively, both of which are glue dimensions.
For instance, the \autodoc:command{\smallskip} command is the equivalent of \autodoc:command{\skip[height=3pt plus 1pt minus 1pt]};
\autodoc:command{\thinspace} is defined as being \autodoc:command{\glue[width=0.16667em]}.

\begin[type=autodoc:codeblock]{raw}
local l = SILE.length({ length = x, stretch = y, shrink = z })
\end{raw}
Similarly, there is a \autodoc:command{\penalty} command for inserting penalty nodes;
\autodoc:command{\break} is defined as \autodoc:command{\penalty[penalty=-10000]}
and \autodoc:command{\nobreak} is \autodoc:command{\penalty[penalty=10000]}.

You can also create horizontal and vertical boxes from within SILE.
One reason for doing so would be to explicitly avoid material being broken up by a page or line break;
another reason for doing so would be that once you box some material up, you then know how wide or tall it is.
The \autodoc:command{\hbox} and \autodoc:command{\vbox} commands put their contents into a box.

Now we can construct horizontal and vertical glue:
At a Lua coding level, SILE’s Lua interface contains a \code{nodefactory} for creating boxes and glue.
Here is one way (among others) for you to construct horizontal and vertical glue:

\begin[type=autodoc:codeblock]{raw}
local glue = SILE.nodefactory.glue({ width = l })
local glue = SILE.nodefactory.glue({ width = l })
local vglue = SILE.nodefactory.vglue({ height = l })
\end{raw}

\section{Kerns}

\define[command=SILEkern]{\font[family=Gentium Plus]{% Book Basic has no +smcp, but readers don't need to know, since we're only using Book Basic as a holdover from old SILE which did.
S\lower[height=0.5ex]{I}L\kern[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}}
\define[command=SILEglue]{\font[family=Gentium Plus]{%
S\lower[height=0.5ex]{I}L\glue[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}}
\autodoc:command{\kern}’s are a type of \autodoc:command{\glue}, only different in that
while a \autodoc:command{\glue} can be broken at the end of a line, a \autodoc:command{\kern}
can’t. Hearkening back to our \SILEkern example from the \em{Macros and
Commands} chapter, consider that example, repeated enough times to cause a
linebreak, but with \autodoc:command{\glue}’s everywhere \autodoc:command{\kern}’s are used
instead:

\line
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\color[color=#dd0000]{\SILEglue}\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\line

Note the \SILEglue in \color[color=#dd0000]{red} is broken
between its ‘L’ and ‘\raise[height=0.6ex]{\font[family=Gentium Plus,features=+smcp]{e}}’.
Instead, if we typeset the same line using \code{\\kern}’s as we had
originally:

\line
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\color[color=#dd0000]{\SILEkern}\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\line

The line just continues on right off the page. Why this is a useful feature is
more obvious if there are spaces between them:

\line
Glues:

\SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \color[color=#dd0000]{\SILEglue} \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue
\line

\line
Kerns:

\SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \color[color=#dd0000]{\SILEkern} \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern
\line

\section{The typesetter}

SILE’s typesetting is organised by the \code{SILE.typesetter} object. It
maintains two queues of material that it is still working on: the node queue and
the output queue. Material in these queues is content that has been parsed but
Expand All @@ -90,8 +171,8 @@ into lines and added to the vertical output queue by calling the function
\code{SILE.typesetter:leaveHmode()}. This is handy when for writing custom
functions, but it is a fairly low level control. (It is unlikely to be
useful while writing a document.) A related but higher level command,
\code{\\par}, is more frequently used when writing a document and embedded in
the content. The \code{\\par} command first calls
\autodoc:command{\par}, is more frequently used when writing a document and embedded in
the content. The \autodoc:command{\par} command first calls
\code{SILE.typesetter:leaveHmode()}, then inserts a vertical skip according to
the \autodoc:setting{document.parskip} setting, then goes on to reset a number of
settings that are typically paragraph-related such as hanging indents.
Expand All @@ -101,7 +182,7 @@ the output, first ensure that the material in the current paragraph has been all
properly boxed-up and moved onto the output queue by calling
\code{SILE.typesetter:leaveHmode()}, then add your desired glue to the output
queue.
This is exactly what the \code{\\skip} and similar commands do.
This is exactly what the \autodoc:command{\skip} and similar commands do.

Adding boxes and glue to the typesetter’s queues is
such a common operation that the typesetter has some utility methods to construct
Expand Down Expand Up @@ -138,62 +219,6 @@ Adding horizontal and vertical penalties to the typesetter’s queues is similar
done with the \code{SILE.typesetter:pushPenalty(\{penalty = x\})} and
\code{SILE.typesetter:pushVpenalty(\{penalty = y\})} methods.

\subsection{Kerns}

\define[command=SILEkern]{\font[family=Gentium Plus]{% Book Basic has no +smcp, but readers don't need to know, since we're only using Book Basic as a holdover from old SILE which did.
S\lower[height=0.5ex]{I}L\kern[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}}
\define[command=SILEglue]{\font[family=Gentium Plus]{%
S\lower[height=0.5ex]{I}L\glue[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}}
\code{\\kern}’s are a type of \code{\\glue}, only different in that
while a \code{\\glue} can be broken at the end of a line, a \code{\\kern}
can’t. Hearkening back to our \SILEkern example from the \em{Macros and
Commands} chapter, consider that example, repeated enough times to cause a
linebreak, but with \code{\\glue}’s everywhere \code{\\kern}’s are used
instead:

\line
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\color[color=#dd0000]{\SILEglue}\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue%
\line

Note the \SILEglue in \color[color=#dd0000]{red} is broken
between its ‘L’ and ‘\raise[height=0.6ex]{\font[family=Gentium Plus,features=+smcp]{e}}’.
Instead, if we typeset the same line using \code{\\kern}’s as we had
originally:

\line
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\color[color=#dd0000]{\SILEkern}\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern%
\line

The line just continues on right off the page. Why this is a useful feature is
more obvious if there are spaces between them:

\line
Glues:

\SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \color[color=#dd0000]{\SILEglue} \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue \SILEglue
\line

\line
Kerns:

\SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \color[color=#dd0000]{\SILEkern} \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern \SILEkern
\line


\section{Frames}

As we have previously mentioned, SILE arranges text into frames on the page.
Expand Down

0 comments on commit 08f59bc

Please sign in to comment.