Repository navigation
Docx writer: the paragraph after a table sits flush against it (Word drops the line lead) #11901
Description
Explain the problem.
Word attaches a paragraph's line-spacing lead below its text and
applies none of it between a table and the following paragraph. With a
reference doc whose body style carries no before-spacing (a
double-spaced manuscript template, APA in our case), the first
paragraph after a table glues to the table's bottom rule, while the
same paragraph after another paragraph shows a full blank line.
LibreOffice applies the lead at table boundaries, so rendering there
hides the bug; Word is the ground truth.
Reproduce (stock pandoc, with the default reference doc doctored to
a double-spaced zero-after body, i.e. any real manuscript template):
cd $(mktemp -d)
pandoc -o ref.docx --print-default-data-file reference.docx
mkdir x && cd x && unzip -q ../ref.docx
python3 - <<'PY'
p = 'word/styles.xml'
s = open(p).read()
s = s.replace('<w:spacing w:before="180" w:after="180" />',
'<w:spacing w:before="0" w:after="0" w:line="480" w:lineRule="auto" />')
open(p, 'w').write(s)
PY
zip -q -r ../ref2.docx . && cd ..
cat > t.md <<'EOF'
| A | B |
|---|---|
| 1 | 2 |
Paragraph after the table.
Paragraph after a paragraph.
EOF
pandoc t.md --reference-doc=ref2.docx -o t.docx
unzip -p t.docx word/document.xml | grep -o 'w:spacing[^/]*/>'Received: every paragraph relies on the style's spacing; none
carries direct spacing, so in Word the paragraph after the table has
nothing above it but the table rule (open t.docx in Word: the first
paragraph touches the rule; the second sits a full blank line below
the first).
Expected: the writer supplies boundary air: the paragraph
following a table carries direct w:spacing w:before="240" (half the
line pitch of a double-spaced line; full lead comes from the
paragraph's own lead below its text). A heading after a table reads
the same boundary mark, since Word gives a heading nothing above it
either. The shared FirstParagraph style cannot express this (it is
also the post-heading paragraph style).
Root cause: the docx writer emits no per-paragraph boundary
spacing at all (blockToOpenXML' for Para in
src/Text/Pandoc/Writers/Docx/OpenXML.hs builds w:pPr from
envParaProperties, which carries only style/indent state), and the
reference doc's styles were never expected to compensate for Word's
asymmetric lead at table boundaries.
Fix: mark the table boundary in the writer state and let the next
paragraph (or heading) consume it as direct w:before spacing;
intervening blocks absorb the mark, and containers (divs, table cells)
stay transparent. The value 240 twips = half the line pitch was
measured in Word (an initial 120 proved too small); the exact value
wants discussion.
Note
This is 11/13 of issues identified in an attempt to create an APA styled Word target from a Typst source. The issues are ordered logically so that fundamental fixes are applied first. I have fixed all of them locally and will split and upstream the fixes one by one.
Activity
jgm commentedon Sep 25, 2026
Would it fix things if the reference.docx included some before/after spacing in the style for table?
sghng commentedon Sep 25, 2026
Not really.
So the value can live in a reference, but only the writer knows the position, hence direct w:before on the paragraph after a table, which works with any reference doc.
jgm commentedon Sep 26, 2026
Have you tried with
Openxml docs say: "When determining the spacing between any two paragraphs, a consumer shall use the maximum of the inter-line spacing in each paragraph, the spacing after the first paragraph and the spacing before the second paragraph to determine the net spacing between the paragraphs."
So, adding spacing before won't lead to an extra space between paragraphs, but it will ensure a space in this case. I'm guessing!
Edit: it doesn't seem to work the way the docs say here...
sghng commentedon Sep 26, 2026
@jgm I also tested your fix, it does have a problem, it inflates all paragraph spacing by 480 twips... For context, when I was doing the Typst --pandoc--> APA Word conversion, I prioritized
reference.docxand Lua filter, the issues I filed can't be done with them (or at least I haven't found a solution without patching pandoc)jgm commentedon Sep 26, 2026
Inserting this into the style for "Table" works fine as far as I can see:
jgm commentedon Sep 26, 2026
Here is my modified ref.docx if you want to try it.
ref.docx
sghng commentedon Sep 26, 2026
@jgm I tried your modified ref.docx, and separately rebuilt the same edit into my own APA
reference.docx. The generated document carries theafter="480"spacing in the Table style, confirmed in the output XML. It has no effect: in Word the paragraph after the table stays glued, and LibreOffice renders pixel-identical output with and without it.The reason is in the document, not the reference. Every paragraph pandoc puts in a table cell carries a paragraph
style: Compact(with its own before/after spacing of 36 twips) for simple tables,Body Textfor tables with multi-paragraph cells. A paragraph style's spacing overrides a table style'sw:pPr, so the table style's after-spacing never reaches any cell.This is a result of the #11864 and 7cf4d41 fix.
compactifyTablemakes every simple-table cell carryCompact, and multi-paragraph cells carryBody Text, so no pandoc-produced table has unstyled cell paragraphs for the table style to reach. The reference fix can only affect documents whose cells carry no paragraph style at all, which I'm guessing is what the document in your screenshot had.And dropping the styles is not an option in my case. The document is double spaced (APA manuscript), so the document default line spacing is double. An unstyled cell paragraph inherits that, and every row gets inflated, which is the row bloat of #11864 again. I pinned
Compactto single line spacing precisely to keep cells tight; its 36 twips of before/after is deliberate small padding. So the cells must carry a style, and every style we could put on them defines its own after-spacing, which shadows whatever the table style tries to add.For completeness, the one table-style property that does reach cells regardless of paragraph styles is the cell bottom margin (
tblCellMar). It engages, but the space lands inside every row, between the text and the rule. It pads the header and interior rows too, and never appears between the table and the following paragraph.So the reference can't carry this one, and direct
w:beforeon the paragraph after the table remains the only mechanism that puts air in the right place.jgm commentedon Sep 26, 2026
I'm a bit confused. See the screenshot above; this is my Word with the ref doc I sent you (double spaced, no space before or after on BodyText; but extra space after table), used to convert a markdown sample more or less like the one you gave. As you can see, the line after the table is plainly not glued; there is space between. This is using pandoc built from HEAD. If your reference doc gives different results, it must be because of some other difference in it.
jgm commentedon Sep 26, 2026
OK, when I added single spacing and no space before/after to Compact, I get
which explains why you are seing something different.
sghng commentedon Sep 26, 2026
@jgm The patched reference.docx I use is here: https://github.com/sghng/abstract/tree/main/tools, to use it simply run
bun run abstract typ2docx file.typ(requires bun). This tool will construct the reference.docx on the fly by applying the patches I defined on the vinilla reference.docx.jgm commentedon Sep 26, 2026
I don't like the idea of having the writer insert a fixed before space after a table. What about just having a special style for the paragraph following a table, similar to FirstParagraph?
sghng commentedon Sep 26, 2026
I think that's the better solution. A few nuances to be deliberate about in the implementation:
Body Texttoday, including the first-line indent in templates that use one. That's a silent regression on upgrade for anyone with a custom reference. Two ways out: fall back toBody Textwhen the reference lacks the style (today's behavior, nothing changes), or inject the placeholder withbasedOn="BodyText"so inheritance survives.--table-caption-position=below, it's the caption, which must keep its Caption style rather than take the new one. And the writer already inserts an empty paragraph between adjacent tables (separateTables, so Word doesn't merge them); that separator is also technically the paragraph after a table, and giving it the style would widen the gap between consecutive tables. The rule should be: only paragraphs that would otherwise getBody TextorFirstParagraphreceive the new style.Changes: a new style as follows, and patching the writer.