fix: scale a named style's line spacing by the font's natural line height
A proportional line spacing multiplies the font's own single line height in Word and LibreOffice, CSS multiplies the font size. The node attr already scaled by Liberation Serif's 1.15; a style's line-height went out raw, so an imported 1.08 rendered ~20% tighter than the file asks for and the drift accumulated down the page.
This commit is contained in:
1 parent
be96ee626c
commit
3a71491b58
2 files changed
+18
-7
No files matched your search
@@ -1,9 +1,9 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
import { DEFAULT_SINGLE_LINE_HEIGHT } from '../../styles/styleSheet';
|
||||
|
||||
// ODF line spacing multiplies the font's natural line height (Liberation Serif
|
||||
// ≈1.15× em); CSS unitless line-height multiplies the font size. Scale by this
|
||||
// ratio so the on-screen box matches LibreOffice. Keep in sync with editor.css.
|
||||
const LINE_HEIGHT_RATIO = 1.15;
|
||||
// ODF line spacing multiplies the font's natural line height, CSS unitless
|
||||
// line-height the font size. A node attr knows no family, so it takes the default.
|
||||
const LINE_HEIGHT_RATIO = DEFAULT_SINGLE_LINE_HEIGHT;
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
||||
@@ -198,8 +198,9 @@ export function cssFontFamily(name: string): string {
|
||||
}
|
||||
|
||||
// Single spacing is the font's *natural* line height, so it differs per family.
|
||||
// editor.css covers Liberation Serif's 1.15; only the bundled families that deviate
|
||||
// are listed, measured against LibreOffice at 12pt.
|
||||
// Liberation Serif's 1.15 is the default (editor.css); only the bundled families that
|
||||
// deviate are listed, measured against LibreOffice at 12pt.
|
||||
export const DEFAULT_SINGLE_LINE_HEIGHT = 1.15;
|
||||
const SINGLE_LINE_HEIGHT: Record<string, number> = {
|
||||
Calibri: 1.2208,
|
||||
Carlito: 1.2208,
|
||||
@@ -207,6 +208,12 @@ const SINGLE_LINE_HEIGHT: Record<string, number> = {
|
||||
'Liberation Mono': 1.1333,
|
||||
};
|
||||
|
||||
// A proportional line spacing multiplies the font's natural line height, while CSS
|
||||
// multiplies the font size — so the stored factor is scaled by the family's own.
|
||||
export function singleLineHeight(fontFamily?: string): number {
|
||||
return (fontFamily && SINGLE_LINE_HEIGHT[fontFamily]) || DEFAULT_SINGLE_LINE_HEIGHT;
|
||||
}
|
||||
|
||||
// The text half of a rule, shared with the table-style family (tableStyles.ts).
|
||||
export function textDeclarations(t: TextProps): string[] {
|
||||
const out: string[] = [];
|
||||
@@ -230,7 +237,11 @@ function declarations(r: ResolvedStyle): string[] {
|
||||
const { para: p } = r;
|
||||
const out = textDeclarations(r.text);
|
||||
if (p.textAlign) out.push(`text-align: ${p.textAlign}`);
|
||||
if (p.lineHeight) out.push(`line-height: ${p.lineHeight}`);
|
||||
if (p.lineHeight) {
|
||||
const n = parseFloat(p.lineHeight);
|
||||
const scaled = Math.round(n * singleLineHeight(r.text.fontFamily) * 1000) / 1000;
|
||||
out.push(`line-height: ${isNaN(n) ? p.lineHeight : scaled}`);
|
||||
}
|
||||
if (p.spaceBefore != null) out.push(`margin-top: ${p.spaceBefore}pt`);
|
||||
if (p.spaceAfter != null) out.push(`margin-bottom: ${p.spaceAfter}pt`);
|
||||
if (p.indent != null) out.push(`margin-left: ${p.indent}cm`);
|
||||
|
||||
Reference in new issue
Block a user