From d89fc0c14f408f173bb0766ca3dec848c59c547f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Wed, 8 Oct 2025 09:23:45 +0200 Subject: [PATCH 1/5] Table and Figure caption styles Updated to Ecma house style --- css/print.css | 22 +++++++++++++++++-- src/Clause.ts | 7 ++++++ src/Figure.ts | 6 ++--- src/Spec.ts | 2 ++ .../generated-reference/assets-inline.html | 22 +++++++++++++++++-- .../generated-reference/duplicate-ids.html | 4 ++-- .../baselines/generated-reference/figure.html | 12 +++++----- test/baselines/generated-reference/xref.html | 2 +- 8 files changed, 61 insertions(+), 16 deletions(-) diff --git a/css/print.css b/css/print.css index 10d6b2d9..52f240f7 100644 --- a/css/print.css +++ b/css/print.css @@ -254,7 +254,7 @@ emu-table td, pre, h1, emu-production, -emu-figure:has(> figure > img) figure, +emu-figure, #metadata-block { break-inside: avoid; border: unset; @@ -461,7 +461,7 @@ table > figcaption { caption-side: top; color: #000; font-weight: bold; - margin-bottom: 1rem; + margin-bottom: 0.5lh; text-align: center; } @@ -474,6 +474,14 @@ table:not(:has(tr:nth-of-type(5))) { break-inside: avoid-page; } +table tr:first-of-type, table tr:nth-of-type(2) { + break-after: avoid; +} + +table tr:last-of-type { + break-before: avoid; +} + /* This inheritance looks weird—it's due to how Prince manages continuity in tables across pages */ table > figcaption { display: table-caption; @@ -508,6 +516,16 @@ emu-figure img { height: auto; } +figure figcaption { + color: var(--black); + text-align: center; +} + +emu-figure figcaption, +emu-table figcaption { + margin-bottom: 0.5lh; +} + .attributes-tag { break-before: avoid-page; break-after: avoid-page; diff --git a/src/Clause.ts b/src/Clause.ts index 842b7a1d..cb5349c0 100644 --- a/src/Clause.ts +++ b/src/Clause.ts @@ -111,6 +111,13 @@ export default class Clause extends Builder { parentNamespace = parent.namespace; } + // Reset figure numbering for each Annex + if (this.isAnnex && !this.parentClause) { + spec._figureCounts.figure = 0; + spec._figureCounts.table = 0; + spec._figurePrefix = `${number}.`; + } + if (node.hasAttribute('namespace')) { this.namespace = node.getAttribute('namespace')!; spec.biblio.createNamespace(this.namespace, parentNamespace); diff --git a/src/Figure.ts b/src/Figure.ts index 292a6c37..f5d9b081 100644 --- a/src/Figure.ts +++ b/src/Figure.ts @@ -21,16 +21,16 @@ export default class Figure extends Builder { this.isInformative = node.hasAttribute('informative'); this.captionElem = node.querySelector('emu-caption'); - this.caption = this.type.charAt(0).toUpperCase() + this.type.slice(1) + ' ' + this.number; + this.caption = `${this.type.charAt(0).toUpperCase()}${this.type.slice(1)} ${spec._figurePrefix}${this.number}`; if (this.isInformative) { this.caption += ' (Informative)'; } if (this.captionElem) { - this.caption += ': ' + this.captionElem.innerHTML; + this.caption += ' — ' + this.captionElem.innerHTML; } else if (node.getAttribute('caption')) { - this.caption += ': ' + node.getAttribute('caption'); + this.caption += ' — ' + node.getAttribute('caption'); } if (this.id) { diff --git a/src/Spec.ts b/src/Spec.ts index 005a0fac..cedd69c7 100644 --- a/src/Spec.ts +++ b/src/Spec.ts @@ -324,6 +324,7 @@ export default class Spec { readonly log: (msg: string) => void; readonly warn: (err: Warning) => void | undefined; + /** @internal */ _figurePrefix: string; /** @internal */ _figureCounts: { [type: string]: number }; /** @internal */ _xrefs: Xref[]; /** @internal */ _ntRefs: NonTerminal[]; @@ -377,6 +378,7 @@ export default class Spec { table: 0, figure: 0, }; + this._figurePrefix = ''; this._xrefs = []; this._ntRefs = []; this._ntStringRefs = []; diff --git a/test/baselines/generated-reference/assets-inline.html b/test/baselines/generated-reference/assets-inline.html index 6470f7d1..c38359c8 100644 --- a/test/baselines/generated-reference/assets-inline.html +++ b/test/baselines/generated-reference/assets-inline.html @@ -3608,7 +3608,7 @@ pre, h1, emu-production, -emu-figure:has(> figure > img) figure, +emu-figure, #metadata-block { break-inside: avoid; border: unset; @@ -3815,7 +3815,7 @@ caption-side: top; color: #000; font-weight: bold; - margin-bottom: 1rem; + margin-bottom: 0.5lh; text-align: center; } @@ -3828,6 +3828,14 @@ break-inside: avoid-page; } +table tr:first-of-type, table tr:nth-of-type(2) { + break-after: avoid; +} + +table tr:last-of-type { + break-before: avoid; +} + /* This inheritance looks weird—it's due to how Prince manages continuity in tables across pages */ table > figcaption { display: table-caption; @@ -3862,6 +3870,16 @@ height: auto; } +figure figcaption { + color: var(--black); + text-align: center; +} + +emu-figure figcaption, +emu-table figcaption { + margin-bottom: 0.5lh; +} + .attributes-tag { break-before: avoid-page; break-after: avoid-page; diff --git a/test/baselines/generated-reference/duplicate-ids.html b/test/baselines/generated-reference/duplicate-ids.html index d5369f50..0ab286ad 100644 --- a/test/baselines/generated-reference/duplicate-ids.html +++ b/test/baselines/generated-reference/duplicate-ids.html @@ -31,7 +31,7 @@

