forked from wvbe/docxml
-
Notifications
You must be signed in to change notification settings - Fork 3
(feat) Add column info to section properties #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
348f49f
Add basic functionality for reading section properties for evenly siz…
3ef68c8
Update how we're doing things. Add console logs to test.
83a1a30
Fix the thing
harvestcore 6d4f7e4
Use array:flatten instead of ?*
harvestcore 15a1aab
Use array:flatten in seciton-properties instead of the *? notation
d4ebab3
Remove unused imports.
d8efd0a
Fix an error introduced by rebasing.
4c97176
Cleanup, remove console logs, start adding more tests.
9b2ff64
Add function for testing a round-trip of an object, might not be nece…
44b4825
Add test.
3fcfa84
Formatting.
harvestcore File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| import { describe } from 'std/testing/bdd'; | ||
|
|
||
| import { describe } from 'std/testing/bdd'; | ||
|
|
||
| import { twip } from '../utilities/length.ts'; | ||
| import { ALL_NAMESPACE_DECLARATIONS } from '../utilities/namespaces.ts'; | ||
| import { createXmlRoundRobinTest } from '../utilities/tests.ts'; | ||
| import { | ||
| createObjectRoundRobinTest, | ||
| createXmlRoundRobinTest, | ||
| } from '../utilities/tests.ts'; | ||
| import { | ||
| SectionProperties, | ||
| sectionPropertiesFromNode, | ||
|
|
@@ -12,7 +14,12 @@ import { | |
|
|
||
| const test = createXmlRoundRobinTest<SectionProperties>( | ||
| sectionPropertiesFromNode, | ||
| sectionPropertiesToNode | ||
| ); | ||
|
|
||
| const reverseTest = createObjectRoundRobinTest<SectionProperties>( | ||
| sectionPropertiesToNode, | ||
| sectionPropertiesFromNode | ||
| ); | ||
|
|
||
| describe('Section formatting', () => { | ||
|
|
@@ -23,15 +30,15 @@ describe('Section formatting', () => { | |
| w:h="1600" | ||
| w:orient="landscape" | ||
| /> | ||
| <w:pgMar | ||
| w:top="1000" | ||
| w:right="1000" | ||
| w:bottom="1000" | ||
| w:left="1000" | ||
| w:header="1000" | ||
| w:footer="1000" | ||
| w:gutter="1000" | ||
| /> | ||
| <w:pgMar | ||
| w:top="1000" | ||
| w:right="1000" | ||
| w:bottom="1000" | ||
| w:left="1000" | ||
| w:header="1000" | ||
| w:footer="1000" | ||
| w:gutter="1000" | ||
| /> | ||
| </w:sectPr>`, | ||
| { | ||
| pageWidth: twip(1200), | ||
|
|
@@ -46,7 +53,80 @@ describe('Section formatting', () => { | |
| footer: twip(1000), | ||
| gutter: twip(1000), | ||
| }, | ||
| } | ||
| ); | ||
| }); | ||
|
|
||
| describe('Section column formatting for equally sized columns', () => { | ||
| test( | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:cols w:num="3" w:equalwidth="1" w:sep="0" w:space="720"/> | ||
| </w:sectPr>`, | ||
| { | ||
| columns: { | ||
| numberOfColumns: 3, | ||
| equalWidth: true, | ||
| separator: false, | ||
| columnSpace: twip(720), | ||
| columnDefs: [], | ||
| }, | ||
| } | ||
| ); | ||
| }); | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove extra spaces. |
||
| describe('Section column formatting for differently sized columns', () => { | ||
| test( | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:cols w:num="3" w:equalwidth="0" w:sep="1" w:space="720" > | ||
| <w:col w:w="1440" w:space="720"/> | ||
| <w:col w:w="1440" w:space="720" /> | ||
| <w:col w:w="2880" /> | ||
| </w:cols> | ||
| </w:sectPr>`, | ||
| { | ||
| columns: { | ||
| numberOfColumns: 3, | ||
| equalWidth: false, | ||
| separator: true, | ||
| columnSpace: twip(720), | ||
| columnDefs: [ | ||
| { columnWidth: twip(1440), columnSpace: twip(720) }, | ||
| { columnWidth: twip(1440), columnSpace: twip(720) }, | ||
| { columnWidth: twip(2880) }, | ||
| ], | ||
| }, | ||
| } | ||
| ); | ||
| }); | ||
|
|
||
| describe('Section column formatting for with missing properties', () => { | ||
| reverseTest( | ||
| { | ||
| columns: { | ||
| numberOfColumns: 3, | ||
| equalWidth: true, | ||
| }, | ||
| }, | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:cols w:num="3" w:equalwidth="1" /> | ||
| </w:sectPr>` | ||
| ); | ||
|
|
||
| reverseTest( | ||
| { | ||
| columns: { | ||
| columnDefs: [ | ||
| { columnWidth: twip(1440), columnSpace: twip(720) }, | ||
| { columnWidth: twip(1440) }, | ||
| ], | ||
| }, | ||
| }, | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:cols w:num="2" w:equalwidth="0"> | ||
| <w:col w:w="1440" w:space="720" /> | ||
| <w:col w:w="1440"/> | ||
| </w:cols> | ||
| </w:sectPr>` | ||
| ); | ||
| }); | ||
|
|
||
|
|
@@ -68,7 +148,7 @@ describe('Section header/footer references', () => { | |
| even: null, | ||
| odd: null, | ||
| }, | ||
| }, | ||
| } | ||
| ); | ||
| }); | ||
|
|
||
|
|
@@ -78,30 +158,30 @@ describe('Section titlePg', () => { | |
| </w:sectPr>`, | ||
| { | ||
| isTitlePage: false, | ||
| }, | ||
| } | ||
| ); | ||
| test( | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:titlePg /> | ||
| </w:sectPr>`, | ||
| { | ||
| isTitlePage: true, | ||
| }, | ||
| } | ||
| ); | ||
| test( | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:titlePg w:val="1" /> | ||
| </w:sectPr>`, | ||
| { | ||
| isTitlePage: true, | ||
| }, | ||
| } | ||
| ); | ||
| test( | ||
| `<w:sectPr ${ALL_NAMESPACE_DECLARATIONS}> | ||
| <w:titlePg w:val="0" /> | ||
| </w:sectPr>`, | ||
| { | ||
| isTitlePage: false, | ||
| }, | ||
| } | ||
| ); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,20 +4,55 @@ import { QNS } from '../utilities/namespaces.ts'; | |
| import { evaluateXPathToMap } from '../utilities/xquery.ts'; | ||
|
|
||
| /** | ||
| * All the formatting options that can be given on a text run (inline text). | ||
| * | ||
| * Serializes to the <w:rPr> element. | ||
| * https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_rPr_topic_ID0EIEKM.html | ||
| * Formatting options that are applied on the Section level. | ||
| * For more on how OOXML secitons are structured: http://officeopenxml.com/WPsection.php | ||
| */ | ||
|
|
||
| export type SectionProperties = { | ||
| /** | ||
| * The column layout for this section. | ||
| */ | ||
| columns?: { | ||
jogawebb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * The number of columns in a section of text. If this property is present it must | ||
| * be an integer greater than 0 and less than or equal to 45. | ||
| */ | ||
| numberOfColumns?: number; | ||
| /** | ||
| * Specifies whether all columns are of equal width. | ||
| */ | ||
| equalWidth?: boolean; | ||
| /** | ||
| * Specifies whether a vertical line is to be drawn between each column. If set to true, then the line is drawn in the center of the space between the columns. | ||
| */ | ||
| separator?: boolean; | ||
| /** | ||
| * The width of a column of text. If an array of columns is provided in the `columnDefs` | ||
| * property, then this value is ignored. | ||
| */ | ||
| columnSpace?: Length; | ||
| /** | ||
| * To create sections that use columns of different widths or uneven spacing, you can define | ||
| * columns as an array of objects. This is _only_ used by Word if the `equalWidth` | ||
| * property is not true. | ||
| */ | ||
| columnDefs?: { columnWidth: Length; columnSpace?: Length }[]; | ||
| }; | ||
|
|
||
| /** | ||
| * A reference to the header portion on every page in this section. | ||
| */ | ||
| headers?: null | string | { first?: string | null; even?: string | null; odd?: string | null }; | ||
| headers?: | ||
| | null | ||
| | string | ||
| | { first?: string | null; even?: string | null; odd?: string | null }; | ||
| /** | ||
| * A reference to the footer portion on every page in this section. | ||
| */ | ||
| footers?: null | string | { first?: string | null; even?: string | null; odd?: string | null }; | ||
| footers?: | ||
| | null | ||
| | string | ||
| | { first?: string | null; even?: string | null; odd?: string | null }; | ||
| /** | ||
| * The width of any page in this section. | ||
| */ | ||
|
|
@@ -44,18 +79,20 @@ export type SectionProperties = { | |
| footer?: null | Length; | ||
| gutter?: null | Length; | ||
| }; | ||
|
|
||
| /** | ||
| * Specifies whether sections in the document shall have different headers and footers for even and odd pages. | ||
| */ | ||
| isTitlePage?: null | boolean; | ||
| }; | ||
|
|
||
| export function sectionPropertiesFromNode(node?: Node | null): SectionProperties { | ||
| export function sectionPropertiesFromNode( | ||
| node?: Node | null | ||
| ): SectionProperties { | ||
| if (!node) { | ||
| return {}; | ||
| } | ||
| const data = evaluateXPathToMap<SectionProperties>( | ||
|
|
||
| return evaluateXPathToMap<SectionProperties>( | ||
| `map { | ||
| "headers": map { | ||
| "first": ./${QNS.w}headerReference[@${QNS.w}type = 'first']/@${QNS.r}id/string(), | ||
|
|
@@ -67,6 +104,18 @@ export function sectionPropertiesFromNode(node?: Node | null): SectionProperties | |
| "even": ./${QNS.w}footerReference[@${QNS.w}type = 'even']/@${QNS.r}id/string(), | ||
| "odd": ./${QNS.w}footerReference[@${QNS.w}type = 'default']/@${QNS.r}id/string() | ||
| }, | ||
| "columns": map { | ||
| "numberOfColumns": ./${QNS.w}cols/@${QNS.w}num/number(), | ||
| "equalWidth": docxml:st-on-off(./${QNS.w}cols/@${QNS.w}equalwidth), | ||
| "separator": if (exists(./${QNS.w}cols/@${QNS.w}sep)) then docxml:st-on-off(./${QNS.w}cols/@${QNS.w}sep) else (), | ||
| "columnSpace": docxml:length(./${QNS.w}cols/@${QNS.w}space, 'twip'), | ||
| "columnDefs": array{ | ||
| ./${QNS.w}cols/${QNS.w}col/map{ | ||
| "columnWidth": docxml:length(@${QNS.w}w, 'twip'), | ||
| "columnSpace": docxml:length(@${QNS.w}space, 'twip') | ||
| } | ||
| } | ||
| }, | ||
| "pageWidth": docxml:length(${QNS.w}pgSz/@${QNS.w}w, 'twip'), | ||
| "pageHeight": docxml:length(${QNS.w}pgSz/@${QNS.w}h, 'twip'), | ||
| "pageOrientation": ./${QNS.w}pgSz/@${QNS.w}orient/string(), | ||
|
|
@@ -81,10 +130,8 @@ export function sectionPropertiesFromNode(node?: Node | null): SectionProperties | |
| }, | ||
| "isTitlePage": exists(./${QNS.w}titlePg) and (not(./${QNS.w}titlePg/@${QNS.w}val) or docxml:st-on-off(./${QNS.w}titlePg/@${QNS.w}val)) | ||
| }`, | ||
| node, | ||
| node | ||
| ); | ||
|
|
||
| return data; | ||
| } | ||
|
|
||
| export function sectionPropertiesToNode(data: SectionProperties = {}): Node { | ||
|
|
@@ -114,6 +161,33 @@ export function sectionPropertiesToNode(data: SectionProperties = {}): Node { | |
| attribute ${QNS.r}id { $footers('odd') }, | ||
| attribute ${QNS.w}type { 'default' } | ||
| } else (), | ||
| if (exists($columns)) then element ${QNS.w}cols { | ||
| if (exists($columns('separator'))) then attribute ${QNS.w}sep { | ||
| $columns('separator') } | ||
| else (), | ||
| if (exists($columns('equalWidth'))) then attribute ${QNS.w}equalwidth { | ||
| $columns('equalWidth') | ||
| } else ( | ||
| if (count($columns('columnDefs')) > 1) | ||
| then ( | ||
| attribute ${QNS.w}equalwidth { false } | ||
| ) | ||
| else () | ||
| ), | ||
| if (exists($columns('numberOfColumns'))) then attribute ${QNS.w}num { | ||
| $columns('numberOfColumns') | ||
| } else (), | ||
| if (exists($columns('columnSpace'))) then attribute ${QNS.w}space { | ||
| round($columns('columnSpace')('twip')) | ||
| } else (), | ||
| if (docxml:st-on-off(string($columns('equalWidth')))) then () | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When there is no "columns" information, does the "equalWidth" attribute always exist? |
||
| else for $column in array:flatten($columns('columnDefs')) | ||
| return element ${QNS.w}col { | ||
| attribute ${QNS.w}w { round($column('columnWidth')('twip')) }, | ||
| if (not(exists($column('columnSpace')))) then () | ||
| else attribute ${QNS.w}space { round($column('columnSpace')('twip')) } | ||
| } | ||
| } else (), | ||
| if (exists($pageWidth) or exists($pageHeight) or $pageOrientation) then element ${QNS.w}pgSz { | ||
| if (exists($pageWidth)) then attribute ${QNS.w}w { | ||
| round($pageWidth('twip')) | ||
|
|
@@ -151,17 +225,26 @@ export function sectionPropertiesToNode(data: SectionProperties = {}): Node { | |
| { | ||
| headers: | ||
| typeof data.headers === 'string' | ||
| ? { first: data.headers, even: data.headers, odd: data.headers } | ||
| ? { | ||
| first: data.headers, | ||
| even: data.headers, | ||
| odd: data.headers, | ||
| } | ||
| : data.headers || {}, | ||
| footers: | ||
| typeof data.footers === 'string' | ||
| ? { first: data.footers, even: data.footers, odd: data.footers } | ||
| ? { | ||
| first: data.footers, | ||
| even: data.footers, | ||
| odd: data.footers, | ||
| } | ||
| : data.footers || {}, | ||
| columns: data.columns || {}, | ||
| pageWidth: data.pageWidth || null, | ||
| pageHeight: data.pageHeight || null, | ||
| pageMargin: data.pageMargin || null, | ||
| pageOrientation: data.pageOrientation || null, | ||
| isTitlePage: data.isTitlePage || null, | ||
| }, | ||
| } | ||
| ); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add more tests forcing incorrect scenarios? Maybe some missing properties? Extra/unknown properties?