fix: honour the paragraph mark's font size on every line
Its size is the block's line-height floor, not just an empty line's height: 10pt text in an 11pt style kept the taller strut and every line came out 0.4mm high. Both importers now carry it as the blockFontSize attr whenever it differs from what the block renders at, and the ODF export emits the FSZ sentinel for a filled block too.
This commit is contained in:
1 parent
8b9ba74a48
commit
0d4ed9e591
3 files changed
+21
-23
No files matched your search
@@ -2846,9 +2846,10 @@ export async function buildOdt(docJson: TiptapNode, margins: PageMargins = DEFAU
|
||||
}));
|
||||
}
|
||||
const content = node.content ?? [];
|
||||
// An empty line's font size (its paragraph-mark size) rides as an FSZ sentinel
|
||||
// that applyEmptyLineFontSizes turns into a paragraph-style fo:font-size.
|
||||
const emptyFs = content.length === 0 && typeof node.attrs?.fontSize === 'string' && node.attrs.fontSize
|
||||
// The paragraph-mark font size rides as an FSZ sentinel that
|
||||
// applyEmptyLineFontSizes turns into a paragraph-style fo:font-size — it is the
|
||||
// block's line-height floor, so it matters on a filled block as much as an empty one.
|
||||
const fsz = typeof node.attrs?.fontSize === 'string' && node.attrs.fontSize
|
||||
? `${FSZ}${node.attrs.fontSize}${FSZ}` : '';
|
||||
// Paragraph background/borders ride as a leading PBX sentinel (odf-kit has no such
|
||||
// options); applyParagraphBoxes mints the style. FSZ stays first so its own pass,
|
||||
@@ -2858,15 +2859,15 @@ export async function buildOdt(docJson: TiptapNode, margins: PageMargins = DEFAU
|
||||
// Named style, unless it is the one odf-kit puts on this node type anyway.
|
||||
const styleName = styleOf(node);
|
||||
const sty = styleName !== odfDefaultStyleOf(node) ? `${STY}${styleName}${STY}` : '';
|
||||
const marks = sty + pbx;
|
||||
const marks = fsz + sty + pbx;
|
||||
const withPbx = (p: ParagraphBuilder) => { if (marks) p.addText(marks); applyRuns(p, content); };
|
||||
|
||||
if (node.type === CUST_P) {
|
||||
if (content.length === 0) doc.addParagraph(emptyFs + marks, opts);
|
||||
if (content.length === 0) doc.addParagraph(marks, opts);
|
||||
else doc.addParagraph(withPbx, opts);
|
||||
} else if (node.type === CUST_H) {
|
||||
const level = (node.attrs?.level as number) ?? 1;
|
||||
if (content.length === 0) doc.addHeading(emptyFs + marks, level, opts);
|
||||
if (content.length === 0) doc.addHeading(marks, level, opts);
|
||||
else doc.addHeading(withPbx, level, opts);
|
||||
}
|
||||
},
|
||||
|
||||
+9
-11
@@ -702,13 +702,11 @@ function convertParagraph(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault:
|
||||
}
|
||||
}
|
||||
|
||||
// An empty line's height comes from the paragraph mark's own run props (w:pPr/w:rPr),
|
||||
// which convertInline never sees (there are no runs). Carry its font size as a block
|
||||
// attr so the empty line renders at the right height (and typed text inherits it).
|
||||
if (content.length === 0) {
|
||||
const fs = emptyLineFontSize(ppr, ctx, baseRun, level);
|
||||
if (fs) attrs.fontSize = fs;
|
||||
}
|
||||
// The paragraph mark's own run props (w:pPr/w:rPr) set the line-height floor for
|
||||
// every line, not just an empty one — a block whose text is smaller than its style
|
||||
// would otherwise keep the style's taller strut. Carried as a block attr.
|
||||
const fs = paragraphMarkFontSize(ppr, ctx, baseRun, defaults.fontSizePt);
|
||||
if (fs) attrs.fontSize = fs;
|
||||
|
||||
const node: Node = { type: level ? 'heading' : 'paragraph' };
|
||||
if (level) attrs.level = level;
|
||||
@@ -718,15 +716,15 @@ function convertParagraph(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault:
|
||||
}
|
||||
|
||||
// The paragraph mark's resolved font size (w:pPr/w:rPr, incl. its rStyle), as a CSS
|
||||
// pt string, or null when it matches the block's default (suppressed like run sizes).
|
||||
function emptyLineFontSize(ppr: Element | null, ctx: Ctx, baseRun: RunProps, level: number | null): string | null {
|
||||
// pt string, or null when it matches what the block renders at anyway (suppressed
|
||||
// like run sizes, against the same yardstick).
|
||||
function paragraphMarkFontSize(ppr: Element | null, ctx: Ctx, baseRun: RunProps, defaultPt: number): string | null {
|
||||
const rPr = fc(ppr, 'rPr');
|
||||
const rStyle = fc(rPr, 'rStyle');
|
||||
const props = mergeRunProps(mergeRunProps(baseRun, ctx.styles.styleOwn(rStyle ? wVal(rStyle) : null)), parseRunProps(rPr));
|
||||
if (props.sizeHalfPt == null) return null;
|
||||
const sizePt = props.sizeHalfPt / 2;
|
||||
const defSize = level != null ? HEADING_SIZES[level - 1] : BODY_FONT_SIZE_PT;
|
||||
return Math.abs(sizePt - defSize) > 0.05 ? `${Math.round(sizePt * 10) / 10}pt` : null;
|
||||
return Math.abs(sizePt - defaultPt) > 0.05 ? `${Math.round(sizePt * 10) / 10}pt` : null;
|
||||
}
|
||||
|
||||
// Heading + clamped level. Detect via the paragraph style id (fast path for our own
|
||||
|
||||
@@ -870,12 +870,11 @@ function convertParaLike(el: Element, ctx: Ctx, kind: BlockKind, boldByDefault =
|
||||
if (stops) attrs.tabStops = stops;
|
||||
const content = convertInline(el, ctx, baseTextProps, defaults, false);
|
||||
|
||||
// An empty line has no runs, so its height comes from the paragraph style's own
|
||||
// font size; carry it as a block attr (mirrors the docx paragraph-mark size).
|
||||
if (content.length === 0) {
|
||||
const sizePt = lengthToPt(baseTextProps['fo:font-size']);
|
||||
if (sizePt != null && Math.abs(sizePt - defaults.fontSizePt) > 0.05) attrs.fontSize = formatPt(sizePt);
|
||||
}
|
||||
// The paragraph style's own font size is the block's line-height floor on every
|
||||
// line, not only on an empty one; carry it as a block attr (mirrors the docx
|
||||
// paragraph-mark size), or text smaller than the style keeps the taller strut.
|
||||
const markSize = lengthToPt(baseTextProps['fo:font-size']);
|
||||
if (markSize != null && Math.abs(markSize - defaults.fontSizePt) > 0.05) attrs.fontSize = formatPt(markSize);
|
||||
|
||||
const node: Node = { type: isHeading ? 'heading' : 'paragraph' };
|
||||
if (isHeading) attrs.level = level;
|
||||
|
||||
Reference in new issue
Block a user