RISC OS Build EnvironmentROBE

Appendix51Appendix Q: mkdrawf file format

Overview

The mkdrawf format is a textual source language for creating RISC OS DrawFiles. It is read by riscos-mkdrawf and can also be emitted by riscos-decdrawf when an existing DrawFile needs to be inspected or recovered into editable source form.

Unlike the binary DrawFile format, mkdrawf is intended to be written and reviewed by people. It describes DrawFile objects directly, but also adds a small expansion language with variables, macros, arithmetic, loops, conditionals, file inclusion, units, and symbolic colours. This makes it suitable for generated diagrams and repeatable artwork in normal source control.

Source language reference

Command relationship

A mkdrawf source file is compiled into a DrawFile with:

riscos-mkdrawf source.mkdrawf output,aff

The reverse operation is:

riscos-decdrawf output,aff > source.mkdrawf

The decoded form is intended to be accepted by riscos-mkdrawf. When riscos-decdrawf writes sprites or JPEG data to external files, keep the generated FromFile references and those external filenames in step before rebuilding.

The round trip is not fully lossless. riscos-decdrawf writes the original creator string only into a comment in the # Header information block, not as a usable Creator item in the decoded source itself. Rebuilding that decoded source with riscos-mkdrawf therefore replaces the original creator string with mkdrawf's own default, even though the path data, sprite words, and other object content round-trip bit for bit. Add an explicit Creator item back into the decoded source before rebuilding if the original creator string matters.

Lexical structure

The language is token based. Outside special text blocks, tokens are separated by whitespace and are case-insensitive. Braces structure nested objects, groups, macro bodies, token lists, and some control constructs. Braces are tokens in their own right and must have whitespace around them.

The main token classes are:

TokenMeaning
NumberDecimal numbers accepted by the C library number parser, or hexadecimal numbers written with a 0x prefix.
StringQuoted text, such as "Example".
ColourA colour written as r<red>g<green>b<blue>, or as three separate colour component numbers when a colour is expected.
KeywordAn object name, object item, flag, or finite-choice value, such as Path, OutlineColour, or Mitred.
Special keywordA language feature such as Define, Set, Include, IfLess, or For.
Global variableA variable name beginning with $.
Local variableA macro-local variable or parameter beginning with %.
Macro nameAny otherwise unrecognised token which has been defined as a macro.

A comment begins with # and continues to the end of the line. The comment is removed before tokenisation.

Dimensions and units

Coordinates are written as pairs of numbers. By convention, 0 0 is the bottom-left of the drawing. The default unit is the point, defined for DrawFile purposes as exactly 1/72 inch. Most DrawFile dimensions are stored as integer multiples of 1/640 point, so extreme decimal precision is not useful.

An explicit dimension is a number immediately followed by one of these suffixes:

SuffixUnit
spScaled points, each 1/640 point.
osOS units, treated by Draw as 1/180 inch.
ptPoints.
mmMillimetres.
cmCentimetres.
inInches.

The Units special keyword changes the current default unit. The predefined variables $ScaledPoints, $OSUnits, $Points, $Millimetres, $Centimetres, and $Inches provide the usual scale values. The Unit token expands to the current unit size in points.

Colours

Colours can be written as RGB tokens such as r200g100b123, or as three integer components in the range 0 to 255 when a colour is expected. The special colours None and Transparent are synonyms and mean that no colour should be used.

The language predefines the colour names Black, White, Red, Green, Blue, and Wimp0 to Wimp15. The higher Wimp colours also have descriptive names such as Wimp_DarkBlue, Wimp_Yellow, Wimp_LightGreen, Wimp_Red, Wimp_Beige, Wimp_DarkGreen, Wimp_Orange, and Wimp_LightBlue.

Expansion language

Before object descriptions are written into the DrawFile, mkdrawf expands the token stream. This expansion layer provides:

FeatureUse
SetAssign a token or token list to a local or global variable.
DefineDefine a macro which expands to a token list.
Macro parametersPass values by named local variables in braces, or by named positional parameters in brackets.
ForRepeat a token block over a numeric range or over the values in a token list.
IfExists, IfExistsHere, IfLess, IfEqualConditionally expand one block and skip another.
IncludeRead another source file at that point in the expansion stream.
ArithmeticUse operators such as Plus, Minus, Times, Over, Sin, Cos, Sqrt, Abs, and Random.
String operationsAppend strings, run strings through OS_GSTrans, and convert between strings and numbers.
Infix expressionsUse parenthesised expressions with operators such as +, -, *, /, ^, and :=.

