` background *does* become a run attribute, see below.)
## Vertical Alignment
- The importer's default for table cells is `.middleAlignment` — note that a freshly initialized `NSTextBlock` defaults to `.topAlignment` instead.
- `valign` attribute and CSS `vertical-align` map directly: `top` → `.topAlignment`, `middle` → `.middleAlignment`, `bottom` → `.bottomAlignment`, `baseline` → `.baselineAlignment`.
## Paragraph-Level Effects
Some table styling lands on the paragraph style or font rather than the blocks:
- `
` → bold font (Times-Bold with default settings) and paragraph alignment `.center`. No block-level difference to ` | `.
- `align`/`text-align` on cells → paragraph `alignment`.
- `dir="rtl"` on the table → `baseWritingDirection = .rightToLeft` on the cell paragraphs. The grid indices remain in source order; visual mirroring is the layout engine's job.
- The importer always sets an explicit `baseWritingDirection` (`.leftToRight` by default) — never `.natural`.
- ` ` inside a cell keeps its usual `paragraphSpacing` (12 pt); bare cell text has none.
## Layout Algorithm and Empty Cells
- `table-layout: fixed` → `layoutAlgorithm = .fixedLayoutAlgorithm`; everything else is `.automaticLayoutAlgorithm`. Since the importer bakes resolved absolute widths anyway, the distinction matters mainly for relayout after edits.
- `empty-cells: hide` → `hidesEmptyCells = true` on the table.
## What the System Importer Does *Not* Use Blocks For
Only table cells produce text blocks. A ` ` with padding, border, and background gets **no** `NSTextBlock`: the background color becomes a run-level background attribute and the padding/border are dropped entirely. A ` ` becomes head/tail indents (±40 pt). DTCoreText's existing use of ``TextBlock`` for padded/background block elements is *richer* than what the system importer does — that behavior must be preserved when extending the class.
## Enum Raw Values and Defaults
For a binary/archival-compatible DT layer the raw values must match exactly. Verified on macOS 26.6:
| Enum | Values |
|------|--------|
| `NSTextBlock.Dimension` | width = 0, minimumWidth = 1, maximumWidth = 2, height = 4, minimumHeight = 5, maximumHeight = 6 — **note the gap at 3** |
| `NSTextBlock.ValueType` | absoluteValueType = 0, percentageValueType = 1 |
| `NSTextBlock.Layer` | **padding = −1**, border = 0, margin = 1 |
| `NSTextBlock.VerticalAlignment` | top = 0, middle = 1, bottom = 2, baseline = 3 |
| `NSTextTable.LayoutAlgorithm` | automatic = 0, fixed = 1 |
| `NSRectEdge` | minX = 0, minY = 1, maxX = 2, maxY = 3 |
Defaults of freshly initialized instances:
- `NSTextBlock()` — all six dimensions 0 (absolute), all layer widths 0 (absolute), no border colors, no background color, `verticalAlignment = .topAlignment`.
- `NSTextTable()` — `numberOfColumns = 0`, `layoutAlgorithm = .automaticLayoutAlgorithm`, `collapsesBorders = false`, `hidesEmptyCells = false`.
## The DT Compatibility Layer
The full stack — model, parsing, layout and writing — is implemented:
1. ``TextBlock`` (`DTTextBlock`) carries the full `NSTextBlock` API: dimensions with value types, per-layer/per-edge widths with value types, per-edge border colors, and vertical alignment. The pre-existing `padding`/`backgroundColor` convenience API remains intact — `padding` maps onto the `.padding` layer. Edges are identified by `CGRectEdge`, whose raw values match `NSRectEdge` (which does not exist on iOS).
2. ``TextTable`` (`DTTextTable`) and ``TextTableBlock`` (`DTTextTableBlock`) mirror their NS counterparts with identical enum raw values. ``TextBlockConverter`` converts in both directions, preserving instance identity through per-converter caches — on macOS against the AppKit classes, and on iOS/tvOS/visionOS/Mac Catalyst 27 against the same classes that UIKit publishes with the 27 SDKs (built with Xcode 27 or later; on older SDKs the converter is compiled out). Its `addingNativeTextBlocks(to:)` / `addingTextBlocksAttribute(to:)` methods convert whole attributed strings between the DTCoreText text blocks attribute and native `NSParagraphStyle.textBlocks`. Verified on the iOS 27 simulator: TextKit 1 (`NSLayoutManager`) lays out and draws the converted tables natively, and the system HTML reader and writer round-trip them (the importer produces `NSTextTableBlock`s, the writer emits `` markup). Note that the 27 SDKs change the AppKit enum raw types from `NSUInteger` to `NSInteger` and soft-deprecate the `NSRectEdge`-based accessors in favor of new `CGRectEdge`-based ones.
3. ``TextTable`` and ``TextTableBlock`` compare **by identity**, exactly like their NS counterparts — and this turned out to be load-bearing, not stylistic: Foundation uniques value-equal attribute dictionaries *globally*, across attributed strings. With value-based equality, two equal-styled tables — even from two different documents parsed concurrently — get their runs mapped onto one canonical attribute dictionary, bleeding block instances between documents and destroying the identity grouping. (Plain ``TextBlock`` keeps its historical value-based equality.)
4. ``HTMLAttributedStringBuilder`` parses `table`/`tr`/`td`/`th`/`thead`/`tbody`/`tfoot`/`caption`/`col` into this model, matching the documented importer behavior: defaults of padding 1 pt / margin 0.5 pt / middle alignment, `cellspacing` split onto cell margins, `border` attribute giving cells 1 pt borders, `tr` colors pushed down to cells, bold+centered `th`, caption as a centered paragraph outside the table, no placeholder blocks for `rowspan`-covered positions. One deliberate difference: percentage widths are **kept** as percentage value types instead of being resolved to absolute points, so layout can resolve them against the actual frame width.
5. `CoreTextLayoutFrame` lays tables out as grids: a simplified automatic algorithm (explicit widths win and make content wrap, otherwise single-line content measurement with natural widths propagating recursively out of nested tables; leftover width from an explicit table width goes to the flexible columns), the fixed algorithm from the first row, row heights grown by `rowspan` cells, vertical alignment within row slots — including true first-baseline alignment across a row, per the `NSTextBlock.VerticalAlignment.baselineAlignment` documentation — plus justified cell text and recursive layout of nested tables. Cell and table border-box frames feed background and per-edge border drawing. With `collapsesBorders`, each boundary draws only the winning (wider) border — interior boundaries between cells and, per perimeter side, the wider of the table border and the outer cell borders — producing a single-line grid like CSS `border-collapse: collapse`. Current limitations: no full min/max-content width negotiation, no RTL column mirroring, and table content bypasses `numberOfLines` truncation.
6. ``TextBlock`` additionally models a per-edge **border style** (solid, dashed, dotted, double) — a DTCoreText extension, since `NSTextBlock` only stores width and color per edge (the system importer drops `border-style` entirely). The parser understands the full CSS border grammar: `border`/`border-{edge}` shorthands in any component order, `border-width`/`border-style`/`border-color` with 1–4 value lists in top/right/bottom/left order, per-edge longhands, the `thin`/`medium`/`thick` width keywords, the CSS rule that a style without a width implies a medium (3 px) border, and `none`/`hidden` removing an edge. The 3D styles (groove/ridge/inset/outset) render as solid. Converting to the NS classes drops the style information.
7. `HTMLWriter` writes the model back out as ``/``/`| ` markup with `colspan`/`rowspan` attributes and inline CSS for widths, backgrounds, borders, padding and vertical alignment — round-tripping structure and styling through the parser.
The demo app contains three table snippets (`Tables.html`, `TableAlignment.html`, `TableWidths.html`) exercising these scenarios, and `TableRenderPreviewTests` renders them (plus eleven focused samples) to PNGs on macOS and iOS for visual inspection.
The parity test suite (`TextBlockAppKitParityTests`) pins the model against AppKit: enum raw values, freshly-initialized defaults, identity-equality semantics, lossless round-trip conversion, and live system-importer fixtures for the structural findings above. `TableParsingTests` additionally compares DTCoreText's parser output structurally against the system importer for identical HTML. On iOS 27, `TextBlockUIKitParityTests` repeats the parity and round-trip checks against the UIKit classes and verifies the native handoff end to end: TextKit 1 grid layout (cells side by side, spans below), rendering to an image, and the system HTML writer/reader round-trip.
---
### Tools/TableInvestigation/Sample Output (Tools/TableInvestigation/sample-output.txt)
================================================================================
CONSTANTS (raw values of the AppKit enums, for DT* compatibility)
--------------------------------------------------------------------------------
NSTextBlock.Dimension:
.width = 0
.minWidth = 1
.maxWidth = 2
.height = 4
.minHeight = 5
.maxHeight = 6
NSTextBlock.ValueType:
.absoluteValueType = 0
.percentageValueType = 1
NSTextBlock.Layer:
.padding = -1
.border = 0
.margin = 1
NSTextBlock.VerticalAlignment:
.topAlignment = 0
.middleAlignment = 1
.bottomAlignment = 2
.baselineAlignment = 3
NSTextTable.LayoutAlgorithm:
.automaticLayoutAlgorithm = 0
.fixedLayoutAlgorithm = 1
NSRectEdge:
.minX = 0
.minY = 1
.maxX = 2
.maxY = 3
Defaults of freshly created NSTextBlock():
dimensions: width=0(abs) minWidth=0(abs) maxWidth=0(abs) height=0(abs) minHeight=0(abs) maxHeight=0(abs)
padding: minX=0(abs) minY=0(abs) maxX=0(abs) maxY=0(abs)
border: minX=0(abs) minY=0(abs) maxX=0(abs) maxY=0(abs)
margin: minX=0(abs) minY=0(abs) maxX=0(abs) maxY=0(abs)
borderColor: minX=nil minY=nil maxX=nil maxY=nil
backgroundColor: nil
verticalAlignment: top
Defaults of freshly created NSTextTable():
numberOfColumns=0 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
================================================================================
CASE 01-simple
HTML: Before After
--------------------------------------------------------------------------------
STRING: "Before\nA1\nB1\nA2\nB2\nAfter\n"
P0 "Before\n"
blocks=[]
font=Times-Roman@12 paraSpacing=12 writingDirection=LTR
P1 "A1\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P2 "B1\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
P3 "A2\n"
blocks=[B3→T1 r1c0]
font=Times-Roman@12 writingDirection=LTR
P4 "B2\n"
blocks=[B4→T1 r1c1]
font=Times-Roman@12 writingDirection=LTR
P5 "After\n"
blocks=[]
font=Times-Roman@12 paraSpacing=12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=14.6719(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B3: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=14.6719(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B4: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 02-th-thead
HTML:
--------------------------------------------------------------------------------
STRING: "Name\nValue\nPi\n3.14\n"
P0 "Name\n"
blocks=[B1→T1 r0c0]
font=Times-Bold@12 align=center writingDirection=LTR
P1 "Value\n"
blocks=[B2→T1 r0c1]
font=Times-Bold@12 align=center writingDirection=LTR
P2 "Pi\n"
blocks=[B3→T1 r1c0]
font=Times-Roman@12 writingDirection=LTR
P3 "3.14\n"
blocks=[B4→T1 r1c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=30(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=28.9062(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B3: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=30(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B4: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=28.9062(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 03-colspan
HTML:
--------------------------------------------------------------------------------
STRING: "Wide\nC1\nA2\nB2\nC2\n"
P0 "Wide\n"
blocks=[B1→T1 r0c0 colSpan=2]
font=Times-Roman@12 writingDirection=LTR
P1 "C1\n"
blocks=[B2→T1 r0c2]
font=Times-Roman@12 writingDirection=LTR
P2 "A2\n"
blocks=[B3→T1 r1c0]
font=Times-Roman@12 writingDirection=LTR
P3 "B2\n"
blocks=[B4→T1 r1c1]
font=Times-Roman@12 writingDirection=LTR
P4 "C2\n"
blocks=[B5→T1 r1c2]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=3 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=2
dimensions: width=32.6875(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=2 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B3: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=14.6719(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B4: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B5: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=2 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 04-rowspan
HTML:
--------------------------------------------------------------------------------
STRING: "Tall\nB1\nB2\n"
P0 "Tall\n"
blocks=[B1→T1 r0c0 rowSpan=2]
font=Times-Roman@12 writingDirection=LTR
P1 "B1\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
P2 "B2\n"
blocks=[B3→T1 r1c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=2 startingColumn=0 columnSpan=1
dimensions: width=18.5(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B3: NSTextTableBlock table=T1 startingRow=1 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=14.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 05-widths
HTML:
--------------------------------------------------------------------------------
STRING: "fixed 100\nhalf\n"
P0 "fixed 100\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "half\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
dimensions: width=627.188(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=308.594(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=308.594(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 06-legacy-attrs
HTML:
--------------------------------------------------------------------------------
STRING: "X\nY\n"
P0 "X\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "Y\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
border: minX=2(abs) minY=2(abs) maxX=2(abs) maxY=2(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
backgroundColor: #FFEEDD a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=8.67188(abs)
padding: minX=5(abs) minY=5(abs) maxX=5(abs) maxY=5(abs)
border: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=1.5(abs) minY=1.5(abs) maxX=1.5(abs) maxY=1.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
backgroundColor: #EEFFDD a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=8.67188(abs)
padding: minX=5(abs) minY=5(abs) maxX=5(abs) maxY=5(abs)
border: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=1.5(abs) minY=1.5(abs) maxX=1.5(abs) maxY=1.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
backgroundColor: #DDEEFF a=1.00
verticalAlignment: middle
================================================================================
CASE 07-css-box
HTML:
--------------------------------------------------------------------------------
STRING: "styled\nplain\n"
P0 "styled\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "plain\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=true hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=120(abs)
padding: minX=4(abs) minY=1(abs) maxX=2(abs) maxY=3(abs)
border: minX=4(abs) minY=1(abs) maxX=2(abs) maxY=3(abs)
borderColor: minX=#FF00FF a=1.00 minY=#FF0000 a=1.00 maxX=#00FF00 a=1.00 maxY=#0000FF a=1.00
backgroundColor: #ABCDEF a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=24(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 08-align-valign
HTML:
--------------------------------------------------------------------------------
STRING: "top\nbottom\nmid right\n"
P0 "top\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "bottom\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
P2 "mid right\n"
blocks=[B3→T1 r0c2]
font=Times-Roman@12 align=right writingDirection=LTR
TABLES:
T1: numberOfColumns=3 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=15.3438(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=34.0156(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: bottom
B3: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=2 columnSpan=1
dimensions: width=44.3438(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 09-nested
HTML: | Outer Aafter inner | Outer B |
--------------------------------------------------------------------------------
STRING: "Outer A\nInner 1\nInner 2\nafter inner\nOuter B\n"
P0 "Outer A\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "Inner 1\n"
blocks=[B1→T1 r0c0 | B2→T2 r0c0]
font=Times-Roman@12 writingDirection=LTR
P2 "Inner 2\n"
blocks=[B1→T1 r0c0 | B3→T2 r0c1]
font=Times-Roman@12 writingDirection=LTR
P3 "after inner\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P4 "Outer B\n"
blocks=[B4→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
T2: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=78.6719(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T2 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=34.3281(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B3: NSTextTableBlock table=T2 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=34.3281(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B4: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=38.3281(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 10-empty-caption
HTML:
--------------------------------------------------------------------------------
STRING: "The Caption\n\nB\n"
P0 "The Caption\n"
blocks=[]
font=Times-Roman@12 align=center writingDirection=LTR
P1 "\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P2 "B\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=3.32812(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=24.6875(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 11-multiparagraph
HTML:
--------------------------------------------------------------------------------
STRING: "One\nTwo\nB\n"
P0 "One\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 paraSpacing=12 writingDirection=LTR
P1 "Two\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 paraSpacing=12 writingDirection=LTR
P2 "B\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=21.1719(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=8.01562(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 12-div-blockquote
HTML: styled div quoted text
--------------------------------------------------------------------------------
STRING: "styled div\nquoted text\n"
P0 "styled div\n"
blocks=[]
font=Times-Roman@12 runBG=#FFFF00 a=1.00 writingDirection=LTR
P1 "quoted text\n"
blocks=[]
font=Times-Roman@12 headIndent=40 tailIndent=-40 paraSpacing=12 writingDirection=LTR
================================================================================
CASE 13-fixed-layout
HTML:
--------------------------------------------------------------------------------
STRING: "A\nB\n"
P0 "A\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "B\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=fixed collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=145(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=145(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 14-heights
HTML:
--------------------------------------------------------------------------------
STRING: "legacy 50\ncss 40\n"
P0 "legacy 50\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "css 40\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=46.3125(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=29.6719(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 15-colgroup
HTML:
--------------------------------------------------------------------------------
STRING: "A\nB\n"
P0 "A\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "B\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=118(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=58(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 16-empty-cells-hide
HTML:
--------------------------------------------------------------------------------
STRING: "A\n\n"
P0 "A\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=true
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=8.67188(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 17-rtl
HTML:
--------------------------------------------------------------------------------
STRING: "one\ntwo\n"
P0 "one\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=RTL
P1 "two\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=RTL
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=17.3281(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=18(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 18-table-margin
HTML:
--------------------------------------------------------------------------------
STRING: "A\n"
P0 "A\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=1 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
margin: minX=20(abs) minY=0(abs) maxX=0(abs) maxY=0(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=194(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 19-min-max-width
HTML:
--------------------------------------------------------------------------------
STRING: "min\nmaxed\n"
P0 "min\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "maxed\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=100(abs) minWidth=100(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=30(abs) maxWidth=30(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: middle
================================================================================
CASE 20-valign-baseline
HTML:
--------------------------------------------------------------------------------
STRING: "css bl\nlegacy bl\n"
P0 "css bl\n"
blocks=[B1→T1 r0c0]
font=Times-Roman@12 writingDirection=LTR
P1 "legacy bl\n"
blocks=[B2→T1 r0c1]
font=Times-Roman@12 writingDirection=LTR
TABLES:
T1: numberOfColumns=2 layoutAlgorithm=automatic collapsesBorders=false hidesEmptyCells=false
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
BLOCKS:
B1: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=0 columnSpan=1
dimensions: width=27(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: baseline
B2: NSTextTableBlock table=T1 startingRow=0 rowSpan=1 startingColumn=1 columnSpan=1
dimensions: width=43.6562(abs)
padding: minX=1(abs) minY=1(abs) maxX=1(abs) maxY=1(abs)
margin: minX=0.5(abs) minY=0.5(abs) maxX=0.5(abs) maxY=0.5(abs)
borderColor: minX=#000000 a=1.00 minY=#000000 a=1.00 maxX=#000000 a=1.00 maxY=#000000 a=1.00
verticalAlignment: baseline
DONE
---
### .Claude/Plan (.claude/plan.md)
# Plan: Issue #1305 — Replace DTHTMLParser with SwiftText HTMLParser
## Problem
DTCoreText depends on `DTFoundation` solely for `DTHTMLParser` (an ObjC libxml2 SAX wrapper). Issue #1305 asks us to swap it out for SwiftText's `HTMLParser` (also libxml2-backed, but with a Swift delegate protocol). This blocks removing the DTFoundation dependency entirely.
The core challenge: `DTHTMLAttributedStringBuilder` implements `DTHTMLParserDelegate` (ObjC protocol). SwiftText's `HTMLParserDelegate` is a **pure Swift protocol** with Swift-typed parameters (`[String: String]` instead of `NSDictionary`). An ObjC class cannot conform to it. Therefore, **the builder must be migrated to Swift first**.
## Approach: Incremental Migration with ObjC Compatibility
Add SwiftText as a package dependency (using the `SwiftTextHTML` product). Migrate the builder to Swift so it can conform to `HTMLParserDelegate`. Annotate with `@objc` to preserve backward compatibility.
## Phase 1: Add SwiftText dependency
**`Package.swift` changes:**
- Add SwiftText package dependency (from GitHub, with `HTML` trait)
- Add `SwiftTextHTML` product dependency to the `DTCoreText` target (alongside DTFoundation temporarily)
- Keep DTFoundation for now — other files still use `DTLog`, `NSString+DTURLEncoding`, etc.
## Phase 2: Migrate DTHTMLAttributedStringBuilder to Swift
This is the critical step — the builder is ~1000 lines of ObjC with GCD concurrency and block-based tag handlers.
1. **Create `DTHTMLAttributedStringBuilder.swift`** in `Core/Source/`
- `@objc public class DTHTMLAttributedStringBuilder: NSObject`
- Conform to `HTMLParserDelegate` (from SwiftText's HTMLParser module)
- Preserve the exact same public API: `init(html:options:documentAttributes:)`, `generatedAttributedString()`, `willFlushCallback`, `parseErrorCallback`, `shouldKeepDocumentNodeTree`, `abortParsing()`
- Internally use `HTMLParser` (from SwiftText) instead of `DTHTMLParser`
- The builder calls ObjC classes (DTHTMLElement, DTCSSStylesheet, etc.) — these bridge to Swift naturally
2. **Remove** `DTHTMLAttributedStringBuilder.h` and `.m`
3. **Update imports** — ObjC files that imported the builder header will get it via the generated `-Swift.h` header
4. **Validate** with the 50+ existing Swift tests in HTMLAttributedStringBuilderTests.swift
## Phase 3: Remove DTFoundation Dependency (future)
1. Audit remaining DTFoundation uses:
- `DTLog.h` — replace with `os_log` or Swift `Logger`
- `NSString+DTURLEncoding` — inline the few methods used
2. Remove `DTFoundation` from `Package.swift`
3. Remove `Externals/DTFoundation` submodule
## Key Design Decisions
- **ObjC compatibility**: Swift builder gets `@objc` so existing ObjC consumers still work. Callback blocks remain ObjC-compatible types.
- **Concurrency model**: Preserve the existing 3-queue GCD model. No async/await yet.
- **Tag handlers**: `_tagStartHandlers`/`_tagEndHandlers` become `[String: () -> Void]` dictionaries.
- **Delegate mapping** (1:1):
| DTHTMLParserDelegate (ObjC) | HTMLParserDelegate (Swift) |
|---|---|
| `parser:didStartElement:attributes:` (NSDictionary) | `parser(_:didStartElement:attributes:)` ([String:String]) |
| `parser:didEndElement:` | `parser(_:didEndElement:)` |
| `parser:foundCharacters:` | `parser(_:foundCharacters:)` |
| `parser:foundCDATA:` | `parser(_:foundCDATA:)` |
| `parser:foundComment:` | `parser(_:foundComment:)` |
| `parser:parseErrorOccurred:` (NSError) | `parser(_:parseErrorOccurred:)` (Error) |
## Files Changed
**Modified:**
- `Package.swift` — add SwiftText dependency, add SwiftTextHTML to DTCoreText target
**New:**
- `Core/Source/DTHTMLAttributedStringBuilder.swift`
**Removed:**
- `Core/Source/DTHTMLAttributedStringBuilder.h`
- `Core/Source/DTHTMLAttributedStringBuilder.m`
## Risks & Mitigations
- **Character accumulation**: SwiftText's parser accumulates characters before reporting (fewer `foundCharacters` calls). Should be an improvement but needs test validation.
- **GCD capture semantics**: ObjC `__weak`/`__strong` dance → Swift `[weak self]`. Careful translation needed.
- **Existing ObjC consumers**: `@objc` annotation + generated header ensures API compatibility.
---
### .Github/Workflows/Swift.Yml (.github/workflows/swift.yml)
---
name: Swift
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build-macos:
runs-on: macos-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- name: Select Xcode 26.0
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "26.0"
- name: Verify Swift version
run: swift --version
- name: Build & Test (macOS)
run: |
swift test --enable-swift-testing
build-ios:
runs-on: macos-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Select Xcode 26.0
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "26.0"
- name: Verify Swift version
run: swift --version
# The runner image only ships runtimes for its default Xcode; after selecting
# a pinned Xcode there may be no compatible iOS Simulator runtime.
- name: Ensure a compatible iOS Simulator runtime is present
run: |
ios_version="$(xcodebuild -version | awk '/^Xcode / { split($2, v, "."); print v[1] "." v[2] }')"
if ! xcrun simctl list runtimes available | grep -q "iOS ${ios_version} "; then
sudo xcodebuild -downloadPlatform iOS \
-buildVersion "${ios_version}" \
-architectureVariant arm64
fi
xcrun simctl list runtimes available | grep "iOS ${ios_version} "
- name: Build for iOS Simulator
run: |
xcodebuild build \
-scheme DTCoreText-Package \
-destination "generic/platform=iOS Simulator" \
-skipPackagePluginValidation
- name: Test on iOS Simulator
run: |
xcodebuild test \
-scheme DTCoreTextTests \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
-parallel-testing-enabled NO \
-skipPackagePluginValidation
--- | |