fix: a header/footer zone keeps its blank lines and its logo's height
A letterhead header is usually a couple of empty lines, the logo, then one more — the empty leading ones were dropped on import, and the body's content area counted every line as one text line, logo included. Page 1 of a contract form started 19mm too high.
This commit is contained in:
1 parent
d26fcae94d
commit
f62559707c
2 files changed
+20
-7
No files matched your search
@@ -66,9 +66,16 @@
|
||||
const HF_LINE_PX = 16 * 1.15;
|
||||
function hfReachPx(doc: HfDoc, distPx: number): number {
|
||||
if (!doc || hfIsEmpty(doc)) return 0;
|
||||
const inline = ((doc.content?.[0] as { content?: { type?: string }[] } | undefined)?.content ?? []);
|
||||
const lines = 1 + inline.filter((n) => n.type === 'hardBreak').length;
|
||||
return distPx + lines * HF_LINE_PX;
|
||||
const inline = ((doc.content?.[0] as { content?: { type?: string; attrs?: { height?: number } }[] } | undefined)?.content ?? []);
|
||||
// Per line, since an as-character image (a letterhead logo) makes its own line
|
||||
// as tall as it is; the others are one text line each.
|
||||
let total = 0;
|
||||
let image = 0;
|
||||
for (const n of inline) {
|
||||
if (n.type === 'hardBreak') { total += Math.max(HF_LINE_PX, image); image = 0; }
|
||||
else if (n.type === 'image' && typeof n.attrs?.height === 'number') image = Math.max(image, n.attrs.height);
|
||||
}
|
||||
return distPx + total + Math.max(HF_LINE_PX, image);
|
||||
}
|
||||
let footerDistPx = $derived(cmToPx((hfDistances ?? DEFAULT_HF_DISTANCES).footer));
|
||||
let headerDistPx = $derived(cmToPx((hfDistances ?? DEFAULT_HF_DISTANCES).header));
|
||||
|
||||
+10
-4
@@ -1613,11 +1613,12 @@ function convertHfPart(relId: string | null, ctx: Ctx): HfDoc {
|
||||
const relsPath = path.replace(/^word\/(.*)$/, 'word/_rels/$1.rels');
|
||||
const hfCtx: Ctx = { ...ctx, rels: parseRels(ctx.files[relsPath]) };
|
||||
|
||||
const inline: Node[] = [];
|
||||
// One line per source paragraph, joined by hard breaks below: an empty leading line
|
||||
// is a line of the zone too, and the body starts under the whole of it.
|
||||
const lines: Node[][] = [];
|
||||
let textAlign: string | null = null;
|
||||
const boxMaps: Record<string, string>[] = [];
|
||||
for (const p of hfParagraphs(root)) {
|
||||
if (inline.length) inline.push({ type: 'hardBreak' });
|
||||
const ppr = fc(p, 'pPr');
|
||||
if (textAlign === null) {
|
||||
const ta = (fc(ppr, 'jc') ? wVal(fc(ppr, 'jc')!) : null) ?? '';
|
||||
@@ -1628,12 +1629,17 @@ function convertHfPart(relId: string | null, ctx: Ctx): HfDoc {
|
||||
// The zone carries no styleName and no style CSS reaches it, so the yardstick is the
|
||||
// editor's own defaults — what the Header/Footer style provides has to become marks
|
||||
// (mirrors odt.ts convertHfZone, which passes no style name either).
|
||||
inline.push(...convertInline(p, hfCtx, baseRun, blockDefaults({}, null, false), true).filter((n) => n.type !== PB_MARKER));
|
||||
lines.push(convertInline(p, hfCtx, baseRun, blockDefaults({}, null, false), true).filter((n) => n.type !== PB_MARKER));
|
||||
}
|
||||
// An all-empty zone is dropped unless it carries a background/rule line (a footer that
|
||||
// is just a colored line has no text). The zone collapses to one paragraph (mergeHfBox).
|
||||
const box = mergeHfBox(boxMaps);
|
||||
if (inline.length === 0 && Object.keys(box).length === 0) return null;
|
||||
if (lines.every((l) => l.length === 0) && Object.keys(box).length === 0) return null;
|
||||
const inline: Node[] = [];
|
||||
lines.forEach((line, i) => {
|
||||
if (i) inline.push({ type: 'hardBreak' });
|
||||
inline.push(...line);
|
||||
});
|
||||
|
||||
const para: Node = { type: 'paragraph', content: inline };
|
||||
const attrs: Record<string, string> = {};
|
||||
|
||||
Reference in new issue
Block a user