Skip to content

Commit b19c4a0

Browse files
author
Gabe Webb
committed
Add basic functionality for reading section properties for evenly sized columns.
1 parent 0557542 commit b19c4a0

File tree

3 files changed

+146
-38
lines changed

3 files changed

+146
-38
lines changed

src/properties/section-properties.test.ts

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ describe('Section formatting', () => {
2222
w:h="1600"
2323
w:orient="landscape"
2424
/>
25-
<w:pgMar
26-
w:top="1000"
27-
w:right="1000"
28-
w:bottom="1000"
29-
w:left="1000"
30-
w:header="1000"
31-
w:footer="1000"
32-
w:gutter="1000"
33-
/>
25+
<w:pgMar
26+
w:top="1000"
27+
w:right="1000"
28+
w:bottom="1000"
29+
w:left="1000"
30+
w:header="1000"
31+
w:footer="1000"
32+
w:gutter="1000"
33+
/>
3434
</w:sectPr>`,
3535
{
3636
pageWidth: twip(1200),
@@ -49,6 +49,48 @@ describe('Section formatting', () => {
4949
);
5050
});
5151

52+
describe('Section column formatting for equally sized columns', () => {
53+
test(
54+
`<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}>
55+
<w:cols w:num="3" w:equalwidth="1" w:sep="0" w:space="720"/>
56+
</w:sectPr>`,
57+
{
58+
columns: {
59+
numberOfColumns: 3,
60+
equalWidth: true,
61+
separator: false,
62+
columnSpacing: twip(720)
63+
}
64+
}
65+
);
66+
});
67+
68+
69+
70+
describe('Section column formatting for differently sized columns', () => {
71+
test(
72+
`<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}>
73+
<w:cols w:num="3" w:equalwidth="0" w:sep="0">
74+
<w:col w:w="1440" w:space="720"/>
75+
<w:col w:w="1440" w:space="720" />
76+
<w:col w:w="2880" />
77+
</w:cols>
78+
</w:sectPr>`,
79+
{
80+
columns: {
81+
numberOfColumns: 3,
82+
equalWidth: false,
83+
separator: true,
84+
columns: [
85+
{columnSize: twip(1440), columnSpacing: twip(720)},
86+
{columnSize: twip(1440), columnSpacing: twip(720)},
87+
{columnSize: twip(1440), columnSpacing: null}
88+
]
89+
}
90+
}
91+
);
92+
});
93+
5294
describe('Section header/footer references', () => {
5395
test(
5496
`<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}>

