docxml
Version:
TypeScript (component) library for building and parsing a DOCX file
99 lines (98 loc) • 3.61 kB
JavaScript
// Import without assignment ensures Deno does not tree-shake this component. To avoid circular
// definitions, components register themselves in a side-effect of their module.
import './Paragraph.js';
import './Table.js';
import { Component, isComponentDefinition, } from '../classes/Component.js';
import { sectionPropertiesFromNode, sectionPropertiesToNode, } from '../properties/section-properties.js';
import { createChildComponentsFromNodes, registerComponent } from '../utilities/components.js';
import { QNS } from '../utilities/namespaces.js';
import { evaluateXPathToMap } from '../utilities/xquery.js';
import { Paragraph } from './Paragraph.js';
export const sectionChildComponentNames = [
'Table',
'Paragraph',
'BookmarkRangeStart',
'BookmarkRangeEnd',
];
/**
* A component that represents a DOCX section, which could have its own page sizing options and so
* on.
*
* In normal OOXML this information belongs at either the end of the document, or inside the
* formatting options of the last paragraph belonging to that section. This component will smooth
* that over in such a way that you can simply put `<Paragraph>` (etc.) inside `<Section>`.
*/
export class Section extends Component {
/**
* Creates an XML DOM node for this component instance.
*/
async toNode(ancestry) {
const parent = ancestry[0];
if (!parent) {
throw new Error(`Cannot serialize a section without parent context.`);
}
const siblings = isComponentDefinition(parent)
? parent.children
: await parent.children;
const isLastSection = siblings[siblings.length - 1] === this;
if (isLastSection) {
return [...(await this.childrenToNode(ancestry)), sectionPropertiesToNode(this.props)];
}
const lastChild = this.children[this.children.length - 1];
if (lastChild instanceof Paragraph) {
lastChild.setSectionProperties(this.props);
}
else {
const paragraph = new Paragraph({});
paragraph.setSectionProperties(this.props);
this.children.push(paragraph);
}
const nodes = await this.childrenToNode(ancestry);
return nodes;
}
/**
* Asserts whether or not a given XML node correlates with this component.
*/
static matchesNode(node) {
return node.nodeName === 'w:sectPr';
}
/**
* Instantiate this component from the XML in an existing DOCX file.
*/
static fromNode(node, context) {
const { children } = evaluateXPathToMap(`
map {
"children": array{
if (parent::${QNS.w}body) then (
./preceding-sibling::${QNS.w}*[
not(./${QNS.w}pPr/${QNS.w}sectPr) and
not(following-sibling::${QNS.w}p[./${QNS.w}pPr/${QNS.w}sectPr])
]
) else (
let $nth := count(../../preceding-sibling::${QNS.w}p[./${QNS.w}pPr/${QNS.w}sectPr])
return (
../../preceding-sibling::${QNS.w}*[
count(preceding-sibling::${QNS.w}p[./${QNS.w}pPr/${QNS.w}sectPr]) = $nth
],
../..
)
)
}
}
`, node);
return new Section(sectionPropertiesFromNode(node), ...createChildComponentsFromNodes(this.children, children, context));
}
}
Object.defineProperty(Section, "children", {
enumerable: true,
configurable: true,
writable: true,
value: sectionChildComponentNames
});
Object.defineProperty(Section, "mixed", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
registerComponent(Section);