1.1 Sub A

2 Section A: Extras

-
Table 1 (Informative): A Table Of Stuff
+
Table 1 (Informative) — A Table Of Stuff
@@ -42,7 +42,7 @@

2 Section A: Extras

3 Section A: Extras

-
Table 2 (Informative): A Table Of Stuff
+
Table 2 (Informative) — A Table Of Stuff
Column 1Column 2
ValueValue 2
diff --git a/test/baselines/generated-reference/figure.html b/test/baselines/generated-reference/figure.html index b482387a..21f5db60 100644 --- a/test/baselines/generated-reference/figure.html +++ b/test/baselines/generated-reference/figure.html @@ -23,11 +23,11 @@ -
Figure 2 (Informative): Informative figure
+
Figure 2 (Informative) — Informative figure
this is a figure!
-
Table 1: An example table
+
Table 1 — An example table
Column 1Column 2
ValueValue 2
@@ -39,7 +39,7 @@
Column 1Column 2
-
Table 2 (Informative): An example table 2
+
Table 2 (Informative) — An example table 2
@@ -49,12 +49,12 @@
Column 1Column 2
ValueValue 2
-
Figure 3 (Informative): This is the caption
+
Figure 3 (Informative) — This is the caption
this is a figure!
-
Table 3: This is a table
+
Table 3 — This is a table
@@ -67,7 +67,7 @@
-
Table 4 (Informative): This is a second table
+
Table 4 (Informative) — This is a second table
diff --git a/test/baselines/generated-reference/xref.html b/test/baselines/generated-reference/xref.html index 70ae4005..50eb4c51 100644 --- a/test/baselines/generated-reference/xref.html +++ b/test/baselines/generated-reference/xref.html @@ -105,7 +105,7 @@

1.1 AbstractOp

this is a figure! -
Table 1: An example table
+
Table 1 — An example table
Column 1Column 2
From d0edfbf285f97f80661e1aa4e9b0bc9ef62a8b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Thu, 13 Nov 2025 17:21:19 +0900 Subject: [PATCH 2/5] clarify em dash in figure captions --- src/Figure.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Figure.ts b/src/Figure.ts index f5d9b081..84dedccc 100644 --- a/src/Figure.ts +++ b/src/Figure.ts @@ -28,9 +28,9 @@ export default class Figure extends Builder { } if (this.captionElem) { - this.caption += ' — ' + this.captionElem.innerHTML; + this.caption += ' \u2014 ' + this.captionElem.innerHTML; } else if (node.getAttribute('caption')) { - this.caption += ' — ' + node.getAttribute('caption'); + this.caption += ' \u2014 ' + node.getAttribute('caption'); } if (this.id) { @@ -60,6 +60,6 @@ export default class Figure extends Builder { const captionElem = spec.doc.createElement('figcaption'); captionElem.innerHTML = figure.caption; - node.childNodes[0].insertBefore(captionElem, node.childNodes[0].firstChild); + node.childNodes[0].insertBefore(captionElem, node.childNodes[0].firstChild); } } From 9e6d3e0a2ba51273c925152ebccdfc98f2c4cdf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Thu, 13 Nov 2025 17:23:02 +0900 Subject: [PATCH 3/5] Position emu-figure captions _below_ figures --- src/Figure.ts | 5 +++++ test/baselines/generated-reference/figure.html | 12 ++++++------ test/baselines/generated-reference/xref.html | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Figure.ts b/src/Figure.ts index 84dedccc..271de9b6 100644 --- a/src/Figure.ts +++ b/src/Figure.ts @@ -60,6 +60,11 @@ export default class Figure extends Builder { const captionElem = spec.doc.createElement('figcaption'); captionElem.innerHTML = figure.caption; + + if (node.nodeName.split('-')[1] === 'FIGURE') { + node.firstElementChild?.appendChild(captionElem); + } else { node.childNodes[0].insertBefore(captionElem, node.childNodes[0].firstChild); + } } } diff --git a/test/baselines/generated-reference/figure.html b/test/baselines/generated-reference/figure.html index 21f5db60..8084bfb6 100644 --- a/test/baselines/generated-reference/figure.html +++ b/test/baselines/generated-reference/figure.html @@ -18,14 +18,14 @@
-
Figure 1
+
this is a figure! -
+
Figure 1
-
Figure 2 (Informative) — Informative figure
+
this is a figure! -
+
Figure 2 (Informative) — Informative figure
Table 1 — An example table
Column 1Column 2
@@ -49,10 +49,10 @@
-
Figure 3 (Informative) — This is the caption
+
this is a figure! -
+
Figure 3 (Informative) — This is the caption
Table 3 — This is a table
diff --git a/test/baselines/generated-reference/xref.html b/test/baselines/generated-reference/xref.html index 50eb4c51..cd510240 100644 --- a/test/baselines/generated-reference/xref.html +++ b/test/baselines/generated-reference/xref.html @@ -101,9 +101,9 @@