The upstream manual notes that token lists can nest to a depth of 256 levels and that file inclusion can have up to 17 input files open at once, subject to library or operating-system limits.

Object description form

After expansion, the source is a sequence of object descriptions. Each object description starts with an object keyword, then a brace-delimited body:

Path {
FillColour None
OutlineColour Blue
Width 1
Move 100 100
Line 200 100
}

Most objects may include a BoundingBox item, although it is usually better to let mkdrawf calculate bounding boxes. Sprite and transformed sprite objects need a bounding box because mkdrawf treats sprite data as opaque. Font tables and options objects do not have bounding boxes.

Supported objects

The main object descriptions are:

ObjectPurpose
HeaderPseudo-object setting the DrawFile version, creator string, and overall bounding box.
FontTableMaps byte-sized font numbers to font names for text and transformed text objects.
TextPlaces one string at a point, with colour, background hint, font style, size, and optional centring controls.
PathDraws filled and outlined paths made from moves, lines, Bezier curves, arcs, closes, relative path elements, and optional dash or cap style.
SpriteEmbeds a sprite directly as words or imports one sprite from a sprite file with FromFile.
GroupContains a named or unnamed nested sequence of other DrawFile objects.
TaggedWraps another object with a four-byte identifier and optional word-aligned data.
TextAreaDefines text columns and formatted text using Draw text-area escape sequences, or strings supplied through XText.
OptionsStores !Draw-specific paper, grid, zoom, toolbox, mode, and undo settings.
XfTextCreates transformed text with a matrix, optional kerning, optional right-to-left rendering, and the usual text placement controls.
XfSpriteCreates a transformed sprite with a matrix and explicit bounding box.
JPEGEmbeds or references a JFIF JPEG image, with bounding box, size, DPI, matrix, and length controls.

The two forms of sprite data are written as alternatives inside the same Sprite block. Importing from a sprite file gives:

Sprite {
BoundingBox 0 0 1000 1000
FromFile "sprites,ff9" "sm!squash"
}

Embedding the sprite directly as words instead means writing the raw little-endian sprite area data verbatim, one word per line, in place of the FromFile item, exactly as riscos-decdrawf emits it by default when decoding a Sprite object:

Sprite {
BoundingBox 0 0 1000 1000
0x00000194 0x73216D73 0x73617571 0x00000068
0x00000004 0x00000008 0x00000000 0x00000007
...
}

Text areas

Text areas are the main exception to normal token processing. A Text block inside a text area is read character by character until a line containing only the closing brace. Comments, variables, and macros do not work inside that literal text block.

The XText item can be used instead when the text should come from a brace-delimited list of strings. Those strings may be produced by normal expansion, such as by Num2Str or GSTrans.

Text-area escape sequences begin with a backslash. Important escapes include \! for the text-area version marker, \A for alignment, \B and \C for background and foreground colours, \D for the number of columns, \F for font references, \L for leading, \M for margins, \P for paragraph leading, \U for underlining, and \V for vertical movement.

Minimal example

This source creates a simple outlined square with a text label:

Header {
Creator "mkdrawf"
}
FontTable {
1 "Homerton.Medium"
}
Path {
FillColour Wimp_LightBlue
OutlineColour Black
Width 1
Move 50 50
Line 250 50
Line 250 180
Line 50 180
Close
}
Text {
Colour Black
Background Wimp_LightBlue
Style 1
Size 14 14
CentreAt 150 115
Text "Generated by mkdrawf"
}

Limits and diagnostics

mkdrawf reports warnings, errors, and fatal errors. It tries to continue after ordinary errors so that a single run can report more than one problem, but output produced after an error should not be trusted. After 100 errors, the tool stops.

The manual also notes implementation limits such as the token-list nesting limit and the open include-file limit. Treat these as practical parser limits rather than features to rely on.

Worked example

For generated diagrams, keep the mkdrawf source next to the code or resources that describe what is being drawn. Rebuild the DrawFile as part of the normal resource build:

riscos-mkdrawf doc/diagram.mkdrawf Resources/Diagram,aff
riscos-decdrawf Resources/Diagram,aff > /tmp/diagram.mkdrawf