Formatting and Styling Basics
Nytril lets you put text and data into a document quickly and easily. Once you have done that, you'll want to add
some formatting and style. Please take a few minutes to read the sections below to learn the basics of revisions and
formatting to prevent common misunderstandings.
Formatters are the way to turn data into visible content. Use them to place your data into a paragraph, table,
document or even a popup tip. Formatters retain a link to the data so you and your users can easily find the source
for any part of a document with a simple control-click.
Revisions
A revision is a set of expressions (each one ending with a semicolon) that are enclosed in curly braces {...}. Each
expression in the revision can either be an
element
or a
property
.
•
An element is a number, string or some other value like a date. It can be a variable holding a value or
function call that returns a value.
•
A property is a
field: value
pair that gives a formatter styling information. It can also be used to specify
content for some formatters.
Elements are added to a revision in order of appearance. If an element is an array, then the array itself is not added
to the revision. Instead, all the values in the array are added to the revision in order, without changing the array.
Properties, on the other hand, have no order and can be specified at any place in the revision. Regardless of where
they are placed, they effect the entire revision. For instance the
TextWeight
:Bold property makes all the text bold,
regardless of whether it is placed at the beginning, middle or end of the revision.
The entire revision {...} is treated as a single object that can be used to 'operate' on a formatter. When a formatter is
'revised' in this way, a separate object is created that represents the revised formatter.
Neither the formatter nor the
revision are changed by this operation.
This means that revisions can be built from other revisions without causing
side effects. A revised formatter can, in turn, be revised further to achieve the desired style structure. This is same
idea as cascading style sheets in web programming and styles in word processors.
In the example below, the Paragraph is revised to contain one property and two elements.
ExamplePar
=
Paragraph
{
TextHeight
:
8pt;
"First element. "
;
"Second element. "
;
};
These revisions can be combined with other revisions. The next example shows another revision being applied to the
previous one, to give it more content and change its color.
RevisedPar
=
ExamplePar
{
TextColor
:
Colors
.
Red
;
"Third element. "
;
};
If a new revision has a property that is the same as a property from a previous revision (in this case TextColor), then
the latter overrides the former. In the next example, the red paragraph from the previous example is revised with a
blue color. Again, this does not change the previous paragraph, but instead makes a new object with the revised
properties. This allows you to reuse existing styles, while making surgical changes as needed.
BluePar
=
RevisedPar
{
TextColor
:
Colors
.
Blue
;
};
Computed Content
If you are used to working with markup languages, it is easy to forget that the content in Nytril is not static. In Nytril,
no content is actually added until the statements creating the content are executed at runtime. This execution occurs
when you refresh the document (by pressing F5). As you look at the examples, keep in mind that each of them is a
function that executes line by line, just like a normal computer program. If you want to see this in action, simply set a
breakpoint on a line by clicking just to the left of the line numbers in your code. When you refresh the document, the
program will stop at the breakpoint, and you will be able to step through the logic of your code. The code below
shows a more complex set of functions that use an
if
statement to make a decision about what content to include
depending on the value of a number. This topic is covered more detain in the
Tutorial
.
namespace
TestOutput
{
TestNumber
(
number
)
=
Span
{
"The number is "
;
number
;
if
(
number
>
9)
", which is quite large. "
;
else
". "
;
};
UseContent
=
Block
{
// Call the function declared above
TestNumber
(5);
TestNumber
(15);
};
}
Content Flow
In general there are two ways that content can flow in a document.
•
Vertical or 'block'
In vertical flow, the children are allowed to take up the entire horizontal space between margins, and flow
downward to the bottom of the page. If the bottom of the page is reached, a new page is created, and the
content will continue to flow.
Paragraph
Table
î—›
•
Horizontal or 'span'
In horizontal flow, the child elements become runs of rectangular 'characters' like text, icons, frames,
canvases and images. These characters are aligned horizontally, end-to-end on a line, and vertically on the
baseline of the text. When the line is too long, a new line is created. When the paragraph runs out of room
on a page, a new page is created and the process continues.
Content fills horizontally
î—ˆ
until the end of the line is
reached and a
new line
is
created.
The Basic Formatters
Now that you understand a little about the two kinds of flow, let's take a look at a few of the workhorses. In most
documents, almost all of the formatting work will be handled by the formatters below. There is a comprehensive list
of formatters at the end.
Span
A Span is a collection of elements with its own text-formatting properties. A Span never exists in a document by itself
and will always be placed inside a Paragraph either explicitly or implicitly. If you want to changed the formatting of
some text in a paragraph without changeing the text around it, put it in a Span.
ExampleSpan
=
Span
{
"Here is a span of text with "
;
Span
{
TextColor
:
Colors
.
Red
;
"a red"
;
};
"span inside. "
;
};
Paragraph
A Paragraph can be thought of as a block that has flowing Spans within. It also has extra properties such as tab stops,
indenting, justification and borders. A Paragraph will take up the available horizontal space inside the margins of
whatever space it happens to be inside, and will flow left-to-right and downward. There are no 'levels' in a Paragraph.
If there are inner Spans, they simply become a differently formatted sections of text inside the Paragraph.
ComplexPar
=
Paragraph
{
ParAlignment
:
Justify
;
LeftIndent
:
8pt;
RevisedPar
;
"Plain text"
;
Span
{
"Inline span with custom formatting"
;
TextHeight
:
120%;
Underline
;
};
};
Block
A Block is a collection of Paragraphs and Tables (and other Blocks) that flow downward in a document. A Block is a
convenient way of gathering vertically oriented content together and assigning common properties to the elements.
If a child element of a block is not already a Paragraph or Table, it will be 'promoted' the be a Paragraph and it will
appear on a new line. If a Block contains another Block, it simply means that the outer Block will add the content of
the inner Block to itself. In other words, no nesting 'levels' are created inside a block. In this way, pre-formed
Paragraphs and Blocks can be stitched together and reused in a different contexts.
The nature of the outer formatter determines how its inner elements are interpreted. Keep in mind that each
formatter can only allow certain formatters to be its children. For example, if we try to add a complex formatter like a
Table to a Paragraph, there would be no clear way for the table to "flow" with the other text in the paragraph
horizontally. A table needs to take up the entire horizontal extent of its parent, so a table must be placed in a block.
ExampleBlock
=
Block
{
TextHeight
:
12pt;
// All the text, in all the elements, will be 12pt
ComplexPar
;
Paragraph
{
"Inline paragraph"
;
TextHeight
:
10pt;
// Override the TextHeight setting above
};
Span
{
"Converts to a separate paragraph"
};
"This string becomes a paragraph"
;
5
+
3;
// Computes the value 8 and becomes a paragraph;
};
Document
A Document is a Block with many additional properties such as paper size, headers, footers, gutter, etc. The example
below shows a Document with custom margins. Notice that the Header and Footer are not Elements of the
Document, but are expressed as Properties. Just as in a Block, the content of the document itself is expressed as
separate Elements of the Revision that operates on the document.
ExampleDoc
=
Document
(
new
(0.5", 1", 0.5", 0.75")) {
Header
:
MarginBlock
{
"Title"
;
};
Footer
:
MarginBlock
{
Paragraph
{
ParAlignment
:
Center
;
"Page "
;
DocFields
.
PageNumber
;
};
};
"Here is the content"
;
ExampleBlock
;
};
Formatters by category
Below is a comprehensive list of the formatters and the container rules. It is critical to understand that each
formatter has a very specific set of characteristics the serve a particular formatting need. The important thing is that
these formatters can be mixed and matched to generate documents of great complexity.
Common
Span
A collection of text runs inside a paragraph. Children may include: [
Span
,
object
].
DropCap
A span with an enlarged dropped letter. Children may include: [
Span
,
object
].
ListSpan
A Span containing enumerated items. Children may include: [
Span
,
object
].
Paragraph
A paragraph contains zero or more spans that wrap inside the left and right margins. Children may include:
[
Span
,
object
].
Block
A container that holds Paragraphs and Tables. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
]. It will convert its child elements to
Paragraph
if they are not already of that
type.
ListBlock
A Block containing enumerated items with a marker to the left. Children may include: [
Paragraph
,
Span
,
object
]. It will convert its child elements to
Paragraph
if they are not already of that type.
ListItem
An item inside a ListBlock. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
]. It will convert its child elements to
Paragraph
if they are not already of that type.
TextBlock
A Block with fixed-width formatting. Children may include: [
Paragraph
,
Span
,
object
]. It will convert its
child elements to
Paragraph
if they are not already of that type.
Frame
A rectangular Block containing Paragraphs and Tables that fits in a Span. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
]. It will convert its child elements to
Paragraph
if they are not
already of that type.
Tables
Table
A container for Rows and Columns. Children may include: [
Row
]. It will convert its child elements to
Row
if they
are not already of that type.
Row
A horizontal container of Cells. Children may include: [
Cell
]. It will convert its child elements to
Cell
if they are
not already of that type.
Cell
A cell inside a Row. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
]. It
will convert its child elements to
Paragraph
if they are not already of that type.
Math
Fraction
A canvas that draws a horizontal/diagonal line between its children. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
]. It will convert its child elements to
Paragraph
if they are not
already of that type.
Nary
Draws an operator with optional superscript, subscript and body elements. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
]. It will convert its child elements to
Paragraph
if they
are not already of that type.
Radical
Draws a square-root shape around is elements. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
].
AlignBlock
A Block containing alignment markers. Children may include: [
Paragraph
]. It will convert its child elements to
Paragraph
if they are not already of that type.
MatrixBox
A canvas that contains Rows and vertically aligns the columns. Children may include: [
Row
]. It will convert its child
elements to
Row
if they are not already of that type.
Chart
Chart
A chart containing one or more ChartSeries. Children may include: [
ChartSeries
].
ChartSeries
A data series that displays in a Chart. Children may include: [
DataPoint
,
double
].
ChartAxis
Defines a Chart axis. Children may include: [
double
].
ChartLegend
Defines a chart legend. Children may include: [
Paragraph
].
Drawing
TreeBox
Draws a tree structure with its Root elements.
QRBox
A QR code.
FitBox
Rezize and rescale an image to fit into a specific rectangle.
Canvas
A rectangular container that holds elements with arbitrary position. Children may include: [
Block
].
HBox
A canvas with a Direction of Horizontal. Children may include: [
Block
].
VBox
A canvas with a Direction of Vertical. Children may include: [
Block
].
Shape
Shape
A container for ShapePaths. Children may include: [
object
,
ClosedPath
,
OpenPath
].
ClosedPath
A closed path. Children may include: [
LineTo
,
QuadTo
,
CurveTo
,
ArcTo
].
OpenPath
An open shape containing elements (e.g. LineTo, QuadTo, CurveTo and ArcTo). Children may include: [
LineTo
,
QuadTo
,
CurveTo
,
ArcTo
].
LineTo
A line segment.
QuadTo
A quadratic curve segment.
CurveTo
A bezier segment.
ArcTo
An elliptic arc segment.
Document
Document
A complete document that contains sections. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
,
MarginBlock
,
SectionContinuous
,
SectionNextPage
,
SectionOddPage
,
SectionEvenPage
]. It will convert its child elements to
Paragraph
if they are not already
of that type.
SectionNextPage
Creates a new section with a page break. Children may include: [
MarginBlock
].
SectionOddPage
Creates a new section starting on the next odd page. Children may include: [
MarginBlock
].
SectionEvenPage
Creates a new section starting on the next even page. Children may include: [
MarginBlock
].
SectionContinuous
Creates a new section on the same page.
Cite
A citation span that contains Reference facts. Children may include: [
DocReference
]. It will convert its child
elements to
DocReference
if they are not already of that type.
Figure
Add a figure or table to the document. Children may include: [
Block
,
Table
,
ListBlock
,
Paragraph
,
Span
,
object
].
DocumentIndex
An index block for the document.
DocumentEndnotes
An endnote listing Block.