En pocas palabras
Un cuaderno escolar japonés: cuentas, casillas y opciones en una página, y kanji y lecturas en otra, de arriba abajo. Bajo cada ejercicio van sus soluciones del revés, para que el niño gire el cuaderno y las compruebe.
Lo que vas a componer
Dos páginas de una serie de ejercicios de tercero en formato B5. La de cuentas va en horizontal: una franja azul pálido con la etiqueta de la asignatura, el título y un tanuki de mascota, una línea para el nombre y una casilla para la nota, y luego ひっ算 con una casilla por cifra del resultado, casillas tras seis cuentas, opciones ア イ ウ en fila, afirmaciones ○× con un paréntesis al final de la línea y dos problemas con líneas para escribir las operaciones. La de kanji se compone como se imprime un cuaderno de 国語, de arriba abajo y de derecha a izquierda: lecturas con una casilla para el kanji, palabras subrayadas cuya lectura va entre paréntesis, opciones que rodear y un significado que elegir. Bajo cada ejercicio, sus soluciones van del revés en letra gris pequeña. El niño ve dónde están, no las lee de un vistazo y gira el cuaderno para comprobarlas.
Esta receta responde a
- ¿Cómo imprimo las soluciones boca abajo al pie de un ejercicio?
- ¿Cómo compongo una página de ejercicios japonesa en columnas verticales con el mismo diseño que la horizontal?
- ¿Cómo compongo la división larga tal como la escriben las escuelas de cada país?
La respuesta corta
// `placement: 'inverted'` sets each exercise's key right after it in a box turned 180°: the
// pupil sees where the answers are without reading them, and turns the book to check. The
// key's words come from the exercise (the options' katakana, ○ and ×, the operations), so
// the Markdown holds nothing twice.
const drill = {
id: 'drill',
number: { shape: 'square', fontFamily: TEXT, fontWeight: 600, background: col('blue') },
instruction: { fontFamily: ROUND, bold: false },
options: { layout: 'row' }, // ア イ ウ side by side (the default labels in Japanese)
trueFalse: { look: 'bracket' }, // ( ) for ○ or ×
blank: { look: 'box', width: 'answer', color: col('rule') }, // □ to write in
area: { kind: 'lines', color: col('rule') },
grid: { rule: col('rule') },
solutions: { placement: 'inverted', box: 'kotae' },
marginTop: em(1.1), marginBottom: em(0.4),
};
const kotae = { id: 'kotae', title: '', backgroundEnabled: false, border: { enabled: false },
padding: { top: pt(0), right: pt(0), bottom: pt(0), left: pt(0) },
body: { fontFamily: ROUND, fontSize: pt(8), lineHeight: pt(12), color: col('muted'),
firstLineIndent: pt(0) },
marginTop: mm(1), marginBottom: mm(2), keepTogether: true };
Ingredientes
- Funciones
- SolucionesEstilos de ejercicioTexto verticalEjerciciosOperaciones en columnaHuecos y bancos de palabrasElección y verdadero o falsoPreguntas abiertas y zonas de escrituraKana, kanji y rōmajiFuentes chinas, japonesas y coreanasLíneas laterales (bōsen)Libros encuadernados por la derechaChips en líneaTres o más columnasAperturas diseñadasAtributos de títuloImágenes en los diseños de páginaAnclaje de elementos de diseñoPaleta de color semánticaExportación a PDF
- También usa
- Columnas en un recuadro o en el textoRecuadros que se parten o noCorte de líneas en chinoEquilibrado de columnasEstilos de párrafoFuentes incrustadas en el PDFFiguras y tablas como recursos
- Tipografía
- Klee One, Zen Maru Gothic (SIL OFL 1.1)
- Recursos
kitsune-600.jpgtanuki-600.jpg- Un tanuki pequeño con gorra escolar amarilla y un lápiz enorme. (Generated With Diffusion Models, original)
- Un zorrito con bufanda roja que lee un libro. (Generated With Diffusion Models, original)
Elaboración
#1 · Gira las soluciones de cada ejercicio
El código está en la respuesta corta. placement: 'inverted' pone las soluciones tras cada ejercicio en un recuadro girado 180°, y ese recuadro va dentro del recuadro del propio ejercicio, así que un salto de página nunca los separa. Las soluciones salen del ejercicio: las operaciones con su resultado, el katakana de la opción correcta, ○ y ×, las palabras de los huecos. El estilo de recuadro de las soluciones, kotae, solo pone la letra pequeña y gris.
#2 · Una franja para la página horizontal y para la vertical
// A heading design on a vertical page is laid along its column: x runs down the page and y
// to the left, so the same band stands at the right edge, from head to foot.
const BAND = 30; // mm: the band's depth (its height across, its width down)
const header = { enabled: true, minHeight: mm(BAND + 3), slot: { elements: [
{ kind: 'box', id: 'band', reserve: false, style: { backgroundColor: col('tint'),
borderRadius: mm(4) }, placement: { anchor: { to: 'container', edge: 'top-left' },
size: { width: 'fill', height: mm(BAND) } } },
{ kind: 'image', id: 'mascot', resourceId: '{attr.pic}', decorative: true, reserve: false,
placement: { anchor: { to: 'container', edge: 'top-right' }, offset: { x: mm(-4), y: mm(1) },
size: { width: mm(28), height: mm(28) } } },
{ kind: 'text', id: 'subject', content: '{attr.kicker}', fontFamily: ROUND, fontSize: pt(10),
fontWeight: 700, color: col('ink'), box: { backgroundColor: col('sun'), borderRadius: mm(3),
padding: { top: mm(0.6), bottom: mm(0.6), left: mm(2.4), right: mm(2.4) } },
placement: { anchor: { to: 'container', edge: 'top-left' }, offset: { x: mm(5), y: mm(5) } } },
{ kind: 'text', id: 'title', content: '{titleText}', fontFamily: ROUND, fontSize: pt(20),
fontWeight: 700, color: col('ink'),
placement: { anchor: { to: '#subject', edge: 'below' }, offset: { x: mm(0), y: mm(2.5) } } },
] } };
Un diseño de título en una página vertical se coloca a lo largo de su columna: la x baja por la página y la y avanza hacia la izquierda. La franja anclada top-left con todo el ancho queda arriba en la página de cuentas y a lo largo del borde derecho en la de kanji; la mascota anclada top-right cae en el extremo de la franja en las dos. El diseño no nombra ninguna dirección.
#3 · Dos cuadernos con un solo texto
// Two drill books of one series: arithmetic across, bound on the left; kanji in columns,
// bound on the right (vertical text binds there by itself).
const [sansu, kokugo] = markdown.split(/(?=^# 春のことば)/m);
const build = (text) => withLoadedFonts(() => buildDocument({ markdown: text, resources },
config()), { ...kitFonts(FONTS), text: markdown });
const across = await build(sansu);
// 国語 is set in columns, top to bottom, right to left: the same design, turned.
writingMode = 'vertical-rl';
const docs = [across, await build(kokugo)];
La configuración es una función, así que cada composición recibe su propio objeto, y entre una y otra solo cambia writingMode. En vertical, el cuaderno de 国語 se encuaderna por la derecha él solo; showBook pone cada cuaderno a su lado del lomo. El texto va en Klee One, una letra de libro de texto, y las etiquetas en Zen Maru Gothic; las dos cargan su puntuación vertical para los paréntesis de la página de kanji.
La receta completa
// ═══ Postext Cookbook · Nº 172 · A Japanese drill with the answers upside down ═══
// https://postext.dev/en/cookbook/japanese-drill-upside-down-answers
// Code: MIT · Text: original (CC BY 4.0) · Pictures: generated with diffusion models
// Fonts: Klee One, Zen Maru Gothic (SIL OFL 1.1) · Needs postext ≥ 1.59.0
import {
buildDocument, withLoadedFonts, renderPageToCanvas, registerResourceImage,
loadVerticalAlternates,
} from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';
const LANG = 'ja'; // @lang: the language of the frame; the drill is Japanese in all
const RECIPE = 'japanese-drill-upside-down-answers';
// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: a drill book's blue, a warm yellow and a pale sky for the band
const palette = {
ink: '#23272c', // text
blue: '#2a6cb0', // the accent: exercise numbers, the title band's rule, the score box
sun: '#f2c14e', // the subject badge
tint: '#eaf2fb', // the title band
rule: '#a8bfd8', // writing lines and boxes
muted: '#69717b', // folios, the name line, the upside-down answers
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.blue, model: 'hex' } },
];
// #endregion
// 教科書体 for the text a child copies from, a round gothic for the labels.
const [TEXT, ROUND] = ['Klee One', 'Zen Maru Gothic'];
// #region answer: the answers printed upside down under each exercise
// `placement: 'inverted'` sets each exercise's key right after it in a box turned 180°: the
// pupil sees where the answers are without reading them, and turns the book to check. The
// key's words come from the exercise (the options' katakana, ○ and ×, the operations), so
// the Markdown holds nothing twice.
const drill = {
id: 'drill',
number: { shape: 'square', fontFamily: TEXT, fontWeight: 600, background: col('blue') },
instruction: { fontFamily: ROUND, bold: false },
options: { layout: 'row' }, // ア イ ウ side by side (the default labels in Japanese)
trueFalse: { look: 'bracket' }, // ( ) for ○ or ×
blank: { look: 'box', width: 'answer', color: col('rule') }, // □ to write in
area: { kind: 'lines', color: col('rule') },
grid: { rule: col('rule') },
solutions: { placement: 'inverted', box: 'kotae' },
El script.js compuesto funciona tal cual: pégalo como script de módulo en cualquier página o abre la receta en CodePen. Carpeta de la receta en GitHub ↗ (se abre en una nueva pestaña)
Variantes
#Reúne las soluciones al final
Un cuaderno con solucionario aparte deja las soluciones fuera de las páginas: chapterEnd las pone tras el último ejercicio.
- solutions: { placement: 'inverted', box: 'kotae' },
+ solutions: { placement: 'chapterEnd', box: 'kotae' },#Compón una página de pasatiempos
La página de pasatiempos gira sus soluciones del mismo modo, en tarjetas a dos columnas.
Errores frecuentes
Error frecuente
Las fuentes chinas se cargan por fragmentos, con el bloque cjk
Fontsource sirve una familia china, japonesa o coreana en un centenar de archivos por peso, cada uno con un intervalo de caracteres. loadFonts solo descarga el archivo latin, así que en pantalla los caracteres chinos salen de una fuente del sistema y se miden mal, y fontsourceProvider entrega al PDF ese archivo latin, que los imprime como cajas vacías. Añade el bloque cjk del kit, llama a loadCjkFonts(FONTS, markdown) después de loadFonts (una vez por voz, con el texto que compone, si el libro usa varias fuentes chinas) y pasa a renderToPdf fontProvider: cjkPdfProvider: ambos toman los archivos que contienen los caracteres del texto. Fuentes chinas, japonesas y coreanas →
Error frecuente
Etiqueta un texto japonés 'ja', nunca zh-Hans ni LANG
Las ediciones de una receta son en y es, pero una muestra japonesa es japonesa en las dos: `locale: LANG` la etiquetaría como inglés o español, y una etiqueta china la compondría con las reglas chinas (puntuación Kaiming, kana pequeño libre de empezar línea, 图 en lugar de 図, formas chinas de los caracteres en el PDF). Desde postext 1.30 la etiqueta es además el idioma en el que se compone el texto (en el que se mide, se pinta y se incrusta), así que cualquier etiqueta que no sea 'ja' da otras formas de glifo que las japonesas (las chinas, con una etiqueta china) y otros cortes de línea. Escribe 'ja': elige la región de Japón (corte y puntuación de la JLReq, puntos de sésamo, reparto de los furigana, etiquetas 図 y 表) y desactiva la separación silábica. El lint falla un texto con kana bajo una etiqueta zh o ko. Corte de líneas japonés (kinsoku) →
- Una lectura todavía no puede ir sobre un hueco: un ruby dentro de
[[…]]forma parte de la respuesta oculta. El ejercicio de 漢字の書き取り pone cada lectura en su propia línea y la casilla detrás, tres por fila en un grupo:::columns; en la página vertical la lectura queda al lado de la casilla, como el furigana. - Klee One solo tiene los pesos 400 y 600:
boldFontWeight: 600deja todas las negritas en un corte que existe, también los números de los apartados y de los ejercicios.
Créditos
- Receta
- Ignacio Ferro
- Texto
- Texto original, CC BY 4.0
- Imágenes
- Un tanuki pequeño con gorra escolar amarilla y un lápiz enorme. · Generated With Diffusion Models · original
- Un zorrito con bufanda roja que lee un libro. · Generated With Diffusion Models · original
- Fuentes
- Klee One (SIL OFL 1.1) · Zen Maru Gothic (SIL OFL 1.1)
- Código
- MIT, como Postext
Editar este texto ↗ (se abre en una nueva pestaña)Carpeta de la receta en GitHub ↗ (se abre en una nueva pestaña)


