Print Layout Templates
Status: Implemented
Author: Toluwaleke Ogundipe
Reviewers: Federico Mena Quintero, Jonathan Blandford
Goals
Specify a format for defining reusable print layout templates.
Rationale
Different puzzle kinds require slightly different layouts when printed on paper. All of them require the puzzle grid and the clues, but for example, in arrowword puzzles, the clues are embedded in the grid and they do not require a list of clues.
Overall Approach
Print layout templates are reusable, declarative specifications that define how different elements of a puzzle are arranged and rendered on a printed page. At its core, a template describes a layout for a specific page size and puzzle kind(s), breaking down the printable area into a hierarchy of elements, each with a distinct role, such as displaying the grid, clues, title, metadata, or other visual components.
These templates provide a consistent and flexible way to support various puzzle kinds and page sizes.
The Format
At the top-level, a TEMPLATE contains some metadata, the main page, and an optional overflow page with space for additional clues. Each page is typically a nested set of ELEMENTS.
Templates are defined in the JSON format as follows.
Important
The code blocks herein are not valid JSON but descriptions of the expected format.
Field/Member names are case-sensitive.
Unknown/Unexpected fields are ignored.
{
// The page size of the template, in millemeters.
"page_size": { "width": Float, "height": Float },
// Applicable puzzle kinds (case-insensitive).
"puzzle_kinds": [ String, ... ],
// The main page.
"main_page": Box,
// Optional: An overflow page for additional clues.
"overflow": Box
}
Where Box is a container of elements and has the following format:
{
// The orientation of the box (case-insensitive); one of:
//
// - horizontal
// - vertical
"orientation": String
// Sub-elements of the box.
"elements": [
{
// Relative ratio of the sub-element within the box;
// Positive.
"ratio": Float,
// A sub-element.
"element": Element
}, ...
]
}
ratiois optional and ignored for divider sub-elements.sub-element ratios are relative i.e the effective/absolute ratio of each sub-element is
(sub-element ratio) / (sum of all sub-element ratios); this is more flexible than absolute ratios, without significant additional cost.Elementdefines a template element.must contain at least one non-divider sub-element.
Template Elements
There are two basic categories of elements:
DISPLAY elements: These might contain text, a grid, or something else, or even nothing at all (to insert white space between other elements).
CONTAINER elements: These are used to arrange other elements.
Each element is identified by a KIND and may include associated data to customize its behavior, e.g font size/styles for text, or clue flow information for multi-region clue layouts.
An element has the following format:
{
// The element kind (case-insensitive).
"kind": String,
// Optional: Used for element kinds that require extra info.
"data": ...
}
The element kinds are as follows. Each doesn’t use the data field except
stated otherwise.
TITLE: The puzzle’s title.
The
datafield has the following format:{ // The font description with which to render text "font": String }
fontis in the format accepted bypango_font_description_from_string(), with the following specifics:only the font size is required, and must be in points.
if the font family is not specified, a default is used.
METADATA: The publisher, author, date, etc (as many as are defined).
The
datafield is as for the TITLE element kind.
INTRO: The puzzle’s intro.
The
datafield is as for the TITLE element kind.
NOTES: The puzzle’s notes.
The
datafield is as for the TITLE element kind.
GRID: The puzzle’s grid.
CLUES: The puzzle’s clues.
The
datafield has the following format:{ // A unique (amongst CLUES elements) identifier; // Positive. "id": Integer, // Clue direction(s) to contain (case-insensitive). One of: // // - all: Contain all clue directions, one after another // - any: Any available clue direction // - <any of the standard IPUZ directions> "direction": String, // The ID of another CLUES element into which the content of this // element may flow. "flows_into": Integer, // The font description with which to render text "font": String }
idis required.a source is a CLUES element into which no other flows.
an extension is a CLUES element into which another flows.
all data fields other than
idare optional, with the following exception(s):directionandfontare required for a source.
for
direction:if specified,
directionis ignored for an extension.if direction
allis used, there must be only one source.if direction
anyis used, thedirectionof all sources must beany.no two sources may have the same IPUZ direction.
for
flows_into:if specified, there must exist another CLUES element with that value as its
id.a CLUES element must not flow into itself.
multiple CLUES elements must not flow into the same other CLUES element.
clues may flow across pages, but only forward i.e a CLUES element must not flow into another on a page before it.
a cyclic flow (i.e a loop of CLUES elements that have no apparent source nor end) is invalid.
fontis as for the TITLE element kind.if not specified for an extension, it inherits that of its source.
SOLUTION: The puzzle’s solution in some form.
DIVIDER: A vertical/horizontal divider.
Has a pre-defined thickness (later defined by the print layout config).
NOTE: This is a standalone element, not a container.
SPACER: A blank space.
BOX: A container to organize elements.
The
datafield has the format of aBoxas earlier defined for the page top-level:
Element kinds may be added, merged or removed over time as deemed fit.
Next steps
[ ] Define templates for common page sizes and puzzle kinds.
Areas For Improvement
[ ] Allow user-defined templates. This will require the following (amongst other things):
[ ] a version field in the format to prevent breakage when there are breaking changes to the format.
[ ] report errors during template loading, instead of treating them as programming errors.
[ ] A GUI to allow users to interactively create, modify and save layouts. This will require the following (amongst other things):
[ ] recursively serializing element structures to JSON (could use
JsonBuilder+JsonGenerator).