1.1 AbstractOp

And a third!

-
Figure 1
+
this is a figure! -
+
Figure 1
Table 1 — An example table
From aca4d6ad50224fe5d07e0e11a4efe04a63abc463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Sat, 19 Sep 2026 13:16:20 +0200 Subject: [PATCH 4/5] Update baselines to Ecma caption style --- css/print.css | 3 ++- test/baselines/generated-reference/abstract-methods.html | 2 +- test/baselines/generated-reference/assets-inline.html | 3 ++- test/baselines/generated-reference/internal-methods.html | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/css/print.css b/css/print.css index 52f240f7..7d57fb5c 100644 --- a/css/print.css +++ b/css/print.css @@ -474,7 +474,8 @@ table:not(:has(tr:nth-of-type(5))) { break-inside: avoid-page; } -table tr:first-of-type, table tr:nth-of-type(2) { +table tr:first-of-type, +table tr:nth-of-type(2) { break-after: avoid; } diff --git a/test/baselines/generated-reference/abstract-methods.html b/test/baselines/generated-reference/abstract-methods.html index c19532c1..4fccd9bc 100644 --- a/test/baselines/generated-reference/abstract-methods.html +++ b/test/baselines/generated-reference/abstract-methods.html @@ -17,7 +17,7 @@
-
Table 1: Abstract Methods of Module Record
+
Table 1 — Abstract Methods of Module Record
diff --git a/test/baselines/generated-reference/assets-inline.html b/test/baselines/generated-reference/assets-inline.html index c38359c8..603ade9f 100644 --- a/test/baselines/generated-reference/assets-inline.html +++ b/test/baselines/generated-reference/assets-inline.html @@ -3828,7 +3828,8 @@ break-inside: avoid-page; } -table tr:first-of-type, table tr:nth-of-type(2) { +table tr:first-of-type, +table tr:nth-of-type(2) { break-after: avoid; } diff --git a/test/baselines/generated-reference/internal-methods.html b/test/baselines/generated-reference/internal-methods.html index 1707a985..c366d98c 100644 --- a/test/baselines/generated-reference/internal-methods.html +++ b/test/baselines/generated-reference/internal-methods.html @@ -17,7 +17,7 @@
-
Table 1: Internal Methods of Object
+
Table 1 — Internal Methods of Object
From 869178dcf5c102b32069b1d4738e632683eaed70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Tue, 22 Sep 2026 18:28:26 +0200 Subject: [PATCH 5/5] Add figure numbering earlier in the constructor to maintain xrefs. --- src/Figure.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Figure.ts b/src/Figure.ts index 271de9b6..76313a46 100644 --- a/src/Figure.ts +++ b/src/Figure.ts @@ -5,7 +5,7 @@ import Builder from './Builder'; export default class Figure extends Builder { type: string; - number: number; + number: number | string; id: string | null; isInformative: boolean; captionElem: HTMLElement | null; @@ -16,12 +16,12 @@ export default class Figure extends Builder { constructor(spec: Spec, node: HTMLElement) { super(spec, node); this.type = node.nodeName.split('-')[1].toLowerCase(); - this.number = ++spec._figureCounts[this.type]; + this.number = `${spec._figurePrefix}${++spec._figureCounts[this.type]}`; this.id = node.getAttribute('id'); this.isInformative = node.hasAttribute('informative'); this.captionElem = node.querySelector('emu-caption'); - this.caption = `${this.type.charAt(0).toUpperCase()}${this.type.slice(1)} ${spec._figurePrefix}${this.number}`; + this.caption = `${this.type.charAt(0).toUpperCase()}${this.type.slice(1)} ${this.number}`; if (this.isInformative) { this.caption += ' (Informative)'; @@ -61,10 +61,11 @@ export default class Figure extends Builder { const captionElem = spec.doc.createElement('figcaption'); captionElem.innerHTML = figure.caption; - if (node.nodeName.split('-')[1] === 'FIGURE') { - node.firstElementChild?.appendChild(captionElem); + // Captions go below figures but above tables etc. (per Ecma house style) + if (node.nodeName === 'EMU-FIGURE') { + ele.appendChild(captionElem); } else { - node.childNodes[0].insertBefore(captionElem, node.childNodes[0].firstChild); + ele.insertBefore(captionElem, ele.firstChild); } } }