Skip to main content
Recipe number 170

Cookbook · Chapter 12 · Textbooks

Column arithmetic as each country writes it

A maths worksheet whose sums are - 864 ÷ 4 lines in the Markdown, set digit by digit in squares, long division as each country's schools lay it out.

On this page
Postext
Tested with Postext 1.59.0
Needs ≥ 1.59.0 · postext-pdf ≥ 1.59.0
Licence
Updated 24 Oct 2026
Code MIT · Text CC BY 4.0
  • Trim 210 × 297 mm
  • 1 column
  • Fira Sans 12/18
  • Lexend
  • 4 pages
  • Level
  • Postext 1.59.0
  • Laid out in 55 ms
  • 118 lines of code

In short

A page of sums for nine-year-olds in Spanish, English, French and German. Each country writes long division its own way, and the sheet follows it; a switch shows the teacher's copy with every answer filled in.

What you'll build

Unit 3 of a Year 4 calculation book, The four operations, in four editions. A row of mental sums in three columns, then additions, subtractions and multiplications written one digit to a square with a row of squares for the answer, then long division and a word problem on squared paper. The division is where the editions part: the English sheet sets the divisor outside the ⟌ bracket with the quotient on top; the Spanish and French ones put the divisor in an L to the right of the dividend and the quotient under it; the German one writes 864 : 4 = on one line with room for the working below, and its products with a dot. The decimal comma or point of the mental sums follows the language too. A switch above the pages turns the pupil's sheet into the teacher's copy, every answer printed in orange in its square.

This recipe answers

  • How do I set long division the way each country's schools write it?
  • How do I make a teacher's edition with the answers printed in their places from the pupil's book?

The short answer

