Generating a Book Cover in Node — Compositing SVG Text Over a Background With sharp, Getting Thai and Other Scripts to Render, and Sizing for KDP, Kobo and Play Books
A repeatable cover pipeline for e-books: a background image, a title block drawn as SVG, composited and resized with sharp. The font pitfalls for non-Latin scripts, why some system fonts drop Thai glyphs, and the pixel sizes the stores expect.
If you publish more than one e-book, or the same book to several stores, hand-making covers in a design tool stops scaling quickly. A cover is a background image with a title block on top, and that is a job for a script: render the text as SVG, composite it over the background with sharp, and export at each store's required size. The only hard part is fonts.
The pipeline
- Background. A photograph or generated image at least as large as the largest output, cropped to a portrait ratio near 1:1.6. A soft dark gradient over the lower half keeps light text readable on any image.
- Text as SVG. Build an SVG string with the title, subtitle and author as
<text>elements, sized in pixels relative to the canvas, withfont-family,font-weightandfill. SVG gives you exact positioning and wraps nothing, so break long titles into separate<text>lines yourself. - Composite.
sharp(background).composite([{ input: Buffer.from(svg), top: 0, left: 0 }]), then.resize()to each target and.jpeg({ quality })to a file. - Outputs. One file per store size, plus a small version for your own site.
The font problem
sharp renders SVG through librsvg, which uses the system's font configuration. Two things go wrong. First, the font you name may not be installed on the machine running the script, in which case a fallback is used silently and the cover looks different from your test. Second, the fallback for a Latin font often has no glyphs for Thai, Lao, Khmer or other scripts, so those characters render as empty boxes, or in a mismatched font that breaks the tone-mark positioning Thai depends on.
The fixes, in order of reliability:
| Approach | Effect |
|---|---|
| Name a font that is installed and contains the script, such as Tahoma or Noto Sans Thai on Windows | Correct glyphs; test once per machine |
| Install the exact font on the build machine and in the Docker image, and name it in a font stack with the script-capable font first | Reproducible across machines |
Embed the font in the SVG with @font-face and a base64 data URL |
Fully self-contained; largest SVG string, but immune to system differences |
Always render a test image and look at it before batch-producing; the failure is visual and produces no error. The same care applies to the fonts used inside the book itself, described in building an EPUB from Markdown with Node.
Sizes the stores want
Requirements change, so check each store's current page, but the pattern is stable: Kindle wants a tall portrait around 1.6:1 at a minimum of a couple of thousand pixels on the long side; Kobo and Google Play accept similar portrait JPEGs; your own product pages want something around a few hundred pixels wide. Export from the same composited master so the covers match everywhere.
Text layout tips
- Keep the title in the upper third and the author at the bottom; the middle of a photograph is where the interesting part usually is.
- For Thai, allow more line height than for Latin, since tone marks and vowels stack above and below the baseline.
- Use
text-anchor="middle"andx="50%"for centred lines rather than computing widths. - Add a subtle shadow with a duplicated
<text>offset by a few pixels in a dark colour, drawn first.
Common mistakes
- Trusting the SVG preview in a browser. The browser has different fonts from librsvg.
- Resizing the text layer after compositing. Rasterised text softens; composite at full size, then resize the whole image.
- One master for all stores without checking each store's minimum; upscale is visible.
Summary
Treat the cover as background plus SVG text, composite with sharp, and export per store from one master. Choose and test a font that actually contains your script, or embed it in the SVG, and look at the first render before you trust the pipeline. The image-processing basics are in resizing, cropping and composing images with sharp.