---
name: evolution-pdf-template-authoring
description: Authoring HTML blocks for quote/invoice PDF templates — QtWebKit limits and the td vertical-align trap in the tenant stylesheet
metadata: 
  node_type: memory
  type: project
  originSessionId: c31d21e9-02e4-4f0b-8b95-1bff0b692aac
  modified: 2026-07-28T02:43:13.756Z
---

Print templates (`uploads` rows, files under `companies/<tenant>/files/Forms and Templates/`) render through wkhtmltopdf = QtWebKit ≈ WebKit 534.34 (Safari 5.1, 2011), via [[evolution-pdf-render-throttle]].

**The `valign` trap.** The tenant stylesheet (upload id 9, `1684375380.css`) sets `td { vertical-align: text-top }`. Two consequences that cost a full debugging round on template 6 (2026-07-28):
- A stylesheet rule outranks the `valign="top"`/`"middle"` HTML attribute, so `valign` is **dead** in these templates. Set alignment via inline `style="vertical-align: top"` — inline wins.
- `text-top` is not legal on a table cell, so it falls back to `baseline`. A cell holding a `display:block` image has no line box → its baseline is the cell *bottom*, so the neighbouring text column gets pushed down by ~the image height. Symptom: "text sits low and the row is far taller than its content."

Fixing the stylesheet itself would shift layout in every template using id 9 — check blast radius first.

**QtWebKit gaps** (all bite silently): no unprefixed `linear-gradient()` (add `-webkit-gradient` + `-webkit-linear-gradient` fallbacks), no flexbox/`gap`/`aspect-ratio`/`clamp()`/`object-fit`/`100vh`/`box-sizing`. Use tables for layout. A gradient is a background *image* — add `background-repeat: no-repeat` + `background-size: 100% 100%` or it tiles. Put the gradient on a wrapping `div`, not the `table`.

**Links:** never wrap a block element (`table`, `div`) in `<a>` — the pre-HTML5 parser closes the anchor early and the content ends up outside the link. Wrap inline content only; use several anchors.

**Images:** PNG/JPEG/GIF/BMP only — WebP needs the `qtimageformats` module, absent from static wkhtmltopdf builds. The render host has no outbound internet, so remote CDN images come back blank; host on `shane.evolutionerp.com.au` (see [[session-link-hostname]]).

**Editing:** the `formedit.inc` textarea posts to `formeditsave.php`. TinyMCE deliberately never attaches (`selector:'textareas'` — plural) so raw HTML survives; don't "fix" that typo.
