pretext-image-engine
v0.1.0
Published
A configurable image-aware text layout engine built on Pretext.
Maintainers
Readme
pretext-image-engine
A standalone, image-aware text layout engine built on @chenglou/pretext.
The engine takes:
- a full base image
- a same-canvas overlay image whose opaque pixels should stay in front
- structured scene JSON for text, regions, colors, highlight, fallback, and debug behavior
It then tries to place text inside the transparent openings without covering the subject. If the opening gets too tight and preserveFullText is enabled, it falls back below the image so nothing disappears.
Why This Exists
This repo is intentionally isolated from any website implementation. It focuses on one thing:
image input plus overlay-mask parsing plus
pretexttext layout
The goal is a reusable engine for editorial-image compositions, not a site-specific component.
Features
- Base-image plus overlay-mask composition
- Row-by-row transparent-slot parsing from the overlay alpha
pretext-powered manual line layout- Region-aware layout for multi-opening overlays
- Automatic dark/light text selection from sampled image luminance
- Optional highlight pills or blocks for better legibility
- Long-opening column splitting
- Resize-aware fallback when full text should be preserved
- Debug overlays for slots and regions
- Package-friendly API plus demo app
Install
npm install pretext-image-engineLocal Development
npm install
npm run devOther commands:
npm run typecheck
npm test
npm run build:lib
npm run build:demo
npm run build
npm run previewBasic Usage
import { createPretextImageEngine, type ImageEngineSceneConfig } from 'pretext-image-engine'
const scene: ImageEngineSceneConfig = {
meta: {
name: 'Demo scene',
alt: 'A photo with an editorial opening.'
},
assets: {
baseSrc: '/scenes/demo/base.png',
overlaySrc: '/scenes/demo/overlay.png'
},
blocks: [
{ style: 'heading', text: 'Editorial image layout.' },
{ style: 'body', text: 'The overlay keeps the subject in front while the text flows into the transparent region.' }
]
}
const mount = document.getElementById('engine')
if (!mount) {
throw new Error('Missing mount node')
}
const engine = createPretextImageEngine(mount, scene)
await engine.readyScene Config
The demo uses src/demo/sample-scene.json as a full reference scene.
There is also a schema file at schemas/scene.schema.json.
Main sections:
meta: scene identity and accessibility textassets: base image, overlay image, alpha threshold, fit modestage: aspect ratio, minimum height, background, frame stylinglayout: padding, min slot width, font downscaling, fallback trigger widthresize: whether to preserve full text and what fallback mode to usecolors: fixed or automatic text color, highlight behavior, shadows, selection colorscolumnSplit: how long transparent strips can become multiple columnsinteraction: whether text is selectabledebug: slot and region visualizationregions: named placement zones for multi-opening masksstyles: reusable typography presets foreyebrow,heading,lede,body,caption, or custom stylesblocks: the actual text content and per-block overrides
Multi-Opening Overlays
If your overlay has several transparent openings, there are two modes:
- Automatic mode: blocks are laid out wherever the transparent slots fit best.
- Region-aware mode: define named
regionsand assign blocks to them.
Example:
{
"regions": {
"sky": {
"xStart": 0.58,
"xEnd": 0.98,
"yStart": 0.04,
"yEnd": 0.42,
"anchorX": 0.78
},
"water": {
"xStart": 0.04,
"xEnd": 0.72,
"yStart": 0.5,
"yEnd": 0.96,
"anchorX": 0.22
}
},
"blocks": [
{ "style": "heading", "region": "sky", "text": "Bridge into haze." },
{ "style": "body", "region": "water", "text": "Longer body copy..." }
]
}Contrast and Highlight
The engine can sample the image under each line and switch automatically between light and dark text.
Highlights can be:
- disabled
- fixed-color pills or blocks
- automatic pills/blocks that flip tone depending on the sampled background
That means you can keep text readable on haze, water, shadow, or mixed backgrounds without hard-coding one color for the whole image.
Long Empty Regions
If an opening becomes a long strip, columnSplit can break it into multiple sub-slots on the same band.
Key fields:
mode:off,auto, orfixedpreferredColumnsmaxColumnsminColumnWidthgapapplyToStyles
Resize Behavior
If resize.preserveFullText is true, the engine tries:
- masked in-image layout
- smaller font scales down to
layout.minScale - fallback below the image if the scene still does not fit
If resize.preserveFullText is false, the engine keeps the text in the image and allows clipping instead of falling back.
Demo Assets
The demo ships with a sample scene under:
public/scenes/san-francisco/The preview app lets you:
- resize the stage
- toggle debug overlays
- toggle text selection
- edit the scene JSON directly
Package Surface
Exports:
createPretextImageEngine()PretextImageEngine- TypeScript types for the scene config
Caveats
- The engine assumes the base image and overlay share the same composition and alignment.
- Automatic placement can only infer geometry. For art-directed multi-region layouts, define named regions.
- Very small openings still need either shorter copy or fallback behavior.
License
MIT
