diff --git a/documentation/c09-concepts.sil b/documentation/c09-concepts.sil index 5aeaf7d93..d32e4b8fe 100644 --- a/documentation/c09-concepts.sil +++ b/documentation/c09-concepts.sil @@ -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 @@ -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. @@ -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 @@ -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.