diff --git a/ChangeLog.md b/ChangeLog.md
index f439e8b..b58edc6 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -2,6 +2,14 @@
## Versions ##
+### Unreleased ###
+
+* Adds bulleted/numbered list support - Example39.ps1 to Example44.ps1 (#105)
+* Adds custom list number formats (Word and Text plugins only)
+ * Adds `NumberStyle` keyword
+* Fixes bug in Text table output breaking Word and Html output (#126)
+* Fixes bug in call stack enumeration with nested functions containing `Section -Orientation` definitions (#121)
+
### 0.10.0 ###
* __BREAKING CHANGE__ - XML output to be deprecated in a future release (#102)
diff --git a/Examples/Example17.ps1 b/Examples/Example17.ps1
index 83186c1..a1b49d0 100644
--- a/Examples/Example17.ps1
+++ b/Examples/Example17.ps1
@@ -16,6 +16,8 @@ $example17 = Document -Name 'PScribo Example 17' {
NOTE: You must ensure that the hashtable keys are the same on all hashtables
in the collection/array as only the first object is enumerated.
+ NOTE: If using 'ColumnWidths' parameter in combination with the 'List' and 'Key' parameters, make sure the ColumnWidth accounts for the additional column generated by the Key. For example, looking at the hashtable below, 4 ColumnWidths values would be required.
+
The following example creates an table with a three rows from an array of
ordered hashtables.
#>
diff --git a/Examples/Example39.ps1 b/Examples/Example39.ps1
new file mode 100644
index 0000000..1414e4f
--- /dev/null
+++ b/Examples/Example39.ps1
@@ -0,0 +1,69 @@
+[CmdletBinding()]
+param (
+ [System.String[]] $Format = 'Html',
+ [System.String] $Path = '~\Desktop',
+ [System.Management.Automation.SwitchParameter] $PassThru
+)
+
+Import-Module PScribo -Force -Verbose:$false
+
+# 'List' single-level bullet lists
+$example39 = Document -Name 'PScribo Example 39' {
+
+ <#
+ A bulleted list is defined by the 'List' keyword and can contain one or more items.
+
+ A simple single-level list can be defined with the '-Item' parameter and passing an
+ array of strings ([string[]]).
+ #>
+ List -Item 'Apples','Oranges','Bananas'
+
+ <#
+ A list can also be created using a script block and nesting one or 'Item' keywords
+ within it.
+ #>
+ List {
+ Item 'Apples'
+ Item 'Bananas'
+ Item 'Oranges'
+ }
+
+ <#
+ Bullet styles can be applied to a list, e.g. 'Dash', 'Circle', 'Disc' and 'Square'. If
+ not specified, the bullet list defaults to the 'Disc' style.
+ #>
+ List -BulletStyle Square {
+ Item 'Apples'
+ Item 'Bananas'
+ Item 'Oranges'
+ }
+
+ <#
+ Formatting styles can be applied to all items in a list.
+ #>
+ List -Style Caption {
+ Item 'Apples'
+ Item 'Bananas'
+ Item 'Oranges'
+ }
+
+ <#
+ Styles can be applied to indiviual items in a list.
+ #>
+ List {
+ Item 'Apples'
+ Item 'Bananas' -Style Caption
+ Item 'Oranges'
+ }
+
+ <#
+ Inline styles can also be applied to indiviual items in a list.
+ #>
+ List {
+ Item 'Apples' -Bold
+ Item 'Bananas' -Italic
+ Item 'Oranges' -Color Firebrick
+ }
+
+}
+$example39 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru
diff --git a/Examples/Example40.ps1 b/Examples/Example40.ps1
new file mode 100644
index 0000000..18b1cfd
--- /dev/null
+++ b/Examples/Example40.ps1
@@ -0,0 +1,36 @@
+[CmdletBinding()]
+param (
+ [System.String[]] $Format = 'Html',
+ [System.String] $Path = '~\Desktop',
+ [System.Management.Automation.SwitchParameter] $PassThru
+)
+
+Import-Module PScribo -Force -Verbose:$false
+
+$example40 = Document -Name 'PScribo Example 40' {
+
+ <#
+ Numbered lists are supported and can be specified with the '-Numbered' parameter.
+ #>
+ List -Item 'Apples','Oranges','Bananas' -Numbered
+
+ <#
+ Multiple number styles are available: 'Number', 'Letter' and 'Roman'. If not specified,
+ a numbered list will default to the 'Number' style. You can specify the required number
+ format with the '-NumberStyle' parameter.
+ #>
+ List -Item 'Apples','Oranges','Bananas' -Numbered -NumberStyle Letter
+
+ <#
+ Multiple number styles are available: 'Number', 'Letter' and 'Roman'. If not specified,
+ a numbered list will default to the 'Number' style. You can specify the required number
+ format with the '-NumberStyle' parameter.
+ #>
+ List -Numbered -NumberStyle Roman {
+ Item 'Apples'
+ Item 'Bananas'
+ Item 'Oranges'
+ }
+
+}
+$example40 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru
diff --git a/Examples/Example41.ps1 b/Examples/Example41.ps1
new file mode 100644
index 0000000..9745f31
--- /dev/null
+++ b/Examples/Example41.ps1
@@ -0,0 +1,60 @@
+[CmdletBinding()]
+param (
+ [System.String[]] $Format = 'Html',
+ [System.String] $Path = '~\Desktop',
+ [System.Management.Automation.SwitchParameter] $PassThru
+)
+
+Import-Module PScribo -Force -Verbose:$false
+
+$example41 = Document -Name 'PScribo Example 41' {
+
+ <#
+ Multi-level bullet lists can be created by nesting one or more 'List' keywords.
+
+ NOTE: There must be an 'Item' keyword defined before a nested 'List' can be used.
+ #>
+ List {
+ Item 'Apples'
+ List {
+ Item 'Jazz'
+ Item 'Granny smith'
+ Item 'Pink lady'
+ }
+ Item 'Bananas'
+ Item 'Oranges'
+ List {
+ Item 'Jaffa'
+ Item 'Tangerine'
+ Item 'Clementine'
+ }
+ }
+
+ <#
+ Each 'List' can have its own bullet style defined.
+
+ NOTE: Word does not support a mixture of bullet/number formats at the same level within a
+ list. Therefore, only the first list type will be rendered at each level - in this
+ example the 'Disc' style will be used.
+
+ NOTE: Html output does not support the 'Dash' bullet style. Dashes will be rendered using
+ the the web broswer's defaults.
+ #>
+ List -BulletStyle Square {
+ Item 'Apples'
+ List -BulletStyle Disc {
+ Item 'Jazz'
+ Item 'Granny smith'
+ Item 'Pink lady'
+ }
+ Item 'Bananas'
+ Item 'Oranges'
+ List -BulletStyle Dash {
+ Item 'Jaffa'
+ Item 'Tangerine'
+ Item 'Clementine'
+ }
+ }
+
+}
+$example41 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru
diff --git a/Examples/Example42.ps1 b/Examples/Example42.ps1
new file mode 100644
index 0000000..f7da3ac
--- /dev/null
+++ b/Examples/Example42.ps1
@@ -0,0 +1,59 @@
+[CmdletBinding()]
+param (
+ [System.String[]] $Format = 'Html',
+ [System.String] $Path = '~\Desktop',
+ [System.Management.Automation.SwitchParameter] $PassThru
+)
+
+Import-Module PScribo -Force -Verbose:$false
+
+$example42 = Document -Name 'PScribo Example 42' {
+
+ <#
+ Multi-level numbered lists can be created by nesting one or more 'List' keywords in
+ combination with the '-Numbered' parameter.
+
+ NOTE: A 'List' defaults to a bulleted list by default, so the '-Numbered' switch needs
+ to be specified at each level - where required.
+ #>
+ List -Numbered {
+ Item 'Apples'
+ List -Numbered {
+ Item 'Jazz'
+ Item 'Granny smith'
+ Item 'Pink lady'
+ }
+ Item 'Bananas'
+ Item 'Oranges'
+ List -Numbered {
+ Item 'Jaffa'
+ Item 'Tangerine'
+ Item 'Clementine'
+ }
+ }
+
+ <#
+ Like bullet lists, each 'List' can have its own number style defined.
+
+ NOTE: Word does not support a mixture of bullet/number formats at the same level within a
+ list. Therefore, only the first list type will be rendered at each level - in this
+ example the 'Letter' style will be used for the second nested numbered list.
+ #>
+ List -Numbered {
+ Item 'Apples'
+ List -Numbered -NumberStyle Letter {
+ Item 'Jazz'
+ Item 'Granny smith'
+ Item 'Pink lady'
+ }
+ Item 'Bananas'
+ Item 'Oranges'
+ List -Numbered -NumberStyle Roman {
+ Item 'Jaffa'
+ Item 'Tangerine'
+ Item 'Clementine'
+ }
+ }
+
+}
+$example42 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru
diff --git a/Examples/Example43.ps1 b/Examples/Example43.ps1
new file mode 100644
index 0000000..f37a76e
--- /dev/null
+++ b/Examples/Example43.ps1
@@ -0,0 +1,83 @@
+[CmdletBinding()]
+param (
+ [System.String[]] $Format = 'Word',
+ [System.String] $Path = '~\Desktop',
+ [System.Management.Automation.SwitchParameter] $PassThru
+)
+
+Import-Module PScribo -Force -Verbose:$false
+
+$example43 = Document -Name 'PScribo Example 43' {
+
+ <#
+ PScribo provides 3 built-in number styles that replicate the standard Html options and
+ the built-in Microsoft Word defaults; 'Number', 'Letter' and 'Roman'. The default number
+ styles display the number in lowercase, right-aligned and terminated with a period '.'.
+
+ It is possible to define your own number styles or override the built-in styles. This
+ provides options to change the casing and/or alignment of the list numbers.
+
+ NOTE: Html numbered lists only support the default '.' number style terminator/suffix. The
+ use of custom number style terminators/suffixes is not supported.
+
+ NOTE: Html numbered/unordered lists do not support alignment.
+
+ For example, to override the built-in number styles ensuring that they are rendered in
+ uppercase and terminated with a parenthesis:
+ #>
+ NumberStyle -Id 'Number' -Format Number -Uppercase -Suffix ')'
+ NumberStyle -Id 'Letter' -Format Letter -Uppercase -Suffix ')'
+ NumberStyle -Id 'Roman' -Format Roman -Uppercase -Suffix ')'
+
+ <#
+ To align the number to the left margin, override or define your own number style with the
+ '-Align' parameter.
+
+ NOTE: The default number style has been changed so we define a 'RightRoman' to mimic the
+ built-in/default settings (without redefining 'Roman' again!).
+ #>
+ NumberStyle -Id 'LeftRoman' -Format Roman -Align Left
+ NumberStyle -Id 'RightRoman' -Format Roman -Align Right
+
+ <#
+ Output right aligned (the default) lists for comparison
+ #>
+ List -Numbered -NumberStyle RightRoman {
+ Item 'Apples'
+ List -Numbered -NumberStyle RightRoman {
+ Item 'Jazz'
+ Item 'Granny smith'
+ Item 'Pink lady'
+ }
+ Item 'Bananas'
+ Item 'Oranges'
+ List -Numbered -NumberStyle RightRoman {
+ Item 'Jaffa'
+ Item 'Tangerine'
+ Item 'Clementine'
+ }
+ }
+
+ <#
+ Output left aligned lists for comparison
+
+ NOTE: Html numbered lists only support the default '.' number style terminator/suffix. The
+ use of custom number style terminators/suffixes is not supported.
+ #>
+ List -Numbered -NumberStyle LeftRoman {
+ Item 'Apples'
+ List -Numbered -NumberStyle LeftRoman {
+ Item 'Jazz'
+ Item 'Granny smith'
+ Item 'Pink lady'
+ }
+ Item 'Bananas'
+ Item 'Oranges'
+ List -Numbered -NumberStyle LeftRoman {
+ Item 'Jaffa'
+ Item 'Tangerine'
+ Item 'Clementine'
+ }
+ }
+}
+$example43 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru
diff --git a/Examples/Example44.ps1 b/Examples/Example44.ps1
new file mode 100644
index 0000000..22d4a2a
--- /dev/null
+++ b/Examples/Example44.ps1
@@ -0,0 +1,34 @@
+[CmdletBinding()]
+param (
+ [System.String[]] $Format = 'Word',
+ [System.String] $Path = '~\Desktop',
+ [System.Management.Automation.SwitchParameter] $PassThru
+)
+
+Import-Module PScribo -Force -Verbose:$false
+
+$example44 = Document -Name 'PScribo Example 44' {
+
+ <#
+ Custom numbered lists are registered with the 'NumberStyle' keyword, but only the Word and
+ Text plugins are supported. All other plugins will render the number as a decimal (using the
+ 'Number' format).
+
+ Custom number lists can contain any wording and punctuation you require.
+
+ NOTE: The '-Uppercase' and '-Suffix' parameters are ignored so you need to include any suffix
+ in the number format definition.
+
+ The '%' token is used to denote where the number will be placed. To include leading zeroes,
+ use multiple '%' tokens, e.g. 'ab%%' for ab01, ab02 and 'XYZ-%%%' for XYZ-001, XYZ-002, etc..
+ #>
+ NumberStyle -Id 'CustomNumberStyle' -Custom 'xYz-%%%.' -Indent 1500 -Hanging 200 -Align Left
+
+ <#
+ Output list using the 'Custom' number style
+ #>
+
+ List -Numbered -NumberStyle CustomNumberStyle -Item 'Apples','Bananas','Oranges'
+
+}
+$example44 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru
diff --git a/PScribo.psd1 b/PScribo.psd1
index b42cca6..03a52bc 100644
--- a/PScribo.psd1
+++ b/PScribo.psd1
@@ -15,7 +15,10 @@
'Footer',
'Header',
'Image',
+ 'Item',
'LineBreak',
+ 'List',
+ 'NumberStyle',
'PageBreak',
'Paragraph',
'Section',
@@ -25,7 +28,8 @@
'TableStyle',
'Text',
'TOC',
- 'Write-PScriboMessage'
+ 'Write-PScriboMessage',
+ 'Get-WordListLevel'
)
AliasesToExport = @(
'GlobalOption'
diff --git a/Src/Plugins/Html/Get-HtmlListItemInlineStyle.ps1 b/Src/Plugins/Html/Get-HtmlListItemInlineStyle.ps1
new file mode 100644
index 0000000..dd764fb
--- /dev/null
+++ b/Src/Plugins/Html/Get-HtmlListItemInlineStyle.ps1
@@ -0,0 +1,55 @@
+function Get-HtmlListItemInlineStyle
+{
+<#
+ .SYNOPSIS
+ Generates inline Html style attribute from PScribo list Item style overrides.
+#>
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [ValidateNotNull()]
+ [System.Management.Automation.PSObject] $Item
+ )
+ process
+ {
+ $itemStyleBuilder = New-Object -TypeName System.Text.StringBuilder
+
+ if ($Item.Font)
+ {
+ $fontList = $List.Font -Join "','"
+ [ref] $null = $itemStyleBuilder.AppendFormat(" font-family: '{0}';", $fontList)
+ }
+
+ if ($Item.Size -gt 0)
+ {
+ ## Create culture invariant decimal https://github.com/iainbrighton/PScribo/issues/6
+ $invariantItemSize = ConvertTo-InvariantCultureString -Object ($Item.Size / 12) -Format 'f2'
+ [ref] $null = $itemStyleBuilder.AppendFormat(' font-size: {0}rem;', $invariantItemSize)
+ }
+
+ if ($Item.Bold -eq $true)
+ {
+ [ref] $null = $itemStyleBuilder.Append(' font-weight: bold;')
+ }
+
+ if ($Item.Italic -eq $true)
+ {
+ [ref] $null = $itemStyleBuilder.Append(' font-style: italic;')
+ }
+
+ if ($Item.Underline -eq $true)
+ {
+ [ref] $null = $itemStyleBuilder.Append(' text-decoration: underline;')
+ }
+
+ if (-not [System.String]::IsNullOrEmpty($Item.Color))
+ {
+ $color = Resolve-PScriboStyleColor -Color $Item.Color
+ [ref] $null = $itemStyleBuilder.AppendFormat(' color: #{0};', $color)
+ }
+
+ return $itemStyleBuilder.ToString().TrimStart()
+ }
+}
diff --git a/Src/Plugins/Html/Out-HtmlDocument.ps1 b/Src/Plugins/Html/Out-HtmlDocument.ps1
index 612f976..7a843ab 100644
--- a/Src/Plugins/Html/Out-HtmlDocument.ps1
+++ b/Src/Plugins/Html/Out-HtmlDocument.ps1
@@ -102,6 +102,11 @@ function Out-HtmlDocument
{
[ref] $null = $htmlBuilder.Append((Out-HtmlImage -Image $subSection))
}
+ 'PScribo.ListReference'
+ {
+ $htmlList = Out-HtmlList -List $Document.Lists[$subSection.Number -1]
+ [ref] $null = $htmlBuilder.Append($htmlList).AppendLine()
+ }
Default
{
Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
diff --git a/Src/Plugins/Html/Out-HtmlList.ps1 b/Src/Plugins/Html/Out-HtmlList.ps1
new file mode 100644
index 0000000..17c67f7
--- /dev/null
+++ b/Src/Plugins/Html/Out-HtmlList.ps1
@@ -0,0 +1,187 @@
+function Out-HtmlList
+{
+<#
+ .SYNOPSIS
+ Output formatted Html list.
+#>
+ [CmdletBinding()]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','Options')]
+ param
+ (
+ ## List to output
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $List,
+
+ ## List indent
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Int32] $Indent,
+
+ ## Number style
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $NumberStyle,
+
+ ## Bullet style
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $BulletStyle
+ )
+ begin
+ {
+ ## Fix Set-StrictMode
+ if (-not (Test-Path -Path Variable:\Options))
+ {
+ $options = New-PScriboHtmlOption
+ }
+ }
+ process
+ {
+ $leader = ''.PadRight($Indent, ' ')
+ $listBuilder = New-Object -TypeName System.Text.StringBuilder
+
+ if ($List.IsNumbered)
+ {
+ if ($List.HasNumberStyle)
+ {
+ $NumberStyle = $List.NumberStyle
+ }
+ elseif (-not $PSBoundParameters.ContainsKey('NumberStyle'))
+ {
+ $NumberStyle = $Document.DefaultNumberStyle
+ }
+ $style = $Document.NumberStyles[$NumberStyle]
+
+ $outHtmlListParams = @{
+ NumberStyle = $NumberStyle
+ }
+
+ switch ($style.Format)
+ {
+ 'Number'
+ {
+ $inlineStyle = 'decimal'
+ }
+ 'Letter'
+ {
+ if ($style.Uppercase)
+ {
+ $inlineStyle = 'upper-alpha'
+ }
+ else
+ {
+ $inlineStyle = 'lower-alpha'
+ }
+ }
+ 'Roman'
+ {
+ if ($style.Uppercase)
+ {
+ $inlineStyle = 'upper-roman'
+ }
+ else
+ {
+ $inlineStyle = 'lower-roman'
+ }
+ }
+ 'Custom'
+ {
+ $inlineStyle = 'decimal'
+ }
+ }
+
+ if ($List.HasStyle)
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0}
', $leader, $List.Style, $inlineStyle)
+ }
+ else
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0}', $leader, $inlineStyle)
+ }
+ [ref] $null = $listBuilder.AppendLine()
+ }
+ else
+ {
+ if ($List.HasBulletStyle)
+ {
+ $BulletStyle = $List.BulletStyle
+ }
+
+ switch ($BulletStyle)
+ {
+ Circle
+ {
+ $inlineStyle = ' style="list-style-type:circle;"'
+ }
+ Disc
+ {
+ $inlineStyle = ' style="list-style-type:disc;"'
+ }
+ Square
+ {
+ $inlineStyle = ' style="list-style-type:square;"'
+ }
+ Default
+ {
+ ## Dash style is not supported in Html so default to the browser's rendering engine
+ $inlineStyle = ''
+ }
+ }
+
+ $outHtmlListParams = @{
+ BulletStyle = $BulletStyle
+ }
+
+ if ($List.HasStyle)
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0}', $leader, $List.Style, $inlineStyle)
+ }
+ else
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0}', $leader, $inlineStyle)
+ }
+ [ref] $null = $listBuilder.AppendLine()
+ }
+
+ $leader = ''.PadRight($Indent, ' ')
+ foreach ($item in $List.Items)
+ {
+ if ($item.Type -eq 'PScribo.Item')
+ {
+ if (($item.HasStyle -eq $true) -and ($item.HasInlineStyle -eq $true))
+ {
+ $inlineStyle = Get-HtmlListItemInlineStyle -Item $item
+ [ref] $null = $listBuilder.AppendFormat('{0}- {3}
', $leader, $item.Style, $inlineStyle, $item.Text)
+ }
+ elseif ($item.HasStyle)
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0} - {2}
', $leader, $item.Style, $item.Text)
+ }
+ elseif ($item.HasInlineStyle)
+ {
+ $inlineStyle = Get-HtmlListItemInlineStyle -Item $item
+ [ref] $null = $listBuilder.AppendFormat('{0} - {2}
', $leader, $inlineStyle, $item.Text)
+ }
+ else
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0} - {1}
', $leader, $item.Text)
+ }
+ [ref] $null = $listBuilder.AppendLine()
+ }
+ else
+ {
+ $nestedList = Out-HtmlList -List $item -Indent ($Indent +2) @outHtmlListParams
+ [ref] $null = $listBuilder.AppendLine($nestedList)
+ }
+ }
+
+ if ($List.IsNumbered)
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0}
', $leader)
+ }
+ else
+ {
+ [ref] $null = $listBuilder.AppendFormat('{0}', $leader)
+ }
+ [ref] $null = $listBuilder.AppendLine()
+
+ return $listBuilder.ToString()
+ }
+}
diff --git a/Src/Plugins/Html/Out-HtmlSection.ps1 b/Src/Plugins/Html/Out-HtmlSection.ps1
index 02d52ab..a995356 100644
--- a/Src/Plugins/Html/Out-HtmlSection.ps1
+++ b/Src/Plugins/Html/Out-HtmlSection.ps1
@@ -95,9 +95,14 @@ function Out-HtmlSection
{
[ref] $null = $sectionBuilder.Append((Out-HtmlImage -Image $subSection))
}
+ 'PScribo.ListReference'
+ {
+ $htmlList = Out-HtmlList -List $Document.Lists[$subSection.Number -1]
+ [ref] $null = $sectionBuilder.Append($htmlList).AppendLine()
+ }
Default
{
- Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
+ Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
}
}
}
diff --git a/Src/Plugins/Text/Get-PScriboListItemMaximumLength.ps1 b/Src/Plugins/Text/Get-PScriboListItemMaximumLength.ps1
new file mode 100644
index 0000000..dc2f828
--- /dev/null
+++ b/Src/Plugins/Text/Get-PScriboListItemMaximumLength.ps1
@@ -0,0 +1,28 @@
+function Get-PScriboListItemMaximumLength
+{
+<#
+ .SYNOPSIS
+ Renders a List's item numbers to determine the maximum string/render width.
+#>
+ [CmdletBinding()]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','NumberStyle')]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $List,
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
+ [System.Management.Automation.PSObject] $NumberStyle
+ )
+ process
+ {
+ $List.Items |
+ Where-Object { $_.Type -eq 'PScribo.Item' } |
+ ForEach-Object {
+ $number = ConvertFrom-NumberStyle -Value $_.Index -NumberStyle $numberStyle
+ Write-Output -InputObject $number.Length
+ } |
+ Measure-Object -Maximum |
+ Select-Object -ExpandProperty Maximum
+ }
+}
diff --git a/Src/Plugins/Text/Out-TextDocument.ps1 b/Src/Plugins/Text/Out-TextDocument.ps1
index c4d82bf..bb6e063 100644
--- a/Src/Plugins/Text/Out-TextDocument.ps1
+++ b/Src/Plugins/Text/Out-TextDocument.ps1
@@ -87,6 +87,11 @@ function Out-TextDocument
{
[ref] $null = $textBuilder.Append((Out-TextImage -Image $subSection))
}
+ 'PScribo.ListReference'
+ {
+ $textList = Out-TextList -List $Document.Lists[$subSection.Number -1]
+ [ref] $null = $textBuilder.Append($textList)
+ }
Default
{
Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
diff --git a/Src/Plugins/Text/Out-TextList.ps1 b/Src/Plugins/Text/Out-TextList.ps1
new file mode 100644
index 0000000..e408711
--- /dev/null
+++ b/Src/Plugins/Text/Out-TextList.ps1
@@ -0,0 +1,134 @@
+function Out-TextList
+{
+<#
+ .SYNOPSIS
+ Output formatted text list.
+#>
+ [CmdletBinding()]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','NumberStyle')]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','Options')]
+ param
+ (
+ ## Section to output
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $List,
+
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Int32] $Indent = 2,
+
+ ## Number style
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $NumberStyle,
+
+ ## Bullet style
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $BulletStyle
+ )
+ begin
+ {
+ ## Fix Set-StrictMode
+ if (-not (Test-Path -Path Variable:\Options))
+ {
+ $options = New-PScriboTextOption
+ }
+ }
+ process
+ {
+ $listBuilder = New-Object -TypeName System.Text.StringBuilder
+ $leader = ''.PadRight($Indent, ' ')
+
+ if ($List.IsNumbered)
+ {
+ if ($List.HasNumberStyle)
+ {
+ $NumberStyle = $List.NumberStyle
+ }
+ elseif (-not $PSBoundParameters.ContainsKey('Style'))
+ {
+ $NumberStyle = $Document.DefaultNumberStyle
+ }
+ $style = $Document.NumberStyles[$NumberStyle]
+
+ $maxItemNumberLength = Get-PScriboListItemMaximumLength -List $List -NumberStyle $style
+
+ $outTextListParams = @{
+ NumberStyle = $NumberStyle
+ }
+ }
+ else
+ {
+ if ($List.HasBulletStyle)
+ {
+ $BulletStyle = $List.BulletStyle
+ }
+
+ switch ($BulletStyle)
+ {
+ Circle
+ {
+ $numberString = 'o'
+ break
+ }
+ Dash
+ {
+ $numberString = '-'
+ break
+ }
+ Disc
+ {
+ $numberString = '*'
+ break
+ }
+ Default
+ {
+ ## Square style is not supported in Text so default to the browser's rendering engine
+ $numberString = '*'
+ }
+ }
+
+ $outTextListParams = @{
+ BulletStyle = $BulletStyle
+ }
+ }
+
+ foreach ($item in $List.Items)
+ {
+ if ($item.Type -eq 'PScribo.Item')
+ {
+ if ($List.IsNumbered)
+ {
+ $padding = ''
+ $itemNumber = ConvertFrom-NumberStyle -Value $item.Index -NumberStyle $style
+ $paddingLength = ($maxItemNumberLength) - $itemNumber.Length
+ if ($paddingLength -gt 0)
+ {
+ $padding = ''.PadRight($paddingLength, ' ')
+ }
+
+ if ($style.Align -eq 'Left')
+ {
+ $numberString = '{0}{1}' -f $itemNumber, $padding
+ }
+ elseif ($style.Align -eq 'Right')
+ {
+ $numberString = '{0}{1}' -f $padding, $itemNumber
+ }
+ }
+
+ [ref] $null = $listBuilder.AppendFormat('{0}{1} {2}', $leader, $numberString, $item.Text).AppendLine()
+ }
+ else
+ {
+ $newIndent = $Indent + 2
+ if ($List.IsNumbered)
+ {
+ $newIndent = $Indent + $maxItemNumberLength +1
+ }
+ $nestedList = Out-TextList -List $item -Indent $newIndent @outTextListParams
+ [ref] $null = $listBuilder.Append($nestedList)
+ }
+ }
+ [ref] $null = $listBuilder.AppendLine()
+ return $listBuilder.ToString()
+ }
+}
diff --git a/Src/Plugins/Text/Out-TextSection.ps1 b/Src/Plugins/Text/Out-TextSection.ps1
index 689baf3..6829a1e 100644
--- a/Src/Plugins/Text/Out-TextSection.ps1
+++ b/Src/Plugins/Text/Out-TextSection.ps1
@@ -76,6 +76,11 @@ function Out-TextSection
{
[ref] $null = $sectionBuilder.Append((Out-TextImage -Image $subSection))
}
+ 'PScribo.ListReference'
+ {
+ $textList = Out-TextList -List $Document.Lists[$subSection.Number -1]
+ [ref] $null = $sectionBuilder.Append($textList)
+ }
Default
{
Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
diff --git a/Src/Plugins/Text/Out-TextTable.ps1 b/Src/Plugins/Text/Out-TextTable.ps1
index 87342de..7b6c368 100644
--- a/Src/Plugins/Text/Out-TextTable.ps1
+++ b/Src/Plugins/Text/Out-TextTable.ps1
@@ -25,8 +25,12 @@ function Out-TextTable
$tableBuilder = New-Object -TypeName System.Text.StringBuilder
$tableRenderWidth = $options.TextWidth - ($Table.Tabs * 4)
- ## We need to replace page numbers before outputting the table
- foreach ($row in $Table.Rows)
+ ## We need to rewrite arrays for text formatting and we don't want to
+ ## alter the source documnent (#126)
+ $cloneTable = Copy-Object -InputObject $Table
+
+ ## We need to flatten arrays and replace page numbers before outputting the table
+ foreach ($row in $cloneTable.Rows)
{
foreach ($property in $row.PSObject.Properties)
{
@@ -43,14 +47,14 @@ function Out-TextTable
if ($Table.IsKeyedList)
{
## Create new objects with headings as properties
- $tableText = (ConvertTo-PSObjectKeyedListTable -Table $Table |
+ $tableText = (ConvertTo-PSObjectKeyedListTable -Table $cloneTable |
Select-Object -Property * -ExcludeProperty '*__Style' |
- Format-Table -Wrap -AutoSize |
- Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine)
+ Format-Table -Wrap -AutoSize |
+ Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine)
}
elseif ($Table.IsList)
{
- $tableText = ($Table.Rows |
+ $tableText = ($cloneTable.Rows |
Select-Object -Property * -ExcludeProperty '*__Style' |
Format-List | Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine)
}
@@ -58,7 +62,7 @@ function Out-TextTable
{
## Don't trim tabs for table headers
## Tables set to AutoSize as otherwise rendering is different between PoSh v4 and v5
- $tableText = ($Table.Rows |
+ $tableText = ($cloneTable.Rows |
Select-Object -Property * -ExcludeProperty '*__Style' |
Format-Table -Wrap -AutoSize |
Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine)
diff --git a/Src/Plugins/Word/Get-WordDocument.ps1 b/Src/Plugins/Word/Get-WordDocument.ps1
index 7973ef2..1b88cba 100644
--- a/Src/Plugins/Word/Get-WordDocument.ps1
+++ b/Src/Plugins/Word/Get-WordDocument.ps1
@@ -21,6 +21,8 @@ function Get-WordDocument
$xmlnsrelationships = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
$xmlnsofficeword14 = 'http://schemas.microsoft.com/office/drawing/2010/main'
$xmlnsmath = 'http://schemas.openxmlformats.org/officeDocument/2006/math'
+ $xmlnsmc = 'http://schemas.openxmlformats.org/markup-compatibility/2006' # required for custom number lists
+ $xmlnsw14 = 'http://schemas.microsoft.com/office/word/2010/wordml' # required for custom number lists
$xmlDocument = New-Object -TypeName 'System.Xml.XmlDocument'
[ref] $null = $xmlDocument.AppendChild($xmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes'))
$documentXml = $xmlDocument.AppendChild($xmlDocument.CreateElement('w', 'document', $xmlns))
@@ -31,6 +33,8 @@ function Get-WordDocument
[ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:r', $xmlnsrelationships)
[ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:m', $xmlnsmath)
[ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:a14', $xmlnsofficeword14)
+ [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:mc', $xmlnsmc)
+ [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:w14', $xmlnsw14)
$body = $documentXml.AppendChild($xmlDocument.CreateElement('w', 'body', $xmlns))
$script:pscriboIsFirstSection = $false
@@ -78,6 +82,10 @@ function Get-WordDocument
{
Out-WordBlankLine -BlankLine $subSection -XmlDocument $xmlDocument -Element $body
}
+ 'PScribo.ListReference'
+ {
+ Out-WordList -List $Document.Lists[$subSection.Number -1] -Element $body -XmlDocument $xmlDocument -NumberId $subSection.Number
+ }
Default
{
Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
diff --git a/Src/Plugins/Word/Get-WordListLevel.ps1 b/Src/Plugins/Word/Get-WordListLevel.ps1
new file mode 100644
index 0000000..747c68e
--- /dev/null
+++ b/Src/Plugins/Word/Get-WordListLevel.ps1
@@ -0,0 +1,39 @@
+function Get-WordListLevel
+{
+<#
+ .SYNOPSIS
+ Process (nested) lists and build index of number/bullet styles.
+#>
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $List,
+
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Collections.Hashtable] $Levels = @{ }
+ )
+ process
+ {
+ foreach ($item in $List.Items)
+ {
+ if ($item.Type -eq 'PScribo.Item')
+ {
+ $level = $item.Level -1
+ if (-not $Levels.ContainsKey($level))
+ {
+ $Levels[$level] = @{
+ IsNumbered = $List.IsNumbered
+ NumberStyle = $List.NumberStyle
+ BulletStyle = $List.BulletStyle
+ }
+ }
+ }
+ elseif ($item.Type -eq 'PScribo.List')
+ {
+ $Levels = Get-WordListLevel -List $item -Levels $Levels
+ }
+ }
+ return $Levels
+ }
+}
diff --git a/Src/Plugins/Word/Get-WordNumberStyle.ps1 b/Src/Plugins/Word/Get-WordNumberStyle.ps1
new file mode 100644
index 0000000..6248262
--- /dev/null
+++ b/Src/Plugins/Word/Get-WordNumberStyle.ps1
@@ -0,0 +1,210 @@
+function Get-WordNumberStyle
+{
+<#
+ .SYNOPSIS
+ Outputs Office Open XML numbering document
+#>
+ [CmdletBinding()]
+ [OutputType([System.Xml.XmlElement])]
+ param
+ (
+ ## PScribo document styles
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $List,
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
+ [System.Xml.XmlDocument] $XmlDocument
+ )
+ begin
+ {
+ $bulletMatrix = @{
+ Disc = [PSCustomObject] @{ Text = ''; Font = 'Symbol'; }
+ Circle = [PSCustomObject] @{ Text = 'o'; Font = 'Courier New'; }
+ Square = [PSCustomObject] @{ Text = ''; Font = 'Wingdings'; }
+ Dash = [PSCustomObject] @{ Text = '-'; Font = 'Courier New' }
+ }
+ }
+ process
+ {
+ ## Create the Numbering.xml document
+ $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
+ $xmlnsmc = 'http://schemas.openxmlformats.org/markup-compatibility/2006'
+
+ $abstractNum = $xmlDocument.CreateElement('w', 'abstractNum', $xmlns)
+ [ref] $null = $abstractNum.SetAttribute('abstractNumId', $xmlns, $list.Number -1)
+ $multiLevelType = $abstractNum.AppendChild($xmlDocument.CreateElement('w', 'multiLevelType', $xmlns))
+ [ref] $null = $multiLevelType.SetAttribute('val', $xmlns, 'hybridMultilevel')
+
+ $listLevels = Get-WordListLevel -List $List
+ foreach ($level in ($listLevels.Keys | Sort-Object))
+ {
+ $listLevel = $listLevels[$level]
+ if ($listLevel.IsNumbered)
+ {
+ $numberStyle = $Document.NumberStyles[$listLevel.NumberStyle]
+ }
+
+ $lvl = $abstractNum.AppendChild($xmlDocument.CreateElement('w', 'lvl', $xmlns))
+ [ref] $null = $lvl.SetAttribute('ilvl', $xmlns, $level)
+ $start = $lvl.AppendChild($xmlDocument.CreateElement('w', 'start', $xmlns))
+ [ref] $null = $start.SetAttribute('val', $xmlns, 1)
+
+ if ($listLevel.IsNumbered)
+ {
+ switch ($numberStyle.Format)
+ {
+ Number
+ {
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'decimal')
+ [ref] $null = $lvlText.SetAttribute('val', $xmlns, ('%{0}{1}' -f ($level +1), $numberStyle.Suffix))
+ }
+ Letter
+ {
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ if ($numberStyle.Uppercase)
+ {
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'upperLetter')
+ }
+ else
+ {
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'lowerLetter')
+ }
+ [ref] $null = $lvlText.SetAttribute('val', $xmlns, ('%{0}{1}' -f ($level +1), $numberStyle.Suffix))
+ }
+ Roman
+ {
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ if ($numberStyle.Uppercase)
+ {
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'upperRoman')
+ }
+ else
+ {
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'lowerRoman')
+ }
+ [ref] $null = $lvlText.SetAttribute('val', $xmlns, ('%{0}{1}' -f ($level +1), $numberStyle.Suffix))
+ }
+ Custom
+ {
+ $regexMatch = [Regex]::Match($numberStyle.Custom, '%+')
+ if ($regexMatch.Success -eq $true)
+ {
+ $numberString = $regexMatch.Value
+ if ($numberString.Length -eq 1)
+ {
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'decimal')
+ }
+ elseif ($numberString.Length -eq 2)
+ {
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'decimalZero')
+ }
+ else
+ {
+ $AlternateContent = $lvl.AppendChild($xmlDocument.CreateElement('mc', 'AlternateContent', $xmlnsmc))
+ $Choice = $AlternateContent.AppendChild($xmlDocument.CreateElement('mc', 'Choice', $xmlnsmc))
+ [ref] $null = $Choice.SetAttribute('Requires', 'w14')
+ $numFmtC = $Choice.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmtC.SetAttribute('val', $xmlns, 'custom')
+
+ if ($numberString.Length -eq 3)
+ {
+ [ref] $null = $numFmtC.SetAttribute('format', $xmlns, '001, 002, 003, ...')
+ }
+ elseif ($numberString.Length -eq 4)
+ {
+ [ref] $null = $numFmtC.SetAttribute('format', $xmlns, '0001, 0002, 0003, ...')
+ }
+ elseif ($numberString.Length -ge 5)
+ {
+ [ref] $null = $numFmtC.SetAttribute('format', $xmlns, '00001, 00002, 00003, ...')
+ }
+
+ $Fallback = $AlternateContent.AppendChild($xmlDocument.CreateElement('mc', 'Fallback', $xmlnsmc))
+ $numFmtF = $Fallback.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmtF.SetAttribute('val', $xmlns, 'decimal')
+
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ }
+ }
+ else
+ {
+ Write-PScriboMessage 'Invalid custom number format' -IsWarning
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'none')
+ }
+ $replacementString = '%{0}' -f ($level +1)
+ $wordNumberString = $numberStyle.Custom -replace '%+', $replacementString
+ [ref] $null = $lvlText.SetAttribute('val', $xmlns, $wordNumberString)
+ }
+ }
+ }
+ else
+ {
+ $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns))
+ [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'bullet')
+ $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns))
+ [ref] $null = $lvlText.SetAttribute('val', $xmlns, $bulletMatrix[$listLevel.BulletStyle].Text)
+ $rPr = $lvl.AppendChild($xmlDocument.CreateElement('w', 'rPr', $xmlns))
+ $rFonts = $rPr.AppendChild($xmlDocument.CreateElement('w', 'rFonts', $xmlns))
+ [ref] $null = $rFonts.SetAttribute('ascii', $xmlns, $bulletMatrix[$listLevel.BulletStyle].Font)
+ [ref] $null = $rFonts.SetAttribute('hAnsi', $xmlns, $bulletMatrix[$listLevel.BulletStyle].Font)
+ [ref] $null = $rFonts.SetAttribute('hint', $xmlns, 'default')
+ }
+
+ $lvlJc = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlJc', $xmlns))
+
+ if ($listLevel.IsNumbered)
+ {
+ $indent = $numberStyle.Indent
+ $hanging = $numberStyle.Hanging
+
+ if ($numberStyle.Align -eq 'Left')
+ {
+ [ref] $null = $lvlJc.SetAttribute('val', $xmlns,'start')
+ if ($indent -eq 0)
+ {
+ $indent = ($level + 1) * 680
+ }
+ if ($hanging -eq 0)
+ {
+ $hanging = 400
+ }
+ }
+ elseif ($numberStyle.Align -eq 'Right')
+ {
+ [ref] $null = $lvlJc.SetAttribute('val', $xmlns, 'end')
+ if ($indent -eq 0)
+ {
+ $indent = ($level + 1) * 680
+ }
+ if ($hanging -eq 0)
+ {
+ $hanging = 120
+ }
+ }
+ }
+ else
+ {
+ [ref] $null = $lvlJc.SetAttribute('val', $xmlns, 'Right')
+ $indent = ($level + 1) * 640
+ $hanging = 280
+ }
+
+ $pPr = $lvl.AppendChild($xmlDocument.CreateElement('w', 'pPr', $xmlns))
+ $ind = $pPr.AppendChild($xmlDocument.CreateElement('w', 'ind', $xmlns))
+
+ [ref] $null = $ind.SetAttribute('left', $xmlns, $indent)
+ [ref] $null = $ind.SetAttribute('hanging', $xmlns, $hanging)
+ }
+
+ return $abstractNum
+ }
+}
diff --git a/Src/Plugins/Word/Get-WordNumberingDocument.ps1 b/Src/Plugins/Word/Get-WordNumberingDocument.ps1
new file mode 100644
index 0000000..95d900b
--- /dev/null
+++ b/Src/Plugins/Word/Get-WordNumberingDocument.ps1
@@ -0,0 +1,41 @@
+function Get-WordNumberingDocument
+{
+<#
+ .SYNOPSIS
+ Outputs Office Open XML numbering document
+#>
+ [CmdletBinding()]
+ [OutputType([System.Xml.XmlDocument])]
+ param
+ (
+ ## PScribo document styles
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Collections.ArrayList] $Lists
+ )
+ process
+ {
+ ## Create the numbering.xml document
+ $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
+ $xmlDocument = New-Object -TypeName 'System.Xml.XmlDocument'
+ [ref] $null = $xmlDocument.AppendChild($XmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes'))
+ $numbering = $xmlDocument.AppendChild($xmlDocument.CreateElement('w', 'numbering', $xmlns))
+ [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006')
+ [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:w14', 'http://schemas.microsoft.com/office/word/2010/wordml')
+
+ foreach ($list in $Lists.GetEnumerator())
+ {
+ $abstractNum = Get-WordNumberStyle -List $list -XmlDocument $xmlDocument
+ [ref] $null = $numbering.AppendChild($abstractNum)
+ }
+
+ foreach ($list in $Lists.GetEnumerator())
+ {
+ $num = $numbering.AppendChild($xmlDocument.CreateElement('w', 'num', $xmlns))
+ [ref] $null = $num.SetAttribute('numId', $xmlns, $list.Number)
+ $abstractNumId = $num.AppendChild($xmlDocument.CreateElement('w', 'abstractNumId', $xmlns))
+ [ref] $null = $abstractNumId.SetAttribute('val', $xmlns, $list.Number -1)
+ }
+
+ return $xmlDocument
+ }
+}
diff --git a/Src/Plugins/Word/Out-WordDocument.ps1 b/Src/Plugins/Word/Out-WordDocument.ps1
index b42ad87..96f281d 100644
--- a/Src/Plugins/Word/Out-WordDocument.ps1
+++ b/Src/Plugins/Word/Out-WordDocument.ps1
@@ -113,28 +113,46 @@ function Out-WordDocument
[ref] $null = $documentPart.CreateRelationship($stylesUri, [System.IO.Packaging.TargetMode]::Internal, $stylesDocumentUri, 'rId1')
[ref] $null = $documentPart.CreateRelationship($settingsUri, [System.IO.Packaging.TargetMode]::Internal, $settingsDocumentUri, 'rId2')
+ ## Create numbering.xml part
+ if ($Document.Lists.Count -gt 0)
+ {
+ $numberingUri = New-Object -TypeName System.Uri -ArgumentList ('/word/numbering.xml', [System.UriKind]::Relative)
+ Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $numberingUri)
+ $numberingPart = $package.CreatePart($numberingUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml')
+ $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($numberingPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite))
+ $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter)
+ Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $numberingUri)
+ $numberingXml = Get-WordNumberingDocument -Lists $Document.Lists
+ $numberingXml.Save($xmlWriter)
+ $xmlWriter.Dispose()
+ $streamWriter.Close()
+
+ $numberingDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering'
+ [ref] $null = $documentPart.CreateRelationship($numberingUri, [System.IO.Packaging.TargetMode]::Internal, $numberingDocumentUri, 'rId3')
+ }
+
$headerDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header'
if ($Document.Header.HasFirstPageHeader)
{
$firstPageHeaderUri = New-Object -TypeName System.Uri -ArgumentList ('/word/firstPageHeader.xml', [System.UriKind]::Relative)
- [ref] $null = $documentPart.CreateRelationship($firstPageHeaderUri, [System.IO.Packaging.TargetMode]::Internal, $headerDocumentUri, 'rId3')
+ [ref] $null = $documentPart.CreateRelationship($firstPageHeaderUri, [System.IO.Packaging.TargetMode]::Internal, $headerDocumentUri, 'rId4')
}
if ($Document.Header.HasDefaultHeader)
{
$defaultHeaderUri = New-Object -TypeName System.Uri -ArgumentList ('/word/defaultHeader.xml', [System.UriKind]::Relative)
- [ref] $null = $documentPart.CreateRelationship($defaultHeaderUri, [System.IO.Packaging.TargetMode]::Internal, $headerDocumentUri, 'rId4')
+ [ref] $null = $documentPart.CreateRelationship($defaultHeaderUri, [System.IO.Packaging.TargetMode]::Internal, $headerDocumentUri, 'rId5')
}
$footerDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer'
if ($Document.Footer.HasFirstPageFooter)
{
$firstPageFooterUri = New-Object -TypeName System.Uri -ArgumentList ('/word/firstPageFooter.xml', [System.UriKind]::Relative)
- [ref] $null = $documentPart.CreateRelationship($firstPageFooterUri, [System.IO.Packaging.TargetMode]::Internal, $footerDocumentUri, 'rId5')
+ [ref] $null = $documentPart.CreateRelationship($firstPageFooterUri, [System.IO.Packaging.TargetMode]::Internal, $footerDocumentUri, 'rId6')
}
if ($Document.Footer.HasDefaultFooter)
{
$defaultFooterUri = New-Object -TypeName System.Uri -ArgumentList ('/word/defaultFooter.xml', [System.UriKind]::Relative)
- [ref] $null = $documentPart.CreateRelationship($defaultFooterUri, [System.IO.Packaging.TargetMode]::Internal, $footerDocumentUri, 'rId6')
+ [ref] $null = $documentPart.CreateRelationship($defaultFooterUri, [System.IO.Packaging.TargetMode]::Internal, $footerDocumentUri, 'rId7')
}
## Process images (assuming we have a section, e.g. example03.ps1)
diff --git a/Src/Plugins/Word/Out-WordList.ps1 b/Src/Plugins/Word/Out-WordList.ps1
new file mode 100644
index 0000000..f8c7b37
--- /dev/null
+++ b/Src/Plugins/Word/Out-WordList.ps1
@@ -0,0 +1,67 @@
+function Out-WordList
+{
+<#
+ .SYNOPSIS
+ Output formatted Word list.
+#>
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $List,
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
+ [System.Xml.XmlElement] $Element,
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
+ [System.Xml.XmlDocument] $XmlDocument,
+
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Int32] $NumberId,
+
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Management.Automation.SwitchParameter] $NotRoot
+ )
+ process
+ {
+ $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
+
+ foreach ($item in $List.Items)
+ {
+ if ($item.Type -eq 'PScribo.Item')
+ {
+ $p = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns))
+ $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns))
+
+ if ($List.HasStyle)
+ {
+ $pStyle = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pStyle', $xmlns))
+ [ref] $null = $pStyle.SetAttribute('val', $xmlns, $List.Style)
+ }
+
+ $numPr = $pPr.AppendChild($XmlDocument.CreateElement('w', 'numPr', $xmlns))
+ $ilvl = $numPr.AppendChild($XmlDocument.CreateElement('w', 'ilvl', $xmlns))
+ [ref] $null = $ilvl.SetAttribute('val', $xmlns, $item.Level -1)
+ $numId = $numPr.AppendChild($XmlDocument.CreateElement('w', 'numId', $xmlns))
+ [ref] $null = $numId.SetAttribute('val', $xmlns, $NumberId)
+
+ $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns))
+ $rPr = Get-WordParagraphRunPr -ParagraphRun $item -XmlDocument $XmlDocument
+ [ref] $null = $r.AppendChild($rPr)
+
+ $t = $r.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns))
+ [ref] $null = $t.AppendChild($XmlDocument.CreateTextNode($item.Text))
+ }
+ elseif ($item.Type -eq 'PScribo.List')
+ {
+ Out-WordList -List $item -Element $Element -XmlDocument $XmlDocument -NumberId $NumberId -NotRoot
+ }
+ }
+
+ ## Append a blank line after each list
+ if (-not $NotRoot)
+ {
+ $p = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns))
+ }
+ }
+}
diff --git a/Src/Plugins/Word/Out-WordSection.ps1 b/Src/Plugins/Word/Out-WordSection.ps1
index 12d655d..cd9684d 100644
--- a/Src/Plugins/Word/Out-WordSection.ps1
+++ b/Src/Plugins/Word/Out-WordSection.ps1
@@ -107,6 +107,10 @@ function Out-WordSection
{
[ref] $null = $Element.AppendChild((Out-WordImage -Image $subSection -XmlDocument $XmlDocument))
}
+ 'PScribo.ListReference'
+ {
+ Out-WordList -List $Document.Lists[$subSection.Number -1] -Element $Element -XmlDocument $xmlDocument -NumberId $subSection.Number
+ }
Default
{
Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
diff --git a/Src/Private/Add-PScriboNumberStyle.ps1 b/Src/Private/Add-PScriboNumberStyle.ps1
new file mode 100644
index 0000000..42951be
--- /dev/null
+++ b/Src/Private/Add-PScriboNumberStyle.ps1
@@ -0,0 +1,91 @@
+function Add-PScriboNumberStyle
+{
+<#
+ .SYNOPSIS
+ Defines a new PScribo numbered list formatting style.
+
+ .DESCRIPTION
+ Creates a number list formatting style that can be applied to the PScribo 'List' keyword.
+
+ .NOTES
+ Not all plugins support all options.
+#>
+ [CmdletBinding()]
+ param
+ (
+ ## Number formatting style name/id
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Predefined')]
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Custom')]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Name')]
+ [System.String] $Id,
+
+ ## NOTE: Only supported in Text, Html and Word output.
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [ValidateSet('Number','Letter','Roman')]
+ [System.String] $Format,
+
+ ## Custom number 'XYZ-###' NOTE: Only supported in Text and Word output.
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [System.String] $Custom,
+
+ ## Number format suffix, e.g. '.' or ')'. NOTE: Only supported in text and Word output
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [ValidateLength(1, 1)]
+ [System.String] $Suffix = '.',
+
+ ## Only applicable to 'Letter' and 'Roman' formats
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [System.Management.Automation.SwitchParameter] $Uppercase,
+
+ ## Set as default table style. NOTE: Cannot set custom styles as default as they're not supported by all plugins.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [System.Management.Automation.SwitchParameter] $Default,
+
+ ## Number alignment.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [ValidateSet('Left', 'Right')]
+ [System.String] $Align = 'Right',
+
+ ## Override the default Word indentation level.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [System.Int32] $Indent,
+
+ ## Override the default Word hanging indentation level.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [System.Int32] $Hanging
+ )
+ begin
+ {
+ if ($Custom)
+ {
+ if (-not $Custom.Contains('%'))
+ {
+ throw ($localized.InvalidCustomNumberStyleError -f $Custom)
+ }
+ }
+ }
+ process
+ {
+ $pscriboDocument.Properties['NumberStyles']++
+ $numberStyle = [PSCustomObject] @{
+ Id = $Id.Replace(' ', $pscriboDocument.Options['SpaceSeparator'])
+ Name = $Id
+ Format = if ($PSBoundParameters.ContainsKey('Format')) { $Format } else { 'Custom' }
+ Custom = $Custom
+ Align = $Align
+ Uppercase = $Uppercase
+ Suffix = $Suffix
+ Indent = $Indent
+ Hanging = $Hanging
+ }
+ $pscriboDocument.NumberStyles[$Id] = $numberStyle
+ if ($Default)
+ {
+ $pscriboDocument.DefaultNumberStyle = $numberStyle.Id
+ }
+ }
+}
diff --git a/Src/Private/ConvertFrom-NumberStyle.ps1 b/Src/Private/ConvertFrom-NumberStyle.ps1
new file mode 100644
index 0000000..4f531f3
--- /dev/null
+++ b/Src/Private/ConvertFrom-NumberStyle.ps1
@@ -0,0 +1,61 @@
+function ConvertFrom-NumberStyle
+{
+<#
+ .SYNOPSIS
+ Converts a number to its string representation, based upon the number style
+#>
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Int32] $Value,
+
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.PSObject] $NumberStyle
+ )
+ process
+ {
+ switch ($NumberStyle.Format)
+ {
+ 'Number'
+ {
+ $numberString = $Value.ToString()
+ }
+ 'Letter'
+ {
+ $numberString = [System.Char] ($Value + 64)
+ }
+ 'Roman'
+ {
+ $numberString = ConvertTo-RomanNumeral -Value $Value
+ }
+ 'Custom'
+ {
+ $numberCount = 0
+ $customStringChars = $numberStyle.Custom.ToCharArray()
+ $customStringLength = $numberStyle.Custom.Length
+ for ($n = $customStringLength - 1; $n -ge 0; $n--)
+ {
+ if ($customStringChars[$n] -eq '%')
+ {
+ $numberCount++
+ }
+ }
+
+ $searchString = ''.PadRight($numberCount, '%')
+ $replacementString = $Value.ToString().PadLeft($numberCount, '0')
+ return $numberStyle.Custom.Replace($searchString, $replacementString)
+ }
+ }
+
+ $numberString = '{0}{1}' -f $numberString, $NumberStyle.Suffix
+ if ($NumberStyle.Uppercase)
+ {
+ return $numberString.ToUpper()
+ }
+ else
+ {
+ return $numberString.ToLower()
+ }
+ }
+}
diff --git a/Src/Private/ConvertTo-RomanNumeral.ps1 b/Src/Private/ConvertTo-RomanNumeral.ps1
new file mode 100644
index 0000000..8135d29
--- /dev/null
+++ b/Src/Private/ConvertTo-RomanNumeral.ps1
@@ -0,0 +1,50 @@
+function ConvertTo-RomanNumeral
+{
+<#
+ .SYNOPSIS
+ Converts a decimal number to Roman numerals.
+#>
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
+ [System.Int32] $Value
+ )
+ begin
+ {
+ $conversionTable = [ordered] @{
+ 1000 = 'M'
+ 900 = 'CM'
+ 500 = 'D'
+ 400 = 'CD'
+ 100 = 'C'
+ 90 = 'XC'
+ 50 = 'L'
+ 40 = 'XL'
+ 10 = 'X'
+ 9 = 'IX'
+ 5 = 'V'
+ 4 = 'IV'
+ 1 = 'I'
+ }
+ }
+ process
+ {
+ $romanNumeralBuilder = New-Object -TypeName System.Text.StringBuilder
+ do
+ {
+ foreach ($romanNumeral in $conversionTable.GetEnumerator())
+ {
+ if ($Value -ge $romanNumeral.Key)
+ {
+ [ref] $null = $romanNumeralBuilder.Append($romanNumeral.Value)
+ $Value -= $romanNumeral.Key
+ break
+ }
+ }
+
+ }
+ until ($Value -eq 0)
+ return $romanNumeralBuilder.ToString()
+ }
+}
diff --git a/Src/Private/Invoke-PScriboListLevel.ps1 b/Src/Private/Invoke-PScriboListLevel.ps1
new file mode 100644
index 0000000..e714b03
--- /dev/null
+++ b/Src/Private/Invoke-PScriboListLevel.ps1
@@ -0,0 +1,54 @@
+function Invoke-PScriboListLevel
+{
+<#
+ .SYNOPSIS
+ Nested function that processes each nested list numbering.
+#>
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory)]
+ [ValidateNotNull()]
+ [System.Management.Automation.PSObject] $List,
+
+ [Parameter(Mandatory)]
+ [AllowEmptyString()]
+ [System.String] $Number
+ )
+ process
+ {
+ $level = $Number.Split('.').Count +1
+ $processingListPadding = ''.PadRight($level -1, ' ')
+ $processingListMessage = $localized.ProcessingList -f $List.Name
+ Write-PScriboMessage -Message ('{0}{1}' -f $processingListPadding, $processingListMessage)
+
+ $itemNumber = 0
+ $numberString = $Number
+ $hasItem = $false
+
+ foreach ($item in $List.Items)
+ {
+ if ($item.Type -eq 'PScribo.Item')
+ {
+ $itemNumber++
+ $item.Level = $level
+ $item.Index = $itemNumber
+ $item.Number = ('{0}.{1}' -f $Number, $itemNumber).TrimStart('.')
+
+ $numberString = $item.Number
+ $hasItem = $true
+ }
+ elseif ($item.Type -eq 'PScribo.List')
+ {
+ if ($hasItem)
+ {
+ Invoke-PScriboListLevel -List $item -Number $numberString
+ }
+ else
+ {
+ Write-PScriboMessage -Message $localized.NoPriorListItemWarning -IsWarning
+ }
+ }
+ }
+ }
+}
diff --git a/Src/Private/New-PScriboDocument.ps1 b/Src/Private/New-PScriboDocument.ps1
index f382639..0dd6d95 100644
--- a/Src/Private/New-PScriboDocument.ps1
+++ b/Src/Private/New-PScriboDocument.ps1
@@ -35,29 +35,32 @@ function New-PScriboDocument
Write-PScriboMessage -Message ($localized.DocumentProcessingStarted -f $Name)
$typeName = 'PScribo.Document'
$pscriboDocument = [PSCustomObject] @{
- Id = $Id.ToUpper()
- Type = $typeName
- Name = $Name
- Sections = New-Object -TypeName System.Collections.ArrayList
- Options = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
- Properties = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
- Styles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
- TableStyles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
- DefaultStyle = $null
- DefaultTableStyle = $null
- Header = [PSCustomObject] @{
- HasFirstPageHeader = $false
- HasDefaultHeader = $false
- FirstPageHeader = $null
- DefaultHeader = $null
- }
- Footer = [PSCustomObject] @{
- HasFirstPageFooter = $false
- HasDefaultFooter = $false
- FirstPageFooter = $null
- DefaultFooter = $null
- }
- TOC = New-Object -TypeName System.Collections.ArrayList
+ Id = $Id.ToUpper()
+ Type = $typeName
+ Name = $Name
+ Sections = New-Object -TypeName System.Collections.ArrayList
+ Options = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
+ Properties = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
+ Styles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
+ TableStyles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
+ NumberStyles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase)
+ Lists = New-Object -TypeName System.Collections.ArrayList # Store all list references for Word numbering.xml generation
+ DefaultStyle = $null
+ DefaultTableStyle = $null
+ DefaultNumberStyle = $null
+ Header = [PSCustomObject] @{
+ HasFirstPageHeader = $false
+ HasDefaultHeader = $false
+ FirstPageHeader = $null
+ DefaultHeader = $null
+ }
+ Footer = [PSCustomObject] @{
+ HasFirstPageFooter = $false
+ HasDefaultFooter = $false
+ FirstPageFooter = $null
+ DefaultFooter = $null
+ }
+ TOC = New-Object -TypeName System.Collections.ArrayList
}
$defaultDocumentOptionParams = @{
MarginTopAndBottom = 72
@@ -92,6 +95,9 @@ function New-PScriboDocument
CaptionStyle = 'Caption'
}
TableStyle @tableDefaultStyleParams -Default -Verbose:$false
+ NumberStyle -Id 'Number' -Format Number -Default -Verbose:$false
+ NumberStyle -Id 'Letter' -Format Letter -Verbose:$false
+ NumberStyle -Id 'Roman' -Format Roman -Verbose:$false
return $pscriboDocument
}
}
diff --git a/Src/Private/New-PScriboItem.ps1 b/Src/Private/New-PScriboItem.ps1
new file mode 100644
index 0000000..3855449
--- /dev/null
+++ b/Src/Private/New-PScriboItem.ps1
@@ -0,0 +1,70 @@
+function New-PScriboItem
+{
+<#
+ .SYNOPSIS
+ Initializes new PScribo list item object.
+
+ .NOTES
+ This is an internal function and should not be called directly.
+#>
+ [CmdletBinding(DefaultParameterSetName = 'Default')]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')]
+ [OutputType([System.Management.Automation.PSCustomObject])]
+ param
+ (
+ [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
+ [System.String] $Text,
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Style')]
+ [System.String] $Style,
+
+ ## Override the bold style
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [System.Management.Automation.SwitchParameter] $Bold,
+
+ ## Override the italic style
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [System.Management.Automation.SwitchParameter] $Italic,
+
+ ## Override the underline style
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [System.Management.Automation.SwitchParameter] $Underline,
+
+ ## Override the font name(s)
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]] $Font,
+
+ ## Override the font size (pt)
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [AllowNull()]
+ [System.UInt16] $Size = $null,
+
+ ## Override the font color/colour
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [AllowNull()]
+ [System.String] $Color = $null
+ )
+ process
+ {
+ $pscriboItem = [PSCustomObject] @{
+ Id = [System.Guid]::NewGuid().ToString()
+ Level = 0
+ Index = 0
+ Number = ''
+ Text = $Text
+ Type = 'PScribo.Item'
+ Style = $Style
+ Bold = $Bold
+ Italic = $Italic
+ Underline = $Underline
+ Font = $Font
+ Size = $Size
+ Color = $Color
+ IsStyleInherited = $PSCmdlet.ParameterSetName -eq 'Default'
+ HasStyle = $PSCmdlet.ParameterSetName -eq 'Style'
+ HasInlineStyle = $PSCmdlet.ParameterSetName -eq 'Inline'
+ }
+ return $pscriboItem
+ }
+}
diff --git a/Src/Private/New-PScriboList.ps1 b/Src/Private/New-PScriboList.ps1
new file mode 100644
index 0000000..28b5e1f
--- /dev/null
+++ b/Src/Private/New-PScriboList.ps1
@@ -0,0 +1,62 @@
+function New-PScriboList
+{
+<#
+ .SYNOPSIS
+ Initializes new PScribo list object.
+
+ .NOTES
+ This is an internal function and should not be called directly.
+#>
+ [CmdletBinding()]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')]
+ [OutputType([System.Management.Automation.PSCustomObject])]
+ param
+ (
+ ## Display name used in verbose output when processing.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $Name,
+
+ ## List style Name/Id reference.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $Style,
+
+ ## Numbered list.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Management.Automation.SwitchParameter] $Numbered,
+
+ ## Numbered list style.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $NumberStyle = $pscriboDocument.DefaultNumberStyle,
+
+ ## Bullet list style.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [ValidateSet('Circle', 'Dash', 'Disc', 'Square')]
+ [System.String] $BulletStyle = 'Disc',
+
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Int32] $Level = 1
+ )
+ process
+ {
+ $pscriboList = [PSCustomObject] @{
+ Id = [System.Guid]::NewGuid().ToString()
+ Name = $Name
+ Type = 'PScribo.List'
+ Items = (New-Object -TypeName System.Collections.ArrayList)
+ Number = 0
+ Level = $Level
+ IsNumbered = $Numbered.ToBool()
+ Style = $Style
+ BulletStyle = $BulletStyle
+ NumberStyle = $NumberStyle
+ IsMultiLevel = $false
+ IsSectionBreak = $false
+ IsSectionBreakEnd = $false
+ IsStyleInherited = -not $PSBoundParameters.ContainsKey('Style')
+ HasStyle = $PSBoundParameters.ContainsKey('Style')
+ HasBulletStyle = -not $Numbered.ToBool()
+ HasNumberStyle = $PSBoundParameters.ContainsKey('NumberStyle')
+ }
+ return $pscriboList
+ }
+}
diff --git a/Src/Private/New-PScriboListReference.ps1 b/Src/Private/New-PScriboListReference.ps1
new file mode 100644
index 0000000..3571fc8
--- /dev/null
+++ b/Src/Private/New-PScriboListReference.ps1
@@ -0,0 +1,35 @@
+function New-PScriboListReference
+{
+<#
+ .SYNOPSIS
+ Initializes new PScribo list reference object.
+
+ .DESCRIPTION
+ Creates a placeholder reference to a list stored in $pscriboDocument.Lists.
+
+ .NOTES
+ This is an internal function and should not be called directly.
+#>
+ [CmdletBinding()]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')]
+ [OutputType([System.Management.Automation.PSCustomObject])]
+ param
+ (
+ ## Display name used in verbose output when processing.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $Name,
+
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.Int32] $Number
+ )
+ process
+ {
+ $pscriboListReference = [PSCustomObject] @{
+ Id = [System.Guid]::NewGuid().ToString()
+ Name = $Name
+ Type = 'PScribo.ListReference'
+ Number = $Number
+ }
+ return $pscriboListReference
+ }
+}
diff --git a/Src/Private/New-PScriboSection.ps1 b/Src/Private/New-PScriboSection.ps1
index 25772e5..47b93a6 100644
--- a/Src/Private/New-PScriboSection.ps1
+++ b/Src/Private/New-PScriboSection.ps1
@@ -38,8 +38,9 @@ function New-PScriboSection
)
begin
{
- $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '' }
- if ($PSBoundParameters.ContainsKey('Orientation') -and ($psCallStack[2].FunctionName -ne 'Document'))
+ ## Ensure we only have one 'Section' in the call stack (#121)
+ $psCallStack = @(Get-PSCallStack | Where-Object { $_.FunctionName -eq 'Section' })
+ if ($PSBoundParameters.ContainsKey('Orientation') -and ($psCallStack.Count -gt 1))
{
Write-PScriboMessage -Message $localized.CannotSetOrientationWarning -IsWarning;
$null = $PSBoundParameters.Remove('Orientation')
diff --git a/Src/Public/Item.ps1 b/Src/Public/Item.ps1
new file mode 100644
index 0000000..b059514
--- /dev/null
+++ b/Src/Public/Item.ps1
@@ -0,0 +1,59 @@
+function Item
+{
+<#
+ .SYNOPSIS
+ Initializes a new PScribo list Item object.
+#>
+ [CmdletBinding(DefaultParameterSetName = 'Default')]
+ [OutputType([System.Management.Automation.PSCustomObject])]
+ param
+ (
+ ## List item text.
+ [Parameter(Mandatory, Position = 0, ValueFromPipeline)]
+ [ValidateNotNull()]
+ [System.String] $Text,
+
+ ## List item style Name/Id reference.
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Style')]
+ [System.String] $Style,
+
+ ## Override the bold style
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [System.Management.Automation.SwitchParameter] $Bold,
+
+ ## Override the italic style
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [System.Management.Automation.SwitchParameter] $Italic,
+
+ ## Override the underline style
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [System.Management.Automation.SwitchParameter] $Underline,
+
+ ## Override the font name(s)
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]] $Font,
+
+ ## Override the font size (pt)
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [AllowNull()]
+ [System.UInt16] $Size = $null,
+
+ ## Override the font color/colour
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')]
+ [AllowNull()]
+ [System.String] $Color = $null
+ )
+ begin
+ {
+ $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '' }
+ if ($psCallStack[1].FunctionName -ne 'List')
+ {
+ throw $localized.ItemRootError
+ }
+ }
+ process
+ {
+ return (New-PScriboItem @PSBoundParameters)
+ }
+}
diff --git a/Src/Public/List.ps1 b/Src/Public/List.ps1
new file mode 100644
index 0000000..46ca4e2
--- /dev/null
+++ b/Src/Public/List.ps1
@@ -0,0 +1,142 @@
+function List
+{
+<#
+ .SYNOPSIS
+ Initializes a new PScribo bulleted or numbered List object.
+#>
+ [CmdletBinding(DefaultParameterSetName = 'BulletItem')]
+ [OutputType([System.Management.Automation.PSCustomObject])]
+ param
+ (
+ ## List item(s).
+ [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ItemBullet')]
+ [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ItemNumbered')]
+ [ValidateNotNull()]
+ [System.String[]] $Item,
+
+ ## PScribo nested list/items.
+ [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName, ParameterSetName = 'ListBullet')]
+ [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName, ParameterSetName = 'ListNumbered')]
+ [ValidateNotNull()]
+ [System.Management.Automation.ScriptBlock] $ScriptBlock,
+
+ ## Display name used in verbose output when processing.
+ [Parameter(ValueFromPipelineByPropertyName)]
+ [System.String] $Name,
+
+ ## List style Name/Id reference.
+ [Parameter(Position = 1, ValueFromPipelineByPropertyName)]
+ [System.String] $Style,
+
+ ## Numbered list.
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ItemNumbered')]
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ListNumbered')]
+ [System.Management.Automation.SwitchParameter] $Numbered,
+
+ ## Numbered list style.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ItemNumbered')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ListNumbered')]
+ [System.String] $NumberStyle,
+
+ ## Bullet list style.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ItemBullet')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ListBullet')]
+ [ValidateSet('Circle', 'Dash', 'Disc', 'Square')]
+ [System.String] $BulletStyle = 'Disc'
+ )
+ begin
+ {
+ $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '' }
+ if ($psCallStack[1].FunctionName -notin 'List','Document','Section')
+ {
+ Write-Warning $psCallStack[1].FunctionName
+ throw $localized.ListRootError
+ }
+ }
+ process
+ {
+ $null = $PSBoundParameters.Remove('ScriptBlock')
+ $null = $PSBoundParameters.Remove('Item')
+
+ $pscriboList = New-PScriboList @PSBoundParameters
+
+ if ($PSCmdlet.ParameterSetName -in 'ItemBullet','ItemNumbered')
+ {
+ foreach ($listItem in $Item)
+ {
+ $pscriboListItem = New-PScriboItem -Text $listItem
+ [ref] $null = $pscriboList.Items.Add($pscriboListItem)
+ }
+ }
+ elseif ($PSCmdlet.ParameterSetName -in 'ListBullet','ListNumbered')
+ {
+ foreach ($result in & $ScriptBlock)
+ {
+ ## Ensure we don't have something errant passed down the pipeline (#29)
+ if ($result -is [System.Management.Automation.PSObject])
+ {
+ if (('Id' -in $result.PSObject.Properties.Name) -and
+ ('Type' -in $result.PSObject.Properties.Name) -and
+ ($result.Type -match '^PScribo.'))
+ {
+ [ref] $null = $pscriboList.Items.Add($result)
+ }
+ else
+ {
+ Write-PScriboMessage -Message ($localized.UnexpectedObjectWarning -f $Name) -IsWarning
+ }
+ }
+ else
+ {
+ Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), $Name) -IsWarning
+ }
+ }
+ }
+
+ ## Only process list levels on the root list object
+ if ($psCallStack[1].FunctionName -in 'Document','Section')
+ {
+ Write-PScriboMessage -Message ($localized.ProcessingList -f $pscriboList.Name)
+ $hasItem = $false
+ $itemNumber = 0
+
+ foreach ($listItem in $pscriboList.Items)
+ {
+ if ($listItem.Type -eq 'PScribo.Item')
+ {
+ $itemNumber++
+ $listItem.Level = 1
+ $listItem.Index = $itemNumber
+ $listItem.Number = $itemNumber.ToString()
+
+ $hasItem = $true
+ }
+ elseif ($listItem.Type -eq 'PScribo.List')
+ {
+ if ($hasItem)
+ {
+ $pscriboList.IsMultiLevel = $true
+ Invoke-PScriboListLevel -List $listItem -Number $itemNumber.ToString()
+ }
+ else
+ {
+ Write-PScriboMessage -Message $localized.NoPriorListItemWarning -IsWarning
+ }
+ }
+ }
+
+ ## Store lists for Word numbering.xml
+ $pscriboDocument.Properties['Lists']++
+ $pscriboList.Number = $pscriboDocument.Properties['Lists']
+ [ref] $null = $pscriboDocument.Lists.Add($pscriboList)
+
+ ## Return list reference
+ $pscriboListReference = New-PScriboListReference -Name $pscriboList.Name -Number $pscriboList.Number
+ return $pscriboListReference
+ }
+ else
+ {
+ return $pscriboList
+ }
+ }
+}
diff --git a/Src/Public/NumberStyle.ps1 b/Src/Public/NumberStyle.ps1
new file mode 100644
index 0000000..24e4d29
--- /dev/null
+++ b/Src/Public/NumberStyle.ps1
@@ -0,0 +1,66 @@
+function NumberStyle
+{
+<#
+ .SYNOPSIS
+ Defines a new PScribo numbered list formatting style.
+
+ .DESCRIPTION
+ Creates a number list formatting style that can be applied to the PScribo 'List' keyword.
+
+ .NOTES
+ Not all plugins support all options.
+#>
+ [CmdletBinding(DefaultParameterSetName = 'Predefined')]
+ param
+ (
+ ## Table Style name/id
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Predefined')]
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Custom')]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Name')]
+ [System.String] $Id,
+
+ ## NOTE: Only supported in Text, Html and Word output.
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [ValidateSet('Number','Letter','Roman')]
+ [System.String] $Format,
+
+ ## Custom number 'XYZ-###' NOTE: Only supported in Text and Word output.
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [System.String] $Custom,
+
+ ## Number format suffix, e.g. '.' or ')'. NOTE: Only supported in text and Word output
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [ValidateLength(1, 1)]
+ [System.String] $Suffix = '.',
+
+ ## Only applicable to 'Letter' and 'Roman' formats
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [System.Management.Automation.SwitchParameter] $Uppercase,
+
+ ## Set as default table style. NOTE: Cannot set custom styles as default as they're not supported by all plugins.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [System.Management.Automation.SwitchParameter] $Default,
+
+ ## Number alignment.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [ValidateSet('Left', 'Right')]
+ [System.String] $Align = 'Right',
+
+ ## Override the default Word indentation level.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [System.Int32] $Indent,
+
+ ## Override the default Word hanging indentation level.
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')]
+ [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')]
+ [System.Int32] $Hanging
+ )
+ process
+ {
+ Write-PScriboMessage -Message ($localized.ProcessingNumberStyle -f $Id)
+ Add-PScriboNumberStyle @PSBoundParameters
+ }
+}
diff --git a/Src/Public/Set-Style.ps1 b/Src/Public/Set-Style.ps1
index 7f00572..c61d7e4 100644
--- a/Src/Public/Set-Style.ps1
+++ b/Src/Public/Set-Style.ps1
@@ -6,6 +6,7 @@ function Set-Style
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')]
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidMultipleTypeAttributes','')]
[OutputType([System.Object])]
param
(
diff --git a/Src/Public/Table.ps1 b/Src/Public/Table.ps1
index c68e402..3d477f3 100644
--- a/Src/Public/Table.ps1
+++ b/Src/Public/Table.ps1
@@ -147,6 +147,11 @@ function Table
Write-PScriboMessage -Message $localized.TableColumnWidthMismatchWarning -IsWarning
$ColumnWidths = $null
}
+ elseif (($PSCmdlet.ParameterSetName -eq 'HashtableListKey') -and (($Hashtable.Count + 1) -ne $ColumnWidths.Count))
+ {
+ Write-PScriboMessage -Message $localized.TableColumnWidthMismatchWarning -IsWarning
+ $ColumnWidths = $null
+ }
elseif (($PSCmdlet.ParameterSetName -eq 'InputObject') -and (-not $List))
{
## Columns might not have been passed and there is no object in the pipeline here, so check $Columns is an array.
diff --git a/Tests/Linting/FileEncoding.Tests.ps1 b/Tests/Linting/FileEncoding.Tests.ps1
index 26c0b30..1360097 100644
--- a/Tests/Linting/FileEncoding.Tests.ps1
+++ b/Tests/Linting/FileEncoding.Tests.ps1
@@ -5,7 +5,6 @@ Describe 'Linting\FileEncoding' {
$excludedPaths = @(
'.git*',
'.vscode',
- 'DSCResources', # We'll take the public DSC resources as-is
'Release',
'*.png',
'*.jpg',
@@ -18,6 +17,7 @@ Describe 'Linting\FileEncoding' {
'Docs',
'PScriboExample.*',
'Lib'
+ 'Get-WordNumberStyle.ps1'
);
function Get-FileEncoding {
diff --git a/Tests/Plugins/Html/Out-HtmlList.Tests.ps1 b/Tests/Plugins/Html/Out-HtmlList.Tests.ps1
new file mode 100644
index 0000000..382cdb4
--- /dev/null
+++ b/Tests/Plugins/Html/Out-HtmlList.Tests.ps1
@@ -0,0 +1,139 @@
+$here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent;
+$pluginRoot = Split-Path -Path $here -Parent;
+$testRoot = Split-Path -Path $pluginRoot -Parent;
+$moduleRoot = Split-Path -Path $testRoot -Parent;
+Import-Module "$moduleRoot\PScribo.psm1" -Force;
+
+InModuleScope 'PScribo' {
+
+ Describe 'Plugins\Html\Out-HtmlList' {
+
+ BeforeEach {
+
+ ## Scaffold document options
+ $Document = Document -Name 'TestDocument' -ScriptBlock {}
+ # $script:currentPageNumber = 1
+ $pscriboDocument = $Document
+ $Options = New-PScriboTextOption
+ }
+
+ It 'Single-level bulleted disc list' {
+
+ $bulletStyle = 'Disc'
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle $bulletStyle }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match ('' -f $bulletStyle)
+ $result | Should Match ('- {0}
' -f $testItems[0])
+ $result | Should Match ('- {0}
' -f $testItems[1])
+ $result | Should Match ('- {0}
' -f $testItems[2])
+ $result | Should Match '
'
+ }
+
+ It 'Single-level bulleted circle list' {
+
+ $bulletStyle = 'Circle'
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle $bulletStyle }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match ('' -f $bulletStyle)
+ $result | Should Match ('- {0}
' -f $testItems[0])
+ $result | Should Match ('- {0}
' -f $testItems[1])
+ $result | Should Match ('- {0}
' -f $testItems[2])
+ $result | Should Match '
'
+ }
+
+ It 'Single-level bulleted dash list' {
+
+ $bulletStyle = 'Dash'
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle $bulletStyle }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match '' # Dash is default unordered list type
+ $result | Should Match ('- {0}
' -f $testItems[0])
+ $result | Should Match ('- {0}
' -f $testItems[1])
+ $result | Should Match ('- {0}
' -f $testItems[2])
+ $result | Should Match '
'
+ }
+
+ It 'Single-level bulleted square list' {
+
+ $bulletStyle = 'Square'
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle $bulletStyle }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match ('' -f $bulletStyle)
+ $result | Should Match ('- {0}
' -f $testItems[0])
+ $result | Should Match ('- {0}
' -f $testItems[1])
+ $result | Should Match ('- {0}
' -f $testItems[2])
+ $result | Should Match '
'
+ }
+
+ It 'Single-level numbered list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -Numbered }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match ''
+ $result | Should Match ('- {0}
' -f $testItems[0])
+ $result | Should Match ('- {0}
' -f $testItems[1])
+ $result | Should Match ('- {0}
' -f $testItems[2])
+ $result | Should Match '
'
+ }
+
+ It 'Multi-level numbered list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $testSubItems = 'Braeburn', 'Granny Smith'
+ $null = Section 'Test' {
+ List -Numbered {
+ Item $testItems[0]
+ List -Numbered {
+ Item $testSubItems[0]
+ Item $testSubItems[1]
+ }
+ Item $testItems[1]
+ Item $testItems[2]
+ }
+ }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match ''
+ $result | Should Match ('- {0}
\s+\s+- {1}
\s+- {2}
\s+
' -f $testItems[0], $testSubItems[0], $testSubItems[1])
+ }
+
+ It 'Multi-level roman numbered list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $testSubItems = 'Braeburn', 'Granny Smith'
+ $null = Section 'Test' {
+ List -Numbered -NumberStyle Roman {
+ Item $testItems[0]
+ List -Numbered {
+ Item $testSubItems[0]
+ Item $testSubItems[1]
+ }
+ Item $testItems[1]
+ Item $testItems[2]
+ }
+ }
+
+ $result = Out-HtmlList -List $Document.Lists[0]
+
+ $result | Should Match ''
+ $result | Should Match ('- {0}
\s+\s+- {1}
\s+- {2}
\s+
' -f $testItems[0], $testSubItems[0], $testSubItems[1])
+ }
+
+ }
+}
diff --git a/Tests/Plugins/Text/Out-TextList.Tests.ps1 b/Tests/Plugins/Text/Out-TextList.Tests.ps1
new file mode 100644
index 0000000..fae701d
--- /dev/null
+++ b/Tests/Plugins/Text/Out-TextList.Tests.ps1
@@ -0,0 +1,136 @@
+$here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent;
+$pluginRoot = Split-Path -Path $here -Parent;
+$testRoot = Split-Path -Path $pluginRoot -Parent;
+$moduleRoot = Split-Path -Path $testRoot -Parent;
+Import-Module "$moduleRoot\PScribo.psm1" -Force;
+
+InModuleScope 'PScribo' {
+
+ Describe 'Plugins\Text\Out-TextList' {
+
+ BeforeEach {
+
+ ## Scaffold document options
+ $Document = Document -Name 'TestDocument' -ScriptBlock {}
+ # $script:currentPageNumber = 1
+ $pscriboDocument = $Document
+ $Options = New-PScriboTextOption
+ }
+
+ It 'Terminates with a blank line' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $expectedMatch = '{0}{0}$' -f [System.Environment]::NewLine
+ $result | Should Match $expectedMatch
+ }
+
+ It 'Single-level bulleted disc list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle Disc }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('\* {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('\* {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('\* {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ It 'Single-level bulleted circle list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle Circle }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('o {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('o {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('o {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ It 'Single-level bulleted dash list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle Dash }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('- {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('- {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('- {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ It 'Single-level bulleted square list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -BulletStyle Square }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('\* {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('\* {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('\* {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ It 'Single-level numbered list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $null = Section 'Test' { List -Item $testItems -Numbered }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('1\. {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('2\. {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('3\. {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ It 'Multi-level numbered list' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $testSubItems = 'Braeburn', 'Granny Smith'
+ $null = Section 'Test' {
+ List -Numbered {
+ Item $testItems[0]
+ List -Numbered {
+ Item $testSubItems[0]
+ Item $testSubItems[1]
+ }
+ Item $testItems[1]
+ Item $testItems[2]
+ }
+ }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('1\. {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('1\. {0}{1}' -f $testSubItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('2\. {0}{1}' -f $testSubItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('2\. {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('3\. {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ It 'outputs custom numbered list format' {
+
+ $testItems = 'Apples', 'Bananas', 'Oranges'
+ $customNumberFormat = 'xYz-%%%.'
+ $indent = 1500
+ $hanging = 200
+
+ $null = Section 'Test' {
+ NumberStyle -Id 'CustomNumberStyle' -Custom $customNumberFormat -Indent $indent -Hanging $hanging -Align Left
+ List -Numbered -NumberStyle CustomNumberStyle -Item $testItems
+ }
+
+ $result = Out-TextList -List $Document.Lists[0]
+
+ $result | Should Match ('xYz-001. {0}{1}' -f $testItems[0], [System.Environment]::NewLine)
+ $result | Should Match ('xYz-002. {0}{1}' -f $testItems[1], [System.Environment]::NewLine)
+ $result | Should Match ('xYz-003. {0}{1}' -f $testItems[2], [System.Environment]::NewLine)
+ }
+
+ }
+}
diff --git a/Tests/Plugins/Word/Out-WordList.Tests.ps1 b/Tests/Plugins/Word/Out-WordList.Tests.ps1
new file mode 100644
index 0000000..bd86715
--- /dev/null
+++ b/Tests/Plugins/Word/Out-WordList.Tests.ps1
@@ -0,0 +1,134 @@
+$here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent;
+$pluginsRoot = Split-Path -Path $here -Parent;
+$testRoot = Split-Path -Path $pluginsRoot -Parent;
+$moduleRoot = Split-Path -Path $testRoot -Parent;
+Import-Module "$moduleRoot\PScribo.psm1" -Force;
+
+InModuleScope 'PScribo' {
+
+ function GetMatch
+ {
+ [CmdletBinding()]
+ param
+ (
+ [System.String] $String,
+ [System.Management.Automation.SwitchParameter] $Complete
+ )
+ Write-Verbose "Pre Match : '$String'"
+ $matchString = $String.Replace('/','\/')
+ if (-not $String.StartsWith('^'))
+ {
+ $matchString = $matchString.Replace('[..]','[\s\S]+')
+ $matchString = $matchString.Replace('[??]','([\s\S]+)?')
+ if ($Complete)
+ {
+ $matchString = '^{0}<\/w:test>$' -f $matchString
+ }
+ }
+ Write-Verbose "Post Match: '$matchString'"
+ return $matchString
+ }
+
+ Describe 'Plugins\Word\Out-WordList' {
+
+ Context 'single-level list' {
+
+ It 'outputs number properties "[..]>[..]" per item' {
+ $document = Document -Name 'TestDocument' {
+ List -Item 'Apples', 'Oranges', 'Pears'
+ }
+ $testDocument = Get-WordDocument -Document $document
+
+ $expected = GetMatch '([..]>[..]){3}'
+
+ $testDocument.DocumentElement.OuterXml | Should Match $expected
+ }
+
+ It 'outputs number property level "" per item' {
+ $document = Document -Name 'TestDocument' {
+ List -Item 'Apples', 'Oranges', 'Pears'
+ }
+ $testDocument = Get-WordDocument -Document $document
+
+ $expected = GetMatch '(.*){3}'
+
+ $testDocument.DocumentElement.OuterXml | Should Match $expected
+ }
+
+ It 'outputs run "[..]" per item' {
+ $document = Document -Name 'TestDocument' {
+ List -Item 'Apples', 'Oranges', 'Pears'
+ }
+ $testDocument = Get-WordDocument -Document $document
+
+ $expected = GetMatch '([..].*){3}'
+
+ $testDocument.DocumentElement.OuterXml | Should Match $expected
+ }
+
+ }
+
+ Context 'multi-level list' {
+
+ It 'outputs run "[..]" per item' {
+ $document = Document -Name 'TestDocument' {
+ List -Numbered {
+ Item 'Apples'
+ List -Numbered {
+ Item 'Braeburn'
+ Item 'Granny Smith'
+ }
+ Item 'Oranges'
+ Item 'Pears'
+ }
+ }
+ $testDocument = Get-WordDocument -Document $document
+
+ $expected = GetMatch '([..].*){5}'
+
+ $testDocument.DocumentElement.OuterXml | Should Match $expected
+ }
+
+ It 'outputs number property level "" per nested item' {
+
+ $document = Document -Name 'TestDocument' {
+ List -Numbered {
+ Item 'Apples'
+ List -Numbered {
+ Item 'Braeburn'
+ Item 'Granny Smith'
+ }
+ Item 'Oranges'
+ Item 'Pears'
+ }
+ }
+ $testDocument = Get-WordDocument -Document $document
+
+ $expected = GetMatch '(.*){2}'
+
+ $testDocument.DocumentElement.OuterXml | Should Match $expected
+ }
+
+ It 'outputs custom numbered list format' {
+
+ $customNumberFormat = 'xYz-%%%.'
+ $indent = 1500
+ $hanging = 200
+
+ $document = Document -Name 'TestDocument' {
+ NumberStyle -Id 'CustomNumberStyle' -Custom $customNumberFormat -Indent $indent -Hanging $hanging -Align Left
+ List -Numbered -NumberStyle CustomNumberStyle -Item 'Apples','Bananas','Oranges'
+ }
+ $testNumberingDocument = Get-WordNumberingDocument -Lists $document.Lists
+
+ $testNumberingDocument.DocumentElement.OuterXml | Should Match ''
+ $testNumberingDocument.DocumentElement.OuterXml | Should Match ''
+ $testNumberingDocument.DocumentElement.OuterXml | Should Match ''
+ $testNumberingDocument.DocumentElement.OuterXml | Should Match ('' -f $customNumberFormat.Replace('%%%','%1'))
+ $testNumberingDocument.DocumentElement.OuterXml | Should Match ('' -f $indent, $hanging)
+ }
+
+ }
+
+ }
+}
diff --git a/Tests/Unit/List.Tests.ps1 b/Tests/Unit/List.Tests.ps1
new file mode 100644
index 0000000..5f285a0
--- /dev/null
+++ b/Tests/Unit/List.Tests.ps1
@@ -0,0 +1,118 @@
+$here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent
+$testRoot = Split-Path -Path $here -Parent
+$moduleRoot = Split-Path -Path $testRoot -Parent
+Import-Module -Name "$moduleRoot\PScribo.psm1" -Force
+
+InModuleScope -ModuleName 'PScribo' -ScriptBlock {
+
+ Describe -Name 'List' {
+
+ BeforeEach {
+
+ $pscriboDocument = Document -Name 'ScaffoldDocument' -ScriptBlock { }
+ }
+
+ It 'returns "PSCustomObject" object' {
+ $l = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges'
+ }
+
+ $l.Sections[0].GetType().Name | Should -eq 'PSCustomObject'
+ }
+
+ It 'creates "PScribo.ListReference" type' {
+ $l = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges'
+ }
+
+ $l.Sections[0].Type | Should -eq 'PScribo.ListReference'
+ }
+
+ It 'creates "PScribo.List" object' {
+ $null = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges'
+ }
+
+ $pscriboDocument.Lists.Count | Should Be 1
+ }
+
+ It 'defaults to "Disc" bulleted list' {
+ $null = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges'
+ }
+
+ $pscriboDocument.Lists[0].IsNumbered | Should Be $false
+ $pscriboDocument.Lists[0].BulletStyle | Should Be 'Disc'
+ }
+
+ It 'creates a numbered list' {
+ $null = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges' -Numbered
+ }
+
+ $pscriboDocument.Lists[0].IsNumbered | Should Be $true
+ }
+
+ It 'defaults to "Number" numbered list' {
+ $null = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges' -Numbered
+ }
+
+ $pscriboDocument.Lists[0].NumberStyle | Should Be 'Number'
+ $pscriboDocument.Lists[0].IsNumbered | Should Be $true
+ }
+
+ It 'inherits style by default' {
+ $null = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges' -Numbered
+ }
+
+ $pscriboDocument.Lists[0].IsStyleInherited | Should Be $true
+ }
+
+ It 'sets style when specified' {
+ $null = Section 'Test' {
+ List -Item 'Apples', 'Bananas', 'Oranges' -Style 'Caption'
+ }
+
+ $pscriboDocument.Lists[0].Style | Should Be 'Caption'
+ $pscriboDocument.Lists[0].IsStyleInherited | Should Be $false
+ $pscriboDocument.Lists[0].HasStyle | Should Be $true
+ }
+
+ It 'creates multi-level list' {
+ $null = Section 'Test' {
+ List {
+ Item 'Apples'
+ List -Item 'Jazz', 'Braeburn', 'Pink Lady'
+ Item 'Bananas'
+ Item 'Oranges'
+ List -Item 'Jaffa', 'Satsuma', 'Tangerine'
+ }
+ }
+
+ $pscriboDocument.Lists[0].IsMultiLevel | Should Be $true
+ $pscriboDocument.Lists[0].Items.Count | Should Be 5
+ $pscriboDocument.Lists[0].Items[1].Items.Count | Should Be 3
+ $pscriboDocument.Lists[0].Items[4].Items.Count | Should Be 3
+ }
+
+ It 'creates multi-level numbered and bulleted list' {
+ $null = Section 'Test' {
+ List -Numbered {
+ Item 'Apples'
+ List -Item 'Jazz', 'Braeburn', 'Pink Lady'
+ Item 'Bananas'
+ Item 'Oranges'
+ List -Item 'Jaffa', 'Satsuma', 'Tangerine'
+ }
+ }
+
+ $pscriboDocument.Lists[0].IsMultiLevel | Should Be $true
+ $pscriboDocument.Lists[0].IsNumbered | Should Be $true
+ $pscriboDocument.Lists[0].Items[1].IsNumbered | Should Be $false
+ $pscriboDocument.Lists[0].Items[4].IsNumbered | Should Be $false
+ }
+
+ }
+}
diff --git a/Tests/Unit/NumberStyle.Tests.ps1 b/Tests/Unit/NumberStyle.Tests.ps1
new file mode 100644
index 0000000..e36f261
--- /dev/null
+++ b/Tests/Unit/NumberStyle.Tests.ps1
@@ -0,0 +1,126 @@
+$here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent
+$testRoot = Split-Path -Path $here -Parent
+$moduleRoot = Split-Path -Path $testRoot -Parent
+Import-Module -Name "$moduleRoot\PScribo.psm1" -Force
+
+InModuleScope -ModuleName 'PScribo' -ScriptBlock {
+
+ Describe -Name 'NumberStyle' {
+
+ BeforeEach {
+
+ $pscriboDocument = Document -Name 'ScaffoldDocument' -ScriptBlock { }
+ }
+
+ It 'returns "PSCustomObject" object' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Number
+
+ $pscriboDocument.NumberStyles[$numberStyleName].GetType().Name | Should -eq 'PSCustomObject'
+ }
+
+ It 'defaults to "." suffix' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Number
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Suffix | Should -eq '.'
+ }
+
+ It 'defaults to "Right" alignment' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Number
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Align | Should -eq 'Right'
+ }
+
+ It 'defaults to "Lowercase"' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Number
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Uppercase | Should -eq $false
+ }
+
+ It 'creates "Letter" number style' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Letter
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Format | Should -eq 'Letter'
+ }
+
+ It 'creates "Roman" number style' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Roman
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Format | Should -eq 'Roman'
+ }
+
+ It 'creates "Custom" number style' {
+ $numberStyleName = 'Custom'
+ $customFormat = 'ABC%'
+
+ NumberStyle -Name $numberStyleName -Custom $customFormat
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Format | Should -eq 'Custom'
+ $pscriboDocument.NumberStyles[$numberStyleName].Custom | Should -eq $customFormat
+ }
+
+ It 'sets custom suffix' {
+ $numberStyleName = 'Custom'
+ $customSuffix = ':'
+
+ NumberStyle -Name $numberStyleName -Format Number -Suffix $customSuffix
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Suffix | Should -eq $customSuffix
+ }
+
+ It 'sets left alignment' {
+ $numberStyleName = 'Custom'
+ $customAlignment = 'Left'
+
+ NumberStyle -Name $numberStyleName -Format Number -Align $customAlignment
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Align | Should -eq $customAlignment
+ }
+
+ It 'sets uppercase text' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Number -Uppercase
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Uppercase | Should -eq $true
+ }
+
+ It 'sets Word indent level when specified' {
+ $numberStyleName = 'Custom'
+ $customIndent = 500
+
+ NumberStyle -Name $numberStyleName -Format Number -Indent $customIndent
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Indent | Should -eq $customIndent
+ }
+
+ It 'sets Word hanging level when specified' {
+ $numberStyleName = 'Custom'
+ $customHanging = 250
+
+ NumberStyle -Name $numberStyleName -Format Number -Hanging $customHanging
+
+ $pscriboDocument.NumberStyles[$numberStyleName].Hanging | Should -eq $customHanging
+ }
+
+ It 'sets default document number style' {
+ $numberStyleName = 'Custom'
+
+ NumberStyle -Name $numberStyleName -Format Number -Default
+
+ $pscriboDocument.DefaultNumberStyle | Should Be $numberStyleName
+ }
+
+ }
+}
diff --git a/Tests/Unit/Table.Tests.ps1 b/Tests/Unit/Table.Tests.ps1
index 59f1bab..8071432 100644
--- a/Tests/Unit/Table.Tests.ps1
+++ b/Tests/Unit/Table.Tests.ps1
@@ -250,6 +250,14 @@ InModuleScope 'PScribo' {
$table.ColumnWidths | Should BeNullOrEmpty
}
+ It 'warns with mismatching columns and column widths' {
+ $columnWidths = @(20,40,40)
+
+ $table = Table -Hashtable $services -List -Key 'Name' -ColumnWidths $columnWidths -WarningAction SilentlyContinue
+
+ $table.ColumnWidths | Should BeNullOrEmpty
+ }
+
It 'creates a table with specified column widths' {
[System.Collections.Specialized.OrderedDictionary[]] $services = @(
[ordered] @{ Name = 'TestService1'; ServiceName = 'Test 1'; DisplayName = 'Test Service 1'; }
diff --git a/en-US/PScribo.Resources.psd1 b/en-US/PScribo.Resources.psd1
index ce9cda4..379e7b0 100644
--- a/en-US/PScribo.Resources.psd1
+++ b/en-US/PScribo.Resources.psd1
@@ -14,6 +14,9 @@ OpenPackageError = Error opening package '{0}'. Ensure the file
IncorrectCharsInPathError = The incorrect char found in the Path.
HeaderFooterDocumentRootError = The 'Header' and 'Footer' keywords can only be defined in the document root section.
ParagraphRunRootError = The 'Text' keyword can only be defined within a 'Paragraph' section.
+ListRootError = The 'List' keyword can only be defined within a 'Paragraph' or 'Section' block.
+ItemRootError = The 'Item' keyword can only be defined within a 'List' section.
+InvalidCustomNumberStyleError = The custom number style '{0}' is invalid and must contain at least one '%'.
MaxHeadingLevelWarning = Html5 supports a maximum of 6 heading levels. Reduce the number of nested Document sections to remove the unsupported tags in the resulting Html output.
TableHeadersWithNoColumnsWarning = Table headers have been specified with no table columns/properties. Headers will be ignored.
@@ -35,6 +38,7 @@ FirstPageFooterOverwriteWarning = Existing first page footer definition overwri
DefaultFooterOverwriteWarning = Existing default page footer definition overwritten.
NoNewLineDeprecatedWarning = The '-NoNewLine' functionality has been deprecated. Use Paragraph runs (Text) to implement this functionality for all output formats.
ValueParameterRemovedWarning = The 'Paragraph -Value' functionality has been removed and is no longer implemented.
+NoPriorListItemWarning = No 'Item' defined before nested 'List'; nested list will be ignored.
DocumentProcessingStarted = Document '{0}' processing started.
DocumentInvokePlugin = Invoking '{0}' plugin.
@@ -81,4 +85,6 @@ ProcessingFooterCompleted = Processing document footer completed.
ProcessingParagraphRunsStarted = Processing paragraph run(s) started.
ProcessingParagraphRunsCompleted = Processing paragraph run(s) completed.
ProcessingParagraphRun = Processing paragraph run '{0}'.
+ProcessingList = Processing list '{0}'.
+ProcessingNumberStyle = Setting number style '{0}'.
'@;
diff --git a/en-US/about_HtmlPlugin.help.txt b/en-US/about_HtmlPlugin.help.txt
index e86ce17..6595fca 100644
--- a/en-US/about_HtmlPlugin.help.txt
+++ b/en-US/about_HtmlPlugin.help.txt
@@ -13,6 +13,10 @@ KNOWN LIMITATIONS
- Html output attempts to create a document-like experience. Paper sizes, page breaks and headers/footers are
rendered, but page numbers cannot be faithfully recreated.
+ - Html output does not support the 'Dash' bullet style. Dashes will be rendered using the the web broswer's
+ defaults.
+ - Html numbered lists only support the default '.' number style terminator/suffix. The use of custom number
+ style terminators/suffixes i.e. ')', is not supported.
PLUGIN OPTIONS
The Html plugin accepts the following output customisation options:
diff --git a/en-US/about_PScriboExamples.help.txt b/en-US/about_PScriboExamples.help.txt
index f37dd63..9114ec9 100644
--- a/en-US/about_PScriboExamples.help.txt
+++ b/en-US/about_PScriboExamples.help.txt
@@ -43,6 +43,12 @@ EXAMPLE INDEX
Example36.ps1 - 'Header' and 'Footer' paragraph styling
Example37.ps1 - 'Header' and 'Footer' tables
Example38.ps1 - 'Paragraph' text run styling
+ Example39.ps1 - 'List' single-level bullet lists
+ Example40.ps1 - 'List' single-level numbered lists
+ Example41.ps1 - 'List' and 'Item' multi-level bullet lists
+ Example42.ps1 - 'List' and 'Item' multi-level numbered lists
+ Example43.ps1 - 'NumberStyle' numbered list number formatting
+ Example44.ps1 - 'NumberStyle' and custom number formatting styles
SEE ALSO
about_PScribo
diff --git a/en-US/about_WordPlugin.help.txt b/en-US/about_WordPlugin.help.txt
index ce53c2d..61f2c87 100644
--- a/en-US/about_WordPlugin.help.txt
+++ b/en-US/about_WordPlugin.help.txt
@@ -20,6 +20,8 @@ KNOWN LIMITATIONS
implemented:
- Table of Contents are not rendered as the 'updateFields' flag is not implemented/supported
- Table cell font colors are not rendered correctly
+ - Word does not support a mixture of formats at the same level within a bullet/numbered list. Therefore, only the
+ first list type will be rendered.
PLUGIN OPTIONS
The Word plugin does not accept any output customisation options.