src/properties/section-properties.ts

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ import { evaluateXPathToMap } from '../utilities/xquery.ts';
1010
* https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_rPr_topic_ID0EIEKM.html
1111
*/
1212
export type SectionProperties = {
13+
/**
14+
* The column layout for a particular section of the document
15+
*/
16+
columns?: {
17+
numberOfColumns: null | number;
18+
equalWidth: null | boolean;
19+
separator: null | boolean;
20+
columnSpacing?: null | Length;
21+
columns?: {columnSize: Length, columnSpacing: Length | null }[]
22+
};
23+
1324
/**
1425
* A reference to the header portion on every page in this section.
1526
*/
@@ -54,41 +65,70 @@ export type SectionProperties = {
5465
export function sectionPropertiesFromNode(node?: Node | null): SectionProperties {
5566
if (!node) {
5667
return {};
57-
}
68+
};
5869
const data = evaluateXPathToMap<SectionProperties>(
59-
`map {
60-
"headers": map {
61-
"first": ./${QNS.w}headerReference[@${QNS.w}type = 'first']/@${QNS.r}id/string(),
62-
"even": ./${QNS.w}headerReference[@${QNS.w}type = 'even']/@${QNS.r}id/string(),
63-
"odd": ./${QNS.w}headerReference[@${QNS.w}type = 'default']/@${QNS.r}id/string()
64-
},
65-
"footers": map {
66-
"first": ./${QNS.w}footerReference[@${QNS.w}type = 'first']/@${QNS.r}id/string(),
67-
"even": ./${QNS.w}footerReference[@${QNS.w}type = 'even']/@${QNS.r}id/string(),
68-
"odd": ./${QNS.w}footerReference[@${QNS.w}type = 'default']/@${QNS.r}id/string()
69-
},
70-
"pageWidth": docxml:length(${QNS.w}pgSz/@${QNS.w}w, 'twip'),
71-
"pageHeight": docxml:length(${QNS.w}pgSz/@${QNS.w}h, 'twip'),
72-
"pageOrientation": ./${QNS.w}pgSz/@${QNS.w}orient/string(),
73-
"pageMargin": map {
74-
"top": docxml:length(./${QNS.w}pgMar/@${QNS.w}top, 'twip'),
75-
"right": docxml:length(./${QNS.w}pgMar/@${QNS.w}right, 'twip'),
76-
"bottom": docxml:length(./${QNS.w}pgMar/@${QNS.w}bottom, 'twip'),
77-
"left": docxml:length(./${QNS.w}pgMar/@${QNS.w}left, 'twip'),
78-
"header": docxml:length(./${QNS.w}pgMar/@${QNS.w}header, 'twip'),
79-
"footer": docxml:length(./${QNS.w}pgMar/@${QNS.w}footer, 'twip'),
80-
"gutter": docxml:length(./${QNS.w}pgMar/@${QNS.w}gutter, 'twip')
81-
},
82-
"isTitlePage": exists(./${QNS.w}titlePg) and (not(./${QNS.w}titlePg/@${QNS.w}val) or docxml:st-on-off(./${QNS.w}titlePg/@${QNS.w}val))
83-
}`,
70+
`let $propsMap := map {
71+
"headers": map {
72+
"first": ./${QNS.w}headerReference[@${QNS.w}type = 'first']/@${QNS.r}id/string(),
73+
"even": ./${QNS.w}headerReference[@${QNS.w}type = 'even']/@${QNS.r}id/string(),
74+
"odd": ./${QNS.w}headerReference[@${QNS.w}type = 'default']/@${QNS.r}id/string()
75+
},
76+
"footers": map {
77+
"first": ./${QNS.w}footerReference[@${QNS.w}type = 'first']/@${QNS.r}id/string(),
78+
"even": ./${QNS.w}footerReference[@${QNS.w}type = 'even']/@${QNS.r}id/string(),
79+
"odd": ./${QNS.w}footerReference[@${QNS.w}type = 'default']/@${QNS.r}id/string()
80+
},
81+
"columns": map {
82+
"numberOfColumns": ./${QNS.w}cols/@${QNS.w}num/number(),
83+
"separator": docxml:st-on-off(./${QNS.w}cols/@${QNS.w}sep),
84+
"equalWidth": docxml:st-on-off(./${QNS.w}cols/@${QNS.w}equalWidth)
85+
},
86+
"pageWidth": docxml:length(${QNS.w}pgSz/@${QNS.w}w, 'twip'),
87+
"pageHeight": docxml:length(${QNS.w}pgSz/@${QNS.w}h, 'twip'),
88+
"pageOrientation": ./${QNS.w}pgSz/@${QNS.w}orient/string(),
89+
"pageMargin": map {
90+
"top": docxml:length(./${QNS.w}pgMar/@${QNS.w}top, 'twip'),
91+
"right": docxml:length(./${QNS.w}pgMar/@${QNS.w}right, 'twip'),
92+
"bottom": docxml:length(./${QNS.w}pgMar/@${QNS.w}bottom, 'twip'),
93+
"left": docxml:length(./${QNS.w}pgMar/@${QNS.w}left, 'twip'),
94+
"header": docxml:length(./${QNS.w}pgMar/@${QNS.w}header, 'twip'),
95+
"footer": docxml:length(./${QNS.w}pgMar/@${QNS.w}footer, 'twip'),
96+
"gutter": docxml:length(./${QNS.w}pgMar/@${QNS.w}gutter, 'twip')
97+
},
98+
"isTitlePage": exists(./${QNS.w}titlePg) and (not(./${QNS.w}titlePg/@${QNS.w}val) or docxml:st-on-off(./${QNS.w}titlePg/@${QNS.w}val))
99+
}
100+
let $colSpacing := if (exists(./${QNS.w}cols/@${QNS.w}space)) then (docxml:length(./${QNS.w}cols/@${QNS.w}space, 'twip')) else ()
101+
let $cols := ./${QNS.w}cols/${QNS.w}col
102+
let $colMap :=
103+
if (exists($cols))
104+
then (
105+
for-each(
106+
$cols,
107+
function($c) {
108+
map {
109+
"columnsSize": docxml:length($c/@${QNS.w}w, 'twip'),
110+
"columnSpacing": docxml:length($c/@${QNS.w}space, 'twip')
111+
}
112+
}
113+
)
114+
)
115+
else()
116+
117+
return (
118+
if (exists($colSpacing))
119+
then (
120+
map:put($propsMap('columns'), 'columnSpacing', $colSpacing)
121+
)
122+
else(),
123+
map:put($propsMap('columns'), 'columns', $colMap)
124+
)`,
84125
node,
85126
);
86-
87127
return data;
88128
}
89129

90130
export function sectionPropertiesToNode(data: SectionProperties = {}): Node {
91-
return create(
131+
const query = create(
92132
`element ${QNS.w}sectPr {
93133
if (exists($headers('first'))) then element ${QNS.w}headerReference {
94134
attribute ${QNS.r}id { $headers('first') },
@@ -114,6 +154,24 @@ export function sectionPropertiesToNode(data: SectionProperties = {}): Node {
114154
attribute ${QNS.r}id { $footers('odd') },
115155
attribute ${QNS.w}type { 'default' }
116156
} else (),
157+
if (exists($columns)) then element ${QNS.w}cols {
158+
attribute ${QNS.w}sep { $columns('separator') },
159+
attribute ${QNS.w}equalWidth { $columns('equalWidth') },
160+
attribute ${QNS.w}num { $columns('numberOfColumns') },
161+
if (exists($columns('columnSpacing')))
162+
then (
163+
attribute ${QNS.w}columnSpacing { $columns('columnSpacing')}
164+
)
165+
else (),
166+
if (exists($columns('columns')))
167+
then (
168+
element columns {
169+
attribute ${QNS.w}w { round($columns('columns')('columnSize')('twip')) },
170+
attribute ${QNS.w}space { round($columns('columns')('columnSpacing')('twip')) }
171+
}
172+
)
173+
else()
174+
} else (),
117175
if (exists($pageWidth) or exists($pageHeight) or $pageOrientation) then element ${QNS.w}pgSz {
118176
if (exists($pageWidth)) then attribute ${QNS.w}w {
119177
round($pageWidth('twip'))
@@ -157,11 +215,19 @@ export function sectionPropertiesToNode(data: SectionProperties = {}): Node {
157215
typeof data.footers === 'string'
158216
? { first: data.footers, even: data.footers, odd: data.footers }
159217
: data.footers || {},
218+
columns: {
219+
numberOfColumns: data.columns?.numberOfColumns,
220+
separator: data.columns?.separator,
221+
equalWidth: data.columns?.equalWidth,
222+
columnSpacing: data.columns?.columnSpacing,
223+
columns: data.columns?.columns,
224+
},
160225
pageWidth: data.pageWidth || null,
161226
pageHeight: data.pageHeight || null,
162227
pageMargin: data.pageMargin || null,
163228
pageOrientation: data.pageOrientation || null,
164229
isTitlePage: data.isTitlePage || null,
165230
},
166231
);
232+
return query;
167233
}

src/utilities/xquery-functions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fontoxpath from 'https://esm.sh/[email protected]?pin=v121';
33
import { convert } from './length.ts';
44
import { QNS } from './namespaces.ts';
55

6-
export const DOCXML_NS_URI = 'https://github.com/wvbe/docxml';
6+
export const DOCXML_NS_URI = 'https://github.com/fontoxml/docxml';
77

88
fontoxpath.registerCustomXPathFunction(
99
{ namespaceURI: DOCXML_NS_URI, localName: 'length' },

0 commit comments

Comments
 (0)