Typografie-Specs — Engine.useFont, mehrzeilig, Word-Wrap, textAlign

Hero-Komposition über einem beliebigen Foto: Titel + Untertitel + Body-Absatz + Code-Snippet, alles über wiederverwendbare `Engine.useFont()`-Specs gesteuert. Stage 2 der Font-Engine — Stil einmal deklarieren, mehrfach anwenden, Varianten mit `.with()` ableiten ohne jedes Feld erneut anzugeben. `\n` setzt explizite Zeilenumbrüche; `maxWidth` aktiviert heuristisches Word-Wrap so dass Absätze in eine Spalte passen. `textAlign` (`"left"` / `"center"` / `"right"`) steuert die Ankerposition pro Zeile. Ideal als Startpunkt für Cover-Bild-Generatoren, Social-Media- Hero-Karten, Foto-mit-Overlay-Tools oder jede „Titel + Tagline + Body"-Darstellung, die sonst eine Layout-Library brauchen würde.

INPUT
INPUT — Typografie-Specs — Engine.useFont, mehrzeilig, Word-Wrap, textAlign
Photo + typographic overlay
Photo + typographic overlay — Typografie-Specs — Engine.useFont, mehrzeilig, Word-Wrap, textAlign
JavaScript
// Reusable typography specs + multi-line text + word-wrap on top of a
// photo background. Demonstrates Engine.useFont() (Stage 2 of the
// font-engine work) — declare a style once, apply it many times,
// optionally derive variants with .with().
// demo_font_layout.js
//!INPUT: INPUT
//!OUTPUT: OUTPUT
//!PARAM: TITLE:string=OpticScript
//!PARAM: SUBTITLE:string=Native-speed image processing for everyone

const img = Engine.loadImage(INPUT);
const W = img.width, H = img.height;

// Darken the photo slightly so the white text reads cleanly.
img.brightness(0.55).vignette(0.4);

// ── Reusable specs ─────────────────────────────────────────────────
// One declaration, many applications. .with() spawns a variant with
// one or two fields overridden — keeps related styles visually linked.

const titleStyle = Engine.useFont("Inter", Math.round(W / 12), {
    color:      "#ffffff",
    textAlign:  "center",
    lineHeight: 1.05,
});
const subtitle = titleStyle.with({
    size:  Math.round(W / 30),
    color: "#ffe080",
});
const body = Engine.useFont("Inter", Math.round(W / 50), {
    color:      "#e8e8f0",
    textAlign:  "left",
    lineHeight: 1.5,
    maxWidth:   Math.round(W * 0.55),
});
const monoCaption = Engine.useFont("JetBrains Mono", Math.round(W / 70), {
    color:      "#888",
    textAlign:  "right",
});

// ── Title block (centered) ─────────────────────────────────────────
img.drawText(TITLE,    W / 2, H * 0.30, titleStyle);
img.drawText(SUBTITLE, W / 2, H * 0.40, subtitle);

// ── Body paragraph (auto-wrapped at maxWidth) ──────────────────────
// Heuristic word-wrap — the engine splits the sentence into greedy
// lines so each one fits inside maxWidth. Precise per-string
// measurement arrives in Stage 3 (Engine.measureText).
img.drawText(
    "Engine.useFont() produces a reusable spec — declare a style " +
    "once, apply it many times. Multi-line and word-wrap come for " +
    "free; .with() derives related variants without restating every " +
    "field.",
    W * 0.06, H * 0.55, body,
);

// ── Code overlay (mono, multi-line via \n) ────────────────────────
// Pre-shrunk font size + manual line breaks. Notice both the mono
// font and the right-aligned monoCaption use the same drawText call —
// it's just a string that happens to have \n inside.
img.drawText(
    "const h1 = Engine.useFont('Inter', 48, {\n" +
    "  color: '#fff', textAlign: 'center'\n" +
    "});\n" +
    "img.drawText('Hello', W/2, 60, h1);",
    W * 0.06, H * 0.78,
    Engine.useFont("JetBrains Mono", Math.round(W / 55), {
        color:      "#5fb3e9",
        textAlign:  "left",
        lineHeight: 1.35,
    }),
);

// Bottom-right caption — right-aligned text needs no manual width math.
img.drawText("MLC OpticScript · Engine.useFont()", W - W * 0.04, H - H * 0.04, monoCaption);

img.save(OUTPUT);
img.free();

// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0