script.js · lines 26–42in full code
// `- 864 : 4` in the Markdown is one operation; the style sets it digit by digit in squares
// of 7.2 mm, a cell for each figure of the answer. Long division follows the country: the
// divisor in an L right of the dividend in Spain and France (potence), `864 : 4 =` on one
// line in Germany, the bracket ⟌ in English-speaking schools.
const calculo = {
  id: 'calculo',
  number: { shape: 'disc', background: col('accent'), fontFamily: LABEL, fontWeight: 700 },
  instruction: { bold: true, color: col('ink') },
  grid: { cell: mm(9), fontFamily: TEXT, fontSize: pt(15), rule: col('rule'),
    color: col('ink') },
  arithmetic: { columns: 4,
    division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
  blank: { look: 'box', width: em(3.2), color: col('rule'), weight: pt(0.8) },
  area: { kind: 'grid', spacing: mm(5), color: col('rule'), weight: pt(0.4) },
  answers: { color: col('accent'), fontFamily: LABEL }, // the teacher's copy, in the accent
  marginTop: mm(7), marginBottom: mm(2), itemSpacing: em(0.5),
};

Operations in squares, long division as the edition's schools set it

Ingredients

Type
Fira Sans, Lexend (SIL OFL 1.1)
Assets
None: every picture is drawn in code

Method

#1 · Write the sums as lines of the Markdown

script.js · lines 26–42in full code
// `- 864 : 4` in the Markdown is one operation; the style sets it digit by digit in squares
// of 7.2 mm, a cell for each figure of the answer. Long division follows the country: the
// divisor in an L right of the dividend in Spain and France (potence), `864 : 4 =` on one
// line in Germany, the bracket ⟌ in English-speaking schools.
const calculo = {
  id: 'calculo',
  number: { shape: 'disc', background: col('accent'), fontFamily: LABEL, fontWeight: 700 },
  instruction: { bold: true, color: col('ink') },
  grid: { cell: mm(9), fontFamily: TEXT, fontSize: pt(15), rule: col('rule'),
    color: col('ink') },
  arithmetic: { columns: 4,
    division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
  blank: { look: 'box', width: em(3.2), color: col('rule'), weight: pt(0.8) },
  area: { kind: 'grid', spacing: mm(5), color: col('rule'), weight: pt(0.4) },
  answers: { color: col('accent'), fontFamily: LABEL }, // the teacher's copy, in the accent
  marginTop: mm(7), marginBottom: mm(2), itemSpacing: em(0.5),
};

An :::exercise{type="arithmetic"} takes one operation per list item, - 864 ÷ 4, in the signs its country writes (÷, :, ×, ·). The engine works out the answer, the quotient and the remainder, and the style decides how the sum is drawn: squares of 7.2 mm, and arithmetic.division picked per edition with t(). A mental-arithmetic row is an ordinary gaps exercise whose list sits in a :::columns{count=3} group, each [[70]] a box as wide as the others.

#2 · Head the sheet

script.js · lines 46–67in full code
const at = (to, edge, x = 0, y = 0, size) => ({ anchor: { to, edge },
  offset: { x: mm(x), y: mm(y) }, ...(size && { size }) });
const field = (id, attr, x, width) => [
  { kind: 'text', id, content: `{attr.${attr}}`, fontFamily: LABEL, fontSize: pt(9.5),
    fontWeight: 600, color: col('muted'), align: 'left',
    placement: at('container', 'top-left', x, 26) },
  { kind: 'rule', id: `${id}-line`, direction: 'horizontal', thickness: pt(0.75),
    color: col('rule'), placement: at('container', 'top-left', x + 17, 31, { width: mm(width) }) },
];
const head = { enabled: true, minHeight: mm(36), slot: { elements: [
  { kind: 'box', id: 'band', style: { backgroundColor: col('accent'), borderRadius: mm(3) },
    placement: at('container', 'top-left', 0, 0, { width: 'fill', height: mm(19) }) },
  { kind: 'text', id: 'unit', content: '{attr.unidad}', fontFamily: LABEL, fontSize: pt(9),
    fontWeight: 700, color: col('accent'), align: 'center', verticalAlign: 'middle',
    textTransform: 'uppercase', letterSpacing: pt(1.2), lineHeight: 1.1,
    box: { backgroundColor: col('paper'), borderRadius: mm(3.5) },
    placement: at('container', 'top-left', 5, 5, { width: mm(26), height: mm(9) }) },
  { kind: 'text', id: 'title', content: '{titleText}', fontFamily: LABEL, fontSize: pt(21),
    fontWeight: 700, color: col('paper'), align: 'left', lineHeight: 1.1,
    placement: at('container', 'top-left', 37, 5.2) },
  ...field('name', 'nombre', 0, 100), ...field('date', 'fecha', 124, 38),
] } };

The unit's level-1 heading draws the orange band, the pill and the title, and the name and date fields from its attributes (nombre="Name" fecha="Date"), so every edition labels them in its own language.

#3 · Switch between the pupil's sheet and the teacher's copy

script.js · lines 180–194in full code
const form = document.getElementById('controls');
const labels = t({ es: ['Alumno', 'Profesor'], en: ['Pupil', 'Teacher'],
  fr: ['Élève', 'Enseignant'], de: ['Schüler', 'Lehrkraft'] });
form.querySelectorAll('label span').forEach((span, k) => { span.textContent = labels[k]; });
function render() {
  // The same config, edited in place: the next build sees the edition the form asks for.
  config.exercises.edition = form.elements.edition.value;
  const doc = buildDocument({ markdown }, config); // the faces are loaded by now
  showPages(doc, { title });
  document.querySelectorAll('#pt-actions a, [data-postext-pdf]').forEach((old) => old.remove());
  offerPdf(() => renderToPdf(doc, { fontProvider: fontsourceProvider }),
    `${RECIPE}-${LANG}-${config.exercises.edition}.pdf`);
}
form.addEventListener('change', render);
render();

exercises.edition: 'teacher' prints every answer in its slot in the colour answers.color gives, here the accent: the boxes of the mental sums, the digits of each result, the quotient and remainder, the model answer of the word problem. The form above the pages edits the config in place and builds again; the fonts are already loaded, so the plain buildDocument is enough. The capture builds both sheets first, so the published pages show the pupil's sheet and then the teacher's.

The whole recipe

Sandbox
// ═══ Postext Cookbook · Nº 170 · Column arithmetic as each country writes it ═══════
// https://postext.dev/en/cookbook/column-arithmetic-by-country
// Code: MIT · Text: original (CC BY 4.0) · Pictures: none
// Fonts: Fira Sans, Lexend (SIL OFL 1.1) · Needs postext ≥ 1.59.0
import { buildDocumentWithFonts, buildDocument, renderPageToCanvas } from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';

const LANG = 'en'; // @lang: the language of the worksheet ('es' | 'en' | 'fr' | 'de')
const RECIPE = 'column-arithmetic-by-country';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: ink, one orange accent, a pale tint, the grey of squared paper
const palette = {
  ink: '#1f2328', accent: '#c4511c', tint: '#fbeee6', rule: '#b9c0c8', muted: '#5f6670',
  paper: '#ffffff',
};
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
  ...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
  { id: 'main-color', name: 'accent (defaults)', value: { hex: palette.accent, model: 'hex' } },
];
// #endregion
const [TEXT, LABEL] = ['Fira Sans', 'Lexend'];

// #region answer: operations in squares, long division as the edition's schools set it
// `- 864 : 4` in the Markdown is one operation; the style sets it digit by digit in squares
// of 7.2 mm, a cell for each figure of the answer. Long division follows the country: the
// divisor in an L right of the dividend in Spain and France (potence), `864 : 4 =` on one
// line in Germany, the bracket ⟌ in English-speaking schools.
const calculo = {
  id: 'calculo',
  number: { shape: 'disc', background: col('accent'), fontFamily: LABEL, fontWeight: 700 },
  instruction: { bold: true, color: col('ink') },
  grid: { cell: mm(9), fontFamily: TEXT, fontSize: pt(15), rule: col('rule'),
    color: col('ink') },
  arithmetic: { columns: 4,
    division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
  blank: { look: 'box', width: em(3.2), color: col('rule'), weight: pt(0.8) },
  area: { kind: 'grid', spacing: mm(5), color: col('rule'), weight: pt(0.4) },
  answers: { color: col('accent'), fontFamily: LABEL }, // the teacher's copy, in the accent
  marginTop: mm(7), marginBottom: mm(2), itemSpacing: em(0.5),
};
// #endregion

// #region head: the unit pill, the title and the lines for a name and a date
const at = (to, edge, x = 0, y = 0, size) => ({ anchor: { to, edge },
  offset: { x: mm(x), y: mm(y) }, ...(size && { size }) });
const field = (id, attr, x, width) => [

The composed script.js runs as it is: paste it into any page’s module script, or open the recipe on CodePen. Recipe folder on GitHub ↗ (opens in a new tab)

Variations

#Put the answers upside down at the foot

A puzzle-book habit: each exercise's key, turned 180° under it.

-  exercises: { edition: 'student' }, // the switch above the pages sets 'teacher'
+  exercises: { edition: 'student', solutions: { placement: 'inverted', layout: 'key' } },

#Divide under the bracket in every edition

Japanese and Chinese schools also use the bracket; the default for a language you do not list is its own.

-    division: t({ es: 'potence', fr: 'potence', de: 'inline', en: 'bracket' }) },
+    division: 'bracket' },

Pitfalls

  • Operations take whole numbers: a sum with decimals such as 12,5 + 3,75 is not read and prints as written. Keep decimals in the mental-arithmetic gaps, where the answer is text.

Credits

Text
Original prose, CC BY 4.0
Fonts
Fira Sans (SIL OFL 1.1) · Lexend (SIL OFL 1.1)
SandboxPDF