tablelayout

command
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

Documentation

Overview

Command tablelayout converts a div-based page into the table markup an email client will render, and refuses the conversions whose result would depend on the document's doctype.

$ tablelayout < page.html
converted 6 rows and 14 columns
  quirks mode      no doctype: a table wrapper inside a <p> would stay in the paragraph
  refused          1 rows and 1 columns inside a <p>: converting them depends on the doctype
  spacers          3 empty cells given a non-breaking space

Mail clients render tables and disagree about everything else, so a page laid out with divs has to become one laid out with tables. The conversion is a wrapper: a <table><tr><td> before the div, a </td></tr></table> after it, and the div itself removed with its content kept.

The one conversion this refuses

A row inside a paragraph. Whether the wrapper ends up inside the paragraph or beside it depends on the document's mode, and the mode depends on the doctype - a table start tag closes an open <p> in a standards-mode document and not in a quirks-mode one. Measured, with x/net/html doing the parsing:

wrapper     no doctype (quirks)   <!doctype html>
<table>     stays in the <p>      leaves the <p>
<div>       leaves                leaves
<span>      stays                 stays

So the same input produces two different trees depending on a doctype that email templates frequently lack, and a converter that silently picks one is a converter that works on the author's machine. This one reports the document's mode, refuses the conversion inside a paragraph, and says how many it refused. The differential suite has the matrix as a test.

"Inside a paragraph" is a question about the wrapper's position, which is why the answer is not simply "was there a <p> start tag earlier". The wrapper goes in immediately before the row's start tag, so what matters is whether the paragraph is still open *there* - and most of the paragraphs in real markup are never closed by a </p> at all, but by the next block element starting. ClosesParagraph below is that list. Getting it wrong in either direction is a real cost: refuse too little and the wrapper moves under one of the two doctypes, refuse too much and a page whose first paragraph was unclosed converts nothing at all.

The empty-cell spacer

An empty <td> collapses in several clients, so an empty cell gets a non-breaking space. It goes in with lolhtml.Text rather than as "&nbsp;", because the escaping is the library's job: a literal U+00A0 is what the client needs, and writing the entity would produce "&amp;nbsp;" if this program ever had to write it through a path that escapes.

What the report is for

A conversion nobody can check is a conversion nobody should ship. The report says how many rows and columns were converted, how many were refused and why, how many spacers went in, and what the document's mode is - which is the fact that decides whether the refusals mattered.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL