150 lines
3.5 KiB
TypeScript
150 lines
3.5 KiB
TypeScript
import {
|
|
WORD_NAMESPACE,
|
|
appendElement,
|
|
firstDirectChild,
|
|
millimetersToTwips,
|
|
parseXmlPart,
|
|
removeDirectChildren,
|
|
serializeXmlPart,
|
|
type XmlElement
|
|
} from "./ooxml.js";
|
|
import type { HeaderFooterReference } from "./header-footer-transform.js";
|
|
|
|
export interface ReferenceSectionOptions {
|
|
dimensions: {
|
|
width: number;
|
|
height: number;
|
|
};
|
|
margins: {
|
|
top: number;
|
|
right: number;
|
|
bottom: number;
|
|
left: number;
|
|
};
|
|
orientation: "portrait" | "landscape";
|
|
headerHeightMm: number;
|
|
footerHeightMm: number;
|
|
pageNumberStart: number;
|
|
references: HeaderFooterReference[];
|
|
usesDifferentFirstPage: boolean;
|
|
}
|
|
|
|
function insertReference(
|
|
section: XmlElement,
|
|
reference: HeaderFooterReference
|
|
) {
|
|
const element = section.ownerDocument!.createElementNS(
|
|
WORD_NAMESPACE,
|
|
`w:${reference.kind}Reference`
|
|
);
|
|
element.setAttributeNS(
|
|
WORD_NAMESPACE,
|
|
"w:type",
|
|
reference.type
|
|
);
|
|
element.setAttributeNS(
|
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
"r:id",
|
|
reference.relationshipId
|
|
);
|
|
section.insertBefore(element, section.firstChild);
|
|
}
|
|
|
|
export function transformDocumentSectionXml(
|
|
content: Uint8Array,
|
|
options: ReferenceSectionOptions
|
|
) {
|
|
const document = parseXmlPart(content, "word/document.xml");
|
|
const body = document.getElementsByTagNameNS(
|
|
WORD_NAMESPACE,
|
|
"body"
|
|
)[0];
|
|
const section = body
|
|
? firstDirectChild(body, WORD_NAMESPACE, "sectPr")
|
|
: undefined;
|
|
if (!section) {
|
|
throw new Error("word/document.xml 缺少 w:body/w:sectPr");
|
|
}
|
|
removeDirectChildren(
|
|
section,
|
|
WORD_NAMESPACE,
|
|
"headerReference",
|
|
"footerReference",
|
|
"pgSz",
|
|
"pgMar",
|
|
"pgNumType",
|
|
"titlePg"
|
|
);
|
|
for (const reference of [...options.references].reverse()) {
|
|
insertReference(section, reference);
|
|
}
|
|
|
|
const pageSize = appendElement(
|
|
section,
|
|
WORD_NAMESPACE,
|
|
"w:pgSz",
|
|
{
|
|
"w:w": String(millimetersToTwips(options.dimensions.width)),
|
|
"w:h": String(millimetersToTwips(options.dimensions.height))
|
|
}
|
|
);
|
|
if (options.orientation === "landscape") {
|
|
pageSize.setAttributeNS(
|
|
WORD_NAMESPACE,
|
|
"w:orient",
|
|
"landscape"
|
|
);
|
|
}
|
|
appendElement(section, WORD_NAMESPACE, "w:pgMar", {
|
|
"w:top": String(millimetersToTwips(options.margins.top)),
|
|
"w:right": String(millimetersToTwips(options.margins.right)),
|
|
"w:bottom": String(millimetersToTwips(options.margins.bottom)),
|
|
"w:left": String(millimetersToTwips(options.margins.left)),
|
|
"w:header": String(
|
|
millimetersToTwips(
|
|
Math.max(
|
|
0,
|
|
options.margins.top - options.headerHeightMm
|
|
)
|
|
)
|
|
),
|
|
"w:footer": String(
|
|
millimetersToTwips(
|
|
Math.max(
|
|
0,
|
|
options.margins.bottom - options.footerHeightMm
|
|
)
|
|
)
|
|
),
|
|
"w:gutter": "0"
|
|
});
|
|
appendElement(section, WORD_NAMESPACE, "w:pgNumType", {
|
|
"w:start": String(options.pageNumberStart)
|
|
});
|
|
if (options.usesDifferentFirstPage) {
|
|
appendElement(section, WORD_NAMESPACE, "w:titlePg");
|
|
}
|
|
return serializeXmlPart(document);
|
|
}
|
|
|
|
export function transformSettingsXml(
|
|
content: Uint8Array,
|
|
usesEvenAndOddPages: boolean
|
|
) {
|
|
const document = parseXmlPart(content, "word/settings.xml");
|
|
const settings = document.documentElement!;
|
|
removeDirectChildren(
|
|
settings,
|
|
WORD_NAMESPACE,
|
|
"evenAndOddHeaders"
|
|
);
|
|
if (usesEvenAndOddPages) {
|
|
appendElement(
|
|
settings,
|
|
WORD_NAMESPACE,
|
|
"w:evenAndOddHeaders"
|
|
);
|
|
}
|
|
return serializeXmlPart(document);
|
|
}
|