fix: a style kerns where its own w:kern says so, not only the document's
Word kerns nothing unless w:kern names a size to start at, and the Office theme declares it on Title alone — but TextProps.kerning stored only the off state, and a style inherits its parent's, so a title under a document that kerns nothing could never turn it back on. Its 26pt line then ran 0.36mm over the text width and wrapped a word onto a second line where LibreOffice sets it in one (measured 151.64mm kerned against 151.98). Both states are stored now, and both formats read and write them.
This commit is contained in:
1 parent
ab25abd492
commit
6957f5682b
6 files changed
+75
-13
No files matched your search
@@ -1385,7 +1385,7 @@ function ownStyleAttrs(style: { para: Record<string, unknown>; text: Record<stri
|
||||
text['style:font-size-complex'] = size;
|
||||
}
|
||||
if (t.letterSpacingPt) text['fo:letter-spacing'] = `${t.letterSpacingPt}pt`;
|
||||
if (t.kerning === false) text['style:letter-kerning'] = 'false';
|
||||
if (t.kerning != null) text['style:letter-kerning'] = String(t.kerning);
|
||||
if (t.bold != null) text['fo:font-weight'] = t.bold ? 'bold' : 'normal';
|
||||
if (t.italic != null) text['fo:font-style'] = t.italic ? 'italic' : 'normal';
|
||||
if (t.underline) text['style:text-underline-style'] = 'solid';
|
||||
|
||||
@@ -762,7 +762,7 @@ function runTextProps(run: RunProps): TextProps {
|
||||
// Word kerns nothing unless w:kern names the size to start at, and a document that
|
||||
// says so for headings only leaves body text unkerned (probed against LibreOffice).
|
||||
const kern = run.kernHalfPt ?? 0;
|
||||
if (!kern || (run.sizeHalfPt != null && run.sizeHalfPt < kern)) out.kerning = false;
|
||||
out.kerning = kern > 0 && (run.sizeHalfPt == null || run.sizeHalfPt >= kern);
|
||||
if (run.bold != null) out.bold = run.bold;
|
||||
if (run.italic != null) out.italic = run.italic;
|
||||
if (run.underline != null) out.underline = run.underline;
|
||||
|
||||
@@ -1181,8 +1181,9 @@ function textPropsFromOdf(props: PropMap, resolver: StyleResolver): TextProps {
|
||||
const spacing = lengthToPt(props['fo:letter-spacing']);
|
||||
if (spacing) out.letterSpacingPt = Math.round(spacing * 100) / 100;
|
||||
// Kerning is on unless the file says otherwise (probed), which is the browser default
|
||||
// too — so only the "off" is worth carrying.
|
||||
if (props['style:letter-kerning'] === 'false') out.kerning = false;
|
||||
// too — but a style under one that turns it off has to say so to get it back.
|
||||
const kerning = props['style:letter-kerning'];
|
||||
if (kerning) out.kerning = kerning !== 'false';
|
||||
const weight = props['fo:font-weight'];
|
||||
if (weight) out.bold = weight === 'bold' || parseInt(weight, 10) >= 600;
|
||||
const style = props['fo:font-style'];
|
||||
|
||||
@@ -12,14 +12,16 @@ built-ins (Standard → Heading → Heading 1–5 / Title / Subtitle, plus Quota
|
||||
(parent chain root-first, nearest wins, cycle-safe), `styleOrder`, and `styleCss`. `sheet.svelte.ts`
|
||||
holds it as a reactive singleton persisted to `odf-editor-styles` (same shape as `i18n.svelte.ts`).
|
||||
|
||||
- **Pair kerning** (`TextProps.kerning` → `font-kerning: none`) is where the two formats
|
||||
- **Pair kerning** (`TextProps.kerning` → `font-kerning`) is where the two formats
|
||||
disagree: ODF kerns unless `style:letter-kerning="false"`, Word kerns **nothing** unless
|
||||
`w:kern` names the point size to start at — and a browser kerns always. Probed: the same
|
||||
string runs 5% narrower kerned, which broke a line a word early on every Word document in
|
||||
the corpus. So only the off state is stored (the editor saves `.odt`, so ODF wins the tie),
|
||||
a DOCX run below its own `w:kern` threshold counts as off, and export writes `w:kern="1"`
|
||||
wherever kerning is on. Document-wide only — `w:docDefaults` and the default paragraph
|
||||
style are where files declare it; a single run overriding it needs a mark we don't have.
|
||||
the corpus. **Both states are stored, per style**: a style inherits its parent's, so a
|
||||
Title whose own `w:kern` names 14pt has to say so to overrule a document kerning nothing
|
||||
(measured at 26pt: 0.34mm, which is the difference between a title of one line and of
|
||||
two). A style set below its own threshold counts as off. Export writes the state it has —
|
||||
ODF `style:letter-kerning`, DOCX `w:kern="1"` wherever kerning is on. A single **run**
|
||||
overriding its style still needs a mark we don't have.
|
||||
|
||||
- **Assignment**: the `ParagraphStyle` extension adds a global `styleName` attr on paragraph/heading,
|
||||
rendered as `data-style`. `setParagraphStyle(name)` switches the node type when the style has an
|
||||
|
||||
@@ -25,8 +25,8 @@ export type TextProps = {
|
||||
fontFamily?: string;
|
||||
fontSizePt?: number;
|
||||
letterSpacingPt?: number; // character spacing (Word's w:spacing, ODF fo:letter-spacing)
|
||||
// Pair kerning. ODF has it on and Word off, so only `false` is ever stored — the
|
||||
// editor saves .odt and follows ODF (which is also the browser default).
|
||||
// Pair kerning. Both states are stored: a style inherits its parent's, so a heading
|
||||
// whose file kerns needs an explicit `true` to overrule a document that does not.
|
||||
kerning?: boolean;
|
||||
bold?: boolean;
|
||||
italic?: boolean;
|
||||
@@ -236,7 +236,7 @@ export function textDeclarations(t: TextProps, asBlock = false): string[] {
|
||||
}
|
||||
if (t.fontSizePt != null) out.push(`font-size: ${t.fontSizePt}pt`);
|
||||
if (t.letterSpacingPt) out.push(`letter-spacing: ${t.letterSpacingPt}pt`);
|
||||
if (t.kerning === false) out.push('font-kerning: none');
|
||||
if (t.kerning != null) out.push(`font-kerning: ${t.kerning ? 'normal' : 'none'}`);
|
||||
if (t.bold != null) out.push(`font-weight: ${t.bold ? 700 : 400}`);
|
||||
if (t.italic != null) out.push(`font-style: ${t.italic ? 'italic' : 'normal'}`);
|
||||
if (t.underline || t.strike) {
|
||||
|
||||
Reference in new issue
Block a user