UNPKG

docxml

Version:

TypeScript (component) library for building and parsing a DOCX file

99 lines (98 loc) 3.61 kB
// 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);