fix: honour w:contextualSpacing on import
Word's List Paragraph carries it, so consecutive list points sit line-tight. Without it every one of them took the document default's 8pt after, and a document that is mostly bullets drifted more than a page.
This commit is contained in:
1 parent
821688c2cb
commit
9f6736d5a8
2 files changed
+26
No files matched your search
@@ -685,6 +685,7 @@ function convertParagraph(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault:
|
||||
// w:pPr counts as formatting on the block.
|
||||
const attrs = blockAttrs(ppr, kind, level, directJc ? jcVal : null, {});
|
||||
const styleId = styleIdOf(ppr, ctx);
|
||||
applyContextualSpacing(el, ppr, ctx, styleId, attrs);
|
||||
// Widow-orphan control has no registry home, so the resolved value rides the block.
|
||||
const directWc = fc(ppr, 'widowControl');
|
||||
if (!(directWc ? onOff(directWc) : ctx.styles.paragraphWidowControl(styleId))) {
|
||||
@@ -719,6 +720,18 @@ function convertParagraph(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault:
|
||||
return node;
|
||||
}
|
||||
|
||||
// w:contextualSpacing drops a paragraph's own spacing towards a neighbour of the same
|
||||
// style — Word's List Paragraph carries it, which is why list points sit line-tight. The
|
||||
// editor has no such mode, so the suppressed side becomes an explicit 0.
|
||||
function applyContextualSpacing(el: Element, ppr: Element | null, ctx: Ctx, styleId: string | null, attrs: Record<string, unknown>): void {
|
||||
const direct = fc(ppr, 'contextualSpacing');
|
||||
if (!(direct ? onOff(direct) : ctx.styles.paragraphContextualSpacing(styleId))) return;
|
||||
const sameStyle = (sib: Element | null) =>
|
||||
!!sib && sib.namespaceURI === W && sib.localName === 'p' && styleIdOf(fc(sib, 'pPr'), ctx) === styleId;
|
||||
if (sameStyle(el.previousElementSibling)) attrs.spaceBefore = 0;
|
||||
if (sameStyle(el.nextElementSibling)) attrs.spaceAfter = 0;
|
||||
}
|
||||
|
||||
// The paragraph mark's resolved font size (w:pPr/w:rPr, incl. its rStyle), as a CSS
|
||||
// pt string, or null when it matches what the block renders at anyway (suppressed
|
||||
// like run sizes, against the same yardstick).
|
||||
|
||||
@@ -124,6 +124,7 @@ export class DocxStyles {
|
||||
private defaultsAlign: string | null = null; // docDefaults w:pPrDefault/w:jc
|
||||
private defaultsSpacing: ParaSpacing = {}; // docDefaults w:pPrDefault/w:spacing
|
||||
private ownWidow = new Map<string, boolean>(); // style's own w:pPr/w:widowControl
|
||||
private ownContextual = new Map<string, boolean>(); // style's own w:pPr/w:contextualSpacing
|
||||
private ownTabs = new Map<string, TabStop[]>(); // style's own w:pPr/w:tabs
|
||||
private defaultsWidow: boolean | null = null; // docDefaults w:pPrDefault/w:widowControl
|
||||
private numToAbstract = new Map<string, string>();
|
||||
@@ -186,6 +187,8 @@ export class DocxStyles {
|
||||
if (sp) this.ownSpacing.set(id, readSpacing(sp));
|
||||
const wc = ppr && firstChild(ppr, 'widowControl');
|
||||
if (wc) this.ownWidow.set(id, toggle(wc));
|
||||
const cs = ppr && firstChild(ppr, 'contextualSpacing');
|
||||
if (cs) this.ownContextual.set(id, toggle(cs));
|
||||
const tabs = ppr && firstChild(ppr, 'tabs');
|
||||
if (tabs) this.ownTabs.set(id, readTabStops(tabs));
|
||||
const ind = ppr && firstChild(ppr, 'ind');
|
||||
@@ -329,6 +332,16 @@ export class DocxStyles {
|
||||
return this.paragraphTabs(this.basedOn.get(styleId) ?? null, seen);
|
||||
}
|
||||
|
||||
// w:contextualSpacing along the w:basedOn chain: the style drops its own spacing
|
||||
// between neighbouring paragraphs that share it (Word's List Paragraph does).
|
||||
paragraphContextualSpacing(styleId: string | null | undefined, seen = new Set<string>()): boolean {
|
||||
if (!styleId || seen.has(styleId)) return false;
|
||||
seen.add(styleId);
|
||||
const own = this.ownContextual.get(styleId);
|
||||
if (own != null) return own;
|
||||
return this.paragraphContextualSpacing(this.basedOn.get(styleId) ?? null, seen);
|
||||
}
|
||||
|
||||
private styleWidow(styleId: string | null | undefined, seen = new Set<string>()): boolean | null {
|
||||
if (!styleId || seen.has(styleId)) return null;
|
||||
seen.add(styleId);
|
||||
|
||||
Reference in new issue
Block a user