├── ExampleHtmlOutput.png ├── ExampleJsonOutput.png ├── ExampleTextOutput.png ├── ExampleWordOutput.png ├── PScriboExample.docx ├── Tests ├── TestImage.jpg ├── TestImage.png ├── Plugins │ ├── Html │ │ ├── Out-HtmlLineBreak.Tests.ps1 │ │ ├── Out-HtmlBlankLine.Tests.ps1 │ │ ├── Out-HtmlPageBreak.Tests.ps1 │ │ └── Out-HtmlStyle.Tests.ps1 │ ├── Text │ │ ├── Out-TextImage.Tests.ps1 │ │ ├── Out-TextBlankLine.Tests.ps1 │ │ ├── Out-TextLineBreak.Tests.ps1 │ │ └── Out-TextPageBreak.Tests.ps1 │ ├── Json │ │ ├── Out-JsonTOC.Tests.ps1 │ │ ├── Out-JsonDocument.Tests.ps1 │ │ ├── Out-JsonTable.Tests.ps1 │ │ └── Out-JsonSection.Tests.ps1 │ ├── Word │ │ ├── ConvertTo-WordColor.Tests.ps1 │ │ ├── Out-WordPageBreak.Tests.ps1 │ │ └── Out-WordBlankLine.Tests.ps1 │ └── Xml │ │ └── Out-XmlDocument.Tests.ps1 ├── Linting │ └── PSScriptAnalyzer.Tests.ps1 └── Unit │ ├── Resolve-ImageUri.Tests.ps1 │ ├── Get-UriBytes.Tests.ps1 │ ├── ConvertTo-Image.Tests.ps1 │ ├── Test-PScriboStyleColor.Tests.ps1 │ ├── BlankLine.Tests.ps1 │ ├── Get-PScriboImage.Tests.ps1 │ ├── LineBreak.Tests.ps1 │ ├── PageBreak.Tests.ps1 │ ├── Image.Tests.ps1 │ └── TOC.Tests.ps1 ├── Examples ├── Example31.jpg ├── Example19.ps1 ├── Example14.ps1 ├── Example11.ps1 ├── Example20.ps1 ├── Example10.ps1 ├── Example22.ps1 ├── Example18.ps1 ├── Example24.ps1 ├── Example12.ps1 ├── Example13.ps1 ├── Example40.ps1 ├── Example07.ps1 ├── Example05.ps1 ├── Example44.ps1 ├── Example02.ps1 ├── Example30.ps1 ├── Example25.ps1 ├── Example17.ps1 ├── Example33.ps1 ├── Example23.ps1 ├── Example15.ps1 ├── Example03.ps1 ├── Example01.ps1 ├── Example28.ps1 ├── Example27.ps1 ├── Example16.ps1 ├── Example21.ps1 ├── Example29.ps1 ├── Example36.ps1 ├── Example06.ps1 ├── Example41.ps1 ├── Example42.ps1 ├── Example04.ps1 ├── Example39.ps1 └── Example34.ps1 ├── Src ├── Plugins │ ├── Html │ │ ├── Out-HtmlLineBreak.ps1 │ │ ├── Out-HtmlBlankLine.ps1 │ │ ├── New-PScriboHtmlOption.ps1 │ │ ├── Out-HtmlTable.ps1 │ │ ├── Get-HtmlCanvasHeight.ps1 │ │ ├── Out-HtmlImage.ps1 │ │ ├── Get-HtmlTableCaption.ps1 │ │ ├── Get-HtmlTablePaddingStyle.ps1 │ │ ├── Get-HtmlTableColGroup.ps1 │ │ ├── Out-HtmlTOC.ps1 │ │ ├── Get-HtmlTableStyle.ps1 │ │ ├── Get-HtmlTableDiv.ps1 │ │ ├── Get-HtmlListItemInlineStyle.ps1 │ │ ├── Out-HtmlPageBreak.ps1 │ │ └── Get-HtmlStyle.ps1 │ ├── Json │ │ ├── Out-JsonTOC.ps1 │ │ ├── Get-JsonTableCaption.ps1 │ │ ├── New-PScriboJsonOption.ps1 │ │ ├── Out-JsonTable.ps1 │ │ └── Out-JsonParagraph.ps1 │ ├── Word │ │ ├── ConvertTo-WordColor.ps1 │ │ ├── Out-WordBlankLine.ps1 │ │ ├── Out-WordPageBreak.ps1 │ │ ├── Get-WordListLevel.ps1 │ │ ├── Out-WordLineBreak.ps1 │ │ ├── Get-WordSettingsDocument.ps1 │ │ ├── Get-WordParagraphRun.ps1 │ │ ├── Get-WordTableConditionalFormattingValue.ps1 │ │ ├── Get-WordStyleRunPr.ps1 │ │ ├── Get-WordNumberingDocument.ps1 │ │ └── Get-WordTableRenderWidthMm.ps1 │ ├── Text │ │ ├── Out-TextBlankLine.ps1 │ │ ├── Out-TextLineBreak.ps1 │ │ ├── Out-TextPageBreak.ps1 │ │ ├── Out-Textimage.ps1 │ │ ├── Get-PScriboListItemMaximumLength.ps1 │ │ ├── Get-TextTableCaption.ps1 │ │ ├── ConvertTo-IndentedString.ps1 │ │ ├── ConvertTo-JustifiedString.ps1 │ │ ├── Out-TextParagraph.ps1 │ │ ├── Out-TextTOC.ps1 │ │ └── New-PScriboTextOption.ps1 │ └── Xml │ │ ├── Out-XmlImage.ps1 │ │ ├── Out-XmlParagraph.ps1 │ │ └── Out-XmlTable.ps1 ├── Private │ ├── Get-PScriboPlugin.ps1 │ ├── ConvertTo-In.ps1 │ ├── Test-PScriboStyle.ps1 │ ├── ConvertTo-Pt.ps1 │ ├── Test-PScriboStyleColor.ps1 │ ├── ConvertTo-Twips.ps1 │ ├── Resolve-PScriboToken.ps1 │ ├── ConvertTo-Octips.ps1 │ ├── ConvertTo-Px.ps1 │ ├── Resolve-ImageUri.ps1 │ ├── ConvertTo-Image.ps1 │ ├── ConvertTo-InvariantCultureString.ps1 │ ├── Invoke-PScriboSection.ps1 │ ├── New-PScriboBlankLine.ps1 │ ├── New-PScriboLineBreak.ps1 │ ├── Get-PScriboDocumentStyle.ps1 │ ├── ConvertTo-Em.ps1 │ ├── New-PScriboPageBreak.ps1 │ ├── ConvertTo-PScriboPreformattedTable.ps1 │ ├── Set-PScriboSectionBreakEnd.ps1 │ ├── Write-PScriboProcessSectionId.ps1 │ ├── Get-UriBytes.ps1 │ ├── New-PScriboListReference.ps1 │ ├── Copy-Object.ps1 │ ├── New-PScriboTOC.ps1 │ ├── ConvertTo-Mm.ps1 │ ├── ConvertTo-RomanNumeral.ps1 │ ├── New-PScriboFormattedTableRowCell.ps1 │ ├── Get-PScriboImage.ps1 │ ├── ConvertTo-PSObjectKeyedListTable.ps1 │ ├── Invoke-PScriboSectionLevel.ps1 │ ├── New-PScriboHeaderFooter.ps1 │ ├── Merge-PScriboPluginOption.ps1 │ ├── Invoke-PScriboListLevel.ps1 │ ├── Get-PScriboHeaderFooter.ps1 │ ├── Get-ImageMimeType.ps1 │ ├── New-PScriboFormattedTableRow.ps1 │ ├── ConvertFrom-NumberStyle.ps1 │ └── New-PScriboParagraph.ps1 └── Public │ ├── BlankLine.ps1 │ ├── PageBreak.ps1 │ ├── LineBreak.ps1 │ ├── TOC.ps1 │ └── Set-Style.ps1 ├── en-US ├── about_PScriboPlugins.help.txt ├── about_HtmlPlugin.help.txt ├── about_WordPlugin.help.txt ├── about_PScribo.help.txt └── about_JsonPlugin.help.txt ├── Pscribo.nuspec ├── LICENSE ├── PScribo.psm1 └── PScribo.psd1 /ExampleHtmlOutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/ExampleHtmlOutput.png -------------------------------------------------------------------------------- /ExampleJsonOutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/ExampleJsonOutput.png -------------------------------------------------------------------------------- /ExampleTextOutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/ExampleTextOutput.png -------------------------------------------------------------------------------- /ExampleWordOutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/ExampleWordOutput.png -------------------------------------------------------------------------------- /PScriboExample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/PScriboExample.docx -------------------------------------------------------------------------------- /Tests/TestImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/Tests/TestImage.jpg -------------------------------------------------------------------------------- /Tests/TestImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/Tests/TestImage.png -------------------------------------------------------------------------------- /Examples/Example31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iainbrighton/PScribo/HEAD/Examples/Example31.jpg -------------------------------------------------------------------------------- /Src/Plugins/Html/Out-HtmlLineBreak.ps1: -------------------------------------------------------------------------------- 1 | function Out-HtmlLineBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Html line break. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param ( ) 10 | process 11 | { 12 | return '
' 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/Private/Get-PScriboPlugin.ps1: -------------------------------------------------------------------------------- 1 | function Get-PScriboPlugin 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns available PScribo plugins. 6 | #> 7 | [CmdletBinding()] 8 | param ( ) 9 | process 10 | { 11 | Get-ChildItem -Path (Join-Path -Path $pscriboRoot -ChildPath '\Src\Plugins') | 12 | Select-Object -ExpandProperty Name 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Src/Public/BlankLine.ps1: -------------------------------------------------------------------------------- 1 | function BlankLine { 2 | <# 3 | .SYNOPSIS 4 | Initializes a new PScribo blank line object. 5 | #> 6 | [CmdletBinding()] 7 | param 8 | ( 9 | [Parameter(ValueFromPipeline, Position = 0)] 10 | [System.UInt32] $Count = 1 11 | ) 12 | process 13 | { 14 | Write-PScriboMessage -Message $localized.ProcessingBlankLine 15 | return (New-PScriboBlankLine @PSBoundParameters) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-In.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-In 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert millimeters into inches 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Single])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [Alias('mm','Millimetre')] 13 | [System.Single] $Millimeter 14 | ) 15 | process 16 | { 17 | $in = $Millimeter / 25.4 18 | return [System.Math]::Round($in, 2) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Src/Private/Test-PScriboStyle.ps1: -------------------------------------------------------------------------------- 1 | function Test-PScriboStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Tests whether a style has been defined. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Boolean])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline, Position = 0)] 12 | [ValidateNotNullOrEmpty()] 13 | [System.String] $Name 14 | ) 15 | process 16 | { 17 | return $PScriboDocument.Styles.ContainsKey($Name) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /en-US/about_PScriboPlugins.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | PScribo Plugins 3 | 4 | SYNOPSIS 5 | PScribo supports outputting to multiple document formats. 6 | 7 | DESCRIPTION 8 | PScribo can output plain text, Json, Html (web) and Microsoft Word documents via plugins. Each format may or may not 9 | support various PScribo document options. Refer to each specific plugin help topic for more information. 10 | 11 | SEE ALSO 12 | about_HtmlPlugin 13 | about_TextPlugin 14 | about_JsonPlugin 15 | about_WordPlugin 16 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Pt.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Pt 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert millimeters into points 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Single])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [Alias('mm','Millimetre')] 13 | [System.Single] $Millimeter 14 | ) 15 | process 16 | { 17 | $pt = (ConvertTo-In -Millimeter $Millimeter) / 0.0138888888888889 18 | return [System.Math]::Round($pt, 2) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Plugins/Html/Out-HtmlLineBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Html\Out-HtmlLineBreak' { 10 | 11 | It 'creates a
html tag' { 12 | Out-HtmlLineBreak | Should BeExactly '
' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Src/Public/PageBreak.ps1: -------------------------------------------------------------------------------- 1 | function PageBreak { 2 | <# 3 | .SYNOPSIS 4 | Creates a PScribo page break object. 5 | #> 6 | [CmdletBinding()] 7 | [OutputType([System.Management.Automation.PSCustomObject])] 8 | param 9 | ( 10 | [Parameter(Position = 0)] 11 | [ValidateNotNullOrEmpty()] 12 | [System.String] $Id = [System.Guid]::NewGuid().ToString() 13 | ) 14 | process 15 | { 16 | Write-PScriboMessage -Message $localized.ProcessingPageBreak 17 | return (New-PScriboPageBreak -Id $Id) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Src/Public/LineBreak.ps1: -------------------------------------------------------------------------------- 1 | function LineBreak { 2 | <# 3 | .SYNOPSIS 4 | Initializes a new PScribo line break object. 5 | #> 6 | [CmdletBinding()] 7 | [OutputType([System.Management.Automation.PSCustomObject])] 8 | param 9 | ( 10 | [Parameter(Position = 0)] 11 | [ValidateNotNullOrEmpty()] 12 | [System.String] $Id = [System.Guid]::NewGuid().ToString() 13 | ) 14 | process 15 | { 16 | Write-PScriboMessage -Message $localized.ProcessingLineBreak 17 | return (New-PScriboLineBreak @PSBoundParameters) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Linting/PSScriptAnalyzer.Tests.ps1: -------------------------------------------------------------------------------- 1 | #requires -Version 4 2 | #requires -Modules PSScriptAnalyzer 3 | 4 | $repoRoot = (Resolve-Path "$PSScriptRoot\..\..").Path; 5 | Describe 'Linting\PSScriptAnalyzer' { 6 | 7 | Get-ChildItem -Path "$repoRoot\Src\*.ps*1" -Recurse -File | ForEach-Object { 8 | It "File '$($_.FullName.Replace($repoRoot,''))' passes PSScriptAnalyzer rules" { 9 | $result = Invoke-ScriptAnalyzer -Path $_.FullName -Severity Warning | Select-Object -ExpandProperty Message 10 | $result | Should BeNullOrEmpty 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Src/Plugins/Json/Out-JsonTOC.ps1: -------------------------------------------------------------------------------- 1 | function Out-JsonTOC { 2 | <# 3 | .SYNOPSIS 4 | Output formatted Table of Contents 5 | #> 6 | begin { 7 | ## Initializing TOC object 8 | $tocBuilder = [ordered]@{} 9 | } 10 | process { 11 | ## Populating TOC 12 | ## Disregarding section numbering as its highly beneficial when parsing JSON after the fact 13 | foreach ($tocEntry in $Document.TOC) { 14 | [ref] $null = $tocBuilder.Add($tocEntry.Number, $tocEntry.Name) 15 | } 16 | 17 | return ($tocBuilder) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Unit/Resolve-ImageUri.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent 2 | $testRoot = Split-Path -Path $here -Parent 3 | $moduleRoot = Split-Path -Path $testRoot -Parent 4 | Import-Module -Name "$moduleRoot\PScribo.psm1" -Force 5 | 6 | InModuleScope -ModuleName 'PScribo' -ScriptBlock { 7 | 8 | Describe -Name 'Resolve-ImageUri' -Fixture { 9 | 10 | It "returns 'System.Uri' type" { 11 | $testPath = 'about:Blank' 12 | 13 | $result = Resolve-ImageUri -Path $testPath 14 | 15 | $result -is [System.Uri] | Should Be $true 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Src/Plugins/Word/ConvertTo-WordColor.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-WordColor 2 | { 3 | <# 4 | .SYNOPSIS 5 | Converts an HTML color to RRGGBB value as Word does not support short Html color codes 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline)] 11 | [System.String] $Color 12 | ) 13 | process 14 | { 15 | $Color = $Color.TrimStart('#') 16 | if ($Color.Length -eq 3) 17 | { 18 | $Color = '{0}{0}{1}{1}{2}{2}' -f $Color[0], $Color[1], $Color[2] 19 | } 20 | 21 | return $Color.ToUpper() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/Private/Test-PScriboStyleColor.ps1: -------------------------------------------------------------------------------- 1 | function Test-PScriboStyleColor 2 | { 3 | <# 4 | .SYNOPSIS 5 | Tests whether a color string is a valid HTML color. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Boolean])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline, Position = 0)] 12 | [ValidateNotNullOrEmpty()] 13 | [System.String] $Color 14 | ) 15 | process 16 | { 17 | if (Resolve-PScriboStyleColor -Color $Color) 18 | { 19 | return $true 20 | } 21 | else 22 | { 23 | return $false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Twips.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Twips 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert millimeters into twips 6 | 7 | .NOTES 8 | 1 twip = 1/20th pt 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] 12 | [OutputType([System.Single])] 13 | param 14 | ( 15 | [Parameter(Mandatory, ValueFromPipeline)] 16 | [Alias('mm','Millimetre')] 17 | [System.Single] $Millimeter 18 | ) 19 | process 20 | { 21 | $twips = (ConvertTo-In -Millimeter $Millimeter) * 1440 22 | return [System.Math]::Round($twips, 2) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Src/Private/Resolve-PScriboToken.ps1: -------------------------------------------------------------------------------- 1 | function Resolve-PScriboToken 2 | { 3 | <# 4 | .SYNOPSIS 5 | Replaces page number tokens with their (approximated) values. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 11 | [AllowEmptyString()] 12 | [System.String] $InputObject 13 | ) 14 | process 15 | { 16 | $tokenisedText = $InputObject -replace 17 | '', $script:currentPageNumber -replace 18 | '', $Document.Properties.Pages 19 | return $tokenisedText 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Octips.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Octips 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert millimeters into octips 6 | 7 | .NOTES 8 | 1 "octip" = 1/8th pt 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] 12 | [OutputType([System.Single])] 13 | param 14 | ( 15 | [Parameter(Mandatory, ValueFromPipeline)] 16 | [Alias('mm','Millimetre')] 17 | [System.Single] $Millimeter 18 | ) 19 | process 20 | { 21 | $octips = (ConvertTo-In -Millimeter $Millimeter) * 576 22 | return [System.Math]::Round($octips, 2) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Out-TextBlankLine.ps1: -------------------------------------------------------------------------------- 1 | function Out-TextBlankLine 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted text blankline. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $BlankLine 13 | ) 14 | process 15 | { 16 | $blankLineBuilder = New-Object -TypeName System.Text.StringBuilder 17 | for ($i = 0; $i -lt $BlankLine.LineCount; $i++) 18 | { 19 | [ref] $null = $blankLineBuilder.AppendLine() 20 | } 21 | return $blankLineBuilder.ToString() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Out-HtmlBlankLine.ps1: -------------------------------------------------------------------------------- 1 | function Out-HtmlBlankLine 2 | { 3 | <# 4 | .SYNOPSIS 5 | Outputs html PScribo.Blankline. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $BlankLine 13 | ) 14 | process 15 | { 16 | $blankLineBuilder = New-Object -TypeName System.Text.StringBuilder 17 | 18 | for ($i = 0; $i -lt $BlankLine.LineCount; $i++) 19 | { 20 | [ref] $null = $blankLineBuilder.Append('
') 21 | } 22 | 23 | return $blankLineBuilder.ToString() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/Plugins/Json/Get-JsonTableCaption.ps1: -------------------------------------------------------------------------------- 1 | function Get-JsonTableCaption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates caption from a PScribo.Table object. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Table 14 | ) 15 | process 16 | { 17 | $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style 18 | 19 | return [PSCustomObject] @{ 20 | Caption = ('{0} {1} {2}' -f $tableStyle.CaptionPrefix, $Table.CaptionNumber, $Table.Caption) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/Public/TOC.ps1: -------------------------------------------------------------------------------- 1 | function TOC { 2 | <# 3 | .SYNOPSIS 4 | Initializes a new PScribo Table of Contents (TOC) object. 5 | #> 6 | [CmdletBinding()] 7 | [OutputType([System.Management.Automation.PSCustomObject])] 8 | param 9 | ( 10 | [Parameter(ValueFromPipeline)] 11 | [ValidateNotNullOrEmpty()] 12 | [System.String] $Name = 'Contents', 13 | 14 | [Parameter(ValueFromPipelineByPropertyName)] 15 | [ValidateNotNullOrEmpty()] 16 | [System.String] $ClassId = 'TOC' 17 | ) 18 | process 19 | { 20 | Write-PScriboMessage -Message ($localized.ProcessingTOC -f $Name) 21 | return (New-PScriboTOC @PSBoundParameters) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Out-TextLineBreak.ps1: -------------------------------------------------------------------------------- 1 | function Out-TextLineBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted line break text. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param ( ) 10 | begin 11 | { 12 | ## Fix Set-StrictMode 13 | if (-not (Test-Path -Path Variable:\Options)) 14 | { 15 | $options = New-PScriboTextOption 16 | } 17 | } 18 | process 19 | { 20 | $convertToAlignedStringParams = @{ 21 | InputObject = ''.PadRight($options.SeparatorWidth, $options.LineBreakSeparator) 22 | Width = $options.TextWidth 23 | } 24 | return (ConvertTo-AlignedString @convertToAlignedStringParams) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/Plugins/Html/New-PScriboHtmlOption.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboHtmlOption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sets the text plugin specific formatting/output options. 6 | .NOTES 7 | All plugin options should be prefixed with the plugin name. 8 | #> 9 | [CmdletBinding()] 10 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 11 | [OutputType([System.Collections.Hashtable])] 12 | param 13 | ( 14 | [Parameter(ValueFromPipelineByPropertyName)] 15 | [ValidateNotNull()] 16 | [System.Boolean] $NoPageLayoutStyle = $false 17 | ) 18 | process 19 | { 20 | return @{ 21 | NoPageLayoutStyle = $NoPageLayoutStyle 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Px.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Px 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert millimeters into pixels (default 96dpi) 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Int16])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [Alias('mm','Millimetre')] 13 | [System.Single] $Millimeter, 14 | 15 | [Parameter(ValueFromPipelineByPropertyName)] 16 | [System.Int16] $Dpi = 96 17 | ) 18 | process 19 | { 20 | $px = [System.Int16] ((ConvertTo-In -Millimeter $Millimeter) * $Dpi) 21 | if ($px -lt 1) 22 | { 23 | return (1 -as [System.Int16]) 24 | } 25 | else 26 | { 27 | return $px 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Plugins/Text/Out-TextImage.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Text\Out-TextImage' { 10 | 11 | It 'adds alttext to image' { 12 | $testImage = [PSCustomObject] @{ 13 | Text = 'Dummy Image' 14 | } 15 | $expected = '\[Image Text="{0}"\]' -f $testImage.Text 16 | 17 | $result = Out-TextImage -Image $testImage 18 | 19 | $result -match $expected | Should Be $true 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Src/Plugins/Json/New-PScriboJsonOption.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboJsonOption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sets the Json plugin specific formatting/output options. 6 | 7 | .NOTES 8 | All plugin options should be prefixed with the plugin name. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [OutputType([System.Collections.Hashtable])] 13 | param 14 | ( 15 | ## Text encoding 16 | [Parameter(ValueFromPipelineByPropertyName)] 17 | [ValidateSet('ASCII','Unicode','UTF7','UTF8')] 18 | [System.String] $Encoding = 'ASCII' 19 | ) 20 | process 21 | { 22 | return @{ 23 | Encoding = $Encoding 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/Private/Resolve-ImageUri.ps1: -------------------------------------------------------------------------------- 1 | function Resolve-ImageUri 2 | { 3 | <# 4 | .SYNOPSIS 5 | Converts an image path into a Uri. 6 | 7 | .NOTES 8 | A Uri includes information about whether the path is local etc. This is useful for plugins 9 | to be able to determine whether to embed images or not. 10 | #> 11 | [CmdletBinding()] 12 | [OutputType([System.Uri])] 13 | param 14 | ( 15 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 16 | [System.String] $Path 17 | ) 18 | process 19 | { 20 | if (Test-Path -Path $Path) 21 | { 22 | $Path = Resolve-Path -Path $Path 23 | } 24 | $uri = New-Object -TypeName System.Uri -ArgumentList @($Path) 25 | return $uri 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Out-HtmlTable.ps1: -------------------------------------------------------------------------------- 1 | function Out-HtmlTable 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Html from PScribo.Table object. 6 | 7 | .NOTES 8 | One table is output per table row with the -List parameter. 9 | #> 10 | [CmdletBinding()] 11 | [OutputType([System.String])] 12 | param 13 | ( 14 | [Parameter(Mandatory, ValueFromPipeline)] 15 | [ValidateNotNull()] 16 | [System.Management.Automation.PSObject] $Table 17 | ) 18 | process 19 | { 20 | [System.Text.StringBuilder] $tableBuilder = New-Object -TypeName 'System.Text.StringBuilder' 21 | $formattedTable = Get-HtmlTable -Table $Table 22 | [ref] $null = $tableBuilder.Append($formattedTable) 23 | return $tableBuilder.ToString() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Out-WordBlankLine.ps1: -------------------------------------------------------------------------------- 1 | function Out-WordBlankLine 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Word xml blank line (empty paragraph). 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline)] 11 | [System.Management.Automation.PSObject] $BlankLine, 12 | 13 | [Parameter(Mandatory)] 14 | [System.Xml.XmlDocument] $XmlDocument, 15 | 16 | [Parameter(Mandatory)] 17 | [System.Xml.XmlElement] $Element 18 | ) 19 | process 20 | { 21 | $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' 22 | for ($i = 0; $i -lt $BlankLine.LineCount; $i++) { 23 | [ref] $null = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/Plugins/Json/Out-JsonTable.ps1: -------------------------------------------------------------------------------- 1 | function Out-JsonTable 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Json table. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $Table 13 | ) 14 | begin 15 | { 16 | ## Initializing table object 17 | $tableBuilder = [System.Collections.ArrayList]::new() 18 | } 19 | process 20 | { 21 | if ($Table.HasCaption) 22 | { 23 | [ref] $null = $tableBuilder.Add((Get-JsonTableCaption -Table $Table)) 24 | } 25 | 26 | [ref] $null = $tableBuilder.Add(($Table.Rows | Select-Object -Property * -ExcludeProperty '*__Style')) 27 | 28 | return $tableBuilder 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Src/Plugins/Xml/Out-XmlImage.ps1: -------------------------------------------------------------------------------- 1 | function Out-XmlImage 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output embedded Xml image. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | ## PScribo Image object 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Image 14 | ) 15 | process 16 | { 17 | $imageElement = $xmlDocument.CreateElement('image') 18 | [ref] $null = $imageElement.SetAttribute('text', $Image.Text) 19 | [ref] $null = $imageElement.SetAttribute('mimeType', $Image.MimeType) 20 | $imageBase64 = [System.Convert]::ToBase64String($Image.Bytes) 21 | [ref] $null = $imageElement.AppendChild($xmlDocument.CreateTextNode($imageBase64)) 22 | 23 | return $imageElement 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlCanvasHeight.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlCanvasHeight 2 | { 3 | <# 4 | .SYNOPSIS 5 | Calculates the usable document canvas height. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory)] 11 | [System.String] $Orientation 12 | ) 13 | process 14 | { 15 | if ($Orientation -eq 'Portrait') 16 | { 17 | $pageHeight = $Document.Options['PageHeight'] 18 | return ($pageHeight - $Document.Options['MarginTop'] - $Document.Options['MarginBottom']) -as [System.Int32] 19 | } 20 | else 21 | { 22 | $pageHeight = $Document.Options['PageWidth'] 23 | return ($pageHeight - $Document.Options['MarginTop'] - (($Document.Options['MarginBottom'] * 1.5) -as [System.Int32])) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Unit/Get-UriBytes.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent 2 | $testRoot = Split-Path -Path $here -Parent 3 | $moduleRoot = Split-Path -Path $testRoot -Parent 4 | Import-Module -Name "$moduleRoot\PScribo.psm1" -Force 5 | 6 | InModuleScope -ModuleName 'PScribo' -ScriptBlock { 7 | 8 | $testRoot = Split-Path -Path $PSScriptRoot -Parent; 9 | 10 | Describe -Name 'Get-UriBytes' -Fixture { 11 | 12 | It "returns 'System.Byte[]' type" { 13 | 14 | $testPath = Join-Path -Path $testRoot -ChildPath 'TestImage.jpg' 15 | $testUri = Resolve-ImageUri -Path $testPath 16 | 17 | $result = Get-UriBytes -Uri $testUri 18 | 19 | $result -is [System.Object[]] | Should Be $true 20 | $result[0] -is [System.Byte] | Should Be $true 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Out-TextPageBreak.ps1: -------------------------------------------------------------------------------- 1 | function Out-TextPageBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted line break text. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param ( ) 10 | process 11 | { 12 | $pagebreakBuilder = New-Object -TypeName System.Text.StringBuilder 13 | 14 | $isFirstPage = $currentPageNumber -eq 1 15 | $pageFooter = Out-TextHeaderFooter -Footer -FirstPage:$isFirstPage 16 | [ref] $null = $pageBreakBuilder.Append($pageFooter) 17 | 18 | $script:currentPageNumber++ 19 | [ref] $null = $pagebreakBuilder.Append((Out-TextLineBreak)).AppendLine() 20 | 21 | $pageHeader = Out-TextHeaderFooter -Header 22 | [ref] $null = $pageBreakBuilder.Append($pageHeader) 23 | 24 | return $pageBreakBuilder.ToString() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Plugins/Html/Out-HtmlBlankLine.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Html\Out-HtmlBlankLine' { 10 | 11 | ## Scaffold new document to initialise options/styles 12 | $pscriboDocument = Document -Name 'Test' -ScriptBlock { }; 13 | 14 | It 'creates a single
html tag' { 15 | BlankLine | Out-HtmlBlankLine | Should BeExactly '
' 16 | } 17 | 18 | It 'creates two
html tags' { 19 | BlankLine -Count 2 | Out-HtmlBlankLine | Should BeExactly '

' 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Tests/Unit/ConvertTo-Image.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent 2 | $testRoot = Split-Path -Path $here -Parent 3 | $moduleRoot = Split-Path -Path $testRoot -Parent 4 | Import-Module -Name "$moduleRoot\PScribo.psm1" -Force 5 | 6 | InModuleScope -ModuleName 'PScribo' -ScriptBlock { 7 | 8 | $testRoot = Split-Path -Path $PSScriptRoot -Parent 9 | 10 | Describe -Name 'ConvertTo-Image' -Fixture { 11 | 12 | It "returns 'System.Drawing.Image' type" { 13 | $testPath = Join-Path -Path $testRoot -ChildPath 'TestImage.jpg' 14 | $testUri = Resolve-ImageUri -Path $testPath 15 | $testBytes = Get-UriBytes -Uri $testUri 16 | 17 | $result = ConvertTo-Image -Bytes $testBytes 18 | 19 | $result -is [System.Drawing.Image] | Should Be $true 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Out-Textimage.ps1: -------------------------------------------------------------------------------- 1 | function Out-TextImage 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted image text. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Image 14 | ) 15 | begin 16 | { 17 | ## Fix Set-StrictMode 18 | if (-not (Test-Path -Path Variable:\Options)) 19 | { 20 | $options = New-PScriboTextOption 21 | } 22 | } 23 | process 24 | { 25 | $convertToAlignedStringParams = @{ 26 | InputObject = '[Image Text="{0}"]' -f $Image.Text 27 | Width = $options.TextWidth 28 | } 29 | 30 | return (ConvertTo-AlignedString @convertToAlignedStringParams) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Image.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Image 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates an image from a byte[] 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Drawing.Image])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Byte[]] $Bytes 13 | ) 14 | process 15 | { 16 | try 17 | { 18 | [System.IO.MemoryStream] $memoryStream = New-Object -TypeName 'System.IO.MemoryStream' -ArgumentList @(,$Bytes) 19 | [System.Drawing.Image] $image = [System.Drawing.Image]::FromStream($memoryStream) 20 | Write-Output -InputObject $image 21 | } 22 | catch 23 | { 24 | $_ 25 | } 26 | finally 27 | { 28 | if ($null -ne $memoryStream) 29 | { 30 | $memoryStream.Close() 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-InvariantCultureString.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-InvariantCultureString 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert to a number to a string with a culture-neutral representation #6, #42. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | ## The sinle/double 11 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 12 | [System.Object] $Object, 13 | 14 | ## Format string 15 | [Parameter(ValueFromPipelineByPropertyName)] 16 | [System.String] $Format 17 | ) 18 | process 19 | { 20 | if ($PSBoundParameters.ContainsKey('Format')) 21 | { 22 | return $Object.ToString($Format, [System.Globalization.CultureInfo]::InvariantCulture); 23 | } 24 | else 25 | { 26 | return $Object.ToString([System.Globalization.CultureInfo]::InvariantCulture); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/Private/Invoke-PScriboSection.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PScriboSection 2 | { 3 | <# 4 | .SYNOPSIS 5 | Processes the document/TOC section versioning each level, i.e. 1.2.2.3 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | param ( ) 12 | process 13 | { 14 | $majorNumber = 1; 15 | foreach ($s in $pscriboDocument.Sections) 16 | { 17 | if ($s.Type -like '*.Section') 18 | { 19 | if ($pscriboDocument.Options['ForceUppercaseSection']) 20 | { 21 | $s.Name = $s.Name.ToUpper(); 22 | } 23 | if (-not $s.IsExcluded) 24 | { 25 | Invoke-PScriboSectionLevel -Section $s -Number $majorNumber; 26 | $majorNumber++; 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboBlankLine.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboBlankLine 2 | { 3 | <# 4 | .SYNOPSIS 5 | Initializes a new PScribo blank line break. 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [OutputType([System.Management.Automation.PSCustomObject])] 13 | param 14 | ( 15 | [Parameter(ValueFromPipeline)] 16 | [System.UInt32] $Count = 1 17 | ) 18 | process 19 | { 20 | $typeName = 'PScribo.BlankLine'; 21 | $pscriboDocument.Properties['BlankLines']++; 22 | $pscriboBlankLine = [PSCustomObject] @{ 23 | Id = [System.Guid]::NewGuid().ToString(); 24 | LineCount = $Count; 25 | Type = $typeName; 26 | } 27 | return $pscriboBlankLine; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboLineBreak.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboLineBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Initializes a new PScribo line break object. 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [OutputType([System.Management.Automation.PSCustomObject])] 13 | param 14 | ( 15 | [Parameter(Position = 0)] 16 | [ValidateNotNullOrEmpty()] 17 | [System.String] $Id = [System.Guid]::NewGuid().ToString() 18 | ) 19 | process 20 | { 21 | $typeName = 'PScribo.LineBreak'; 22 | $pscriboDocument.Properties['LineBreaks']++; 23 | $pscriboLineBreak = [PSCustomObject] @{ 24 | Id = $Id; 25 | Type = $typeName; 26 | } 27 | return $pscriboLineBreak; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/Private/Get-PScriboDocumentStyle.ps1: -------------------------------------------------------------------------------- 1 | function Get-PScriboDocumentStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns document style or table style. 6 | 7 | .NOTES 8 | Enables testing without having to generate a mock document object! 9 | #> 10 | [CmdletBinding()] 11 | param 12 | ( 13 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Style')] 14 | [System.String] $Style, 15 | 16 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'TableStyle')] 17 | [System.String] $TableStyle 18 | ) 19 | process 20 | { 21 | if ($PSCmdlet.ParameterSetName -eq 'Style') 22 | { 23 | return $Document.Styles[$Style] 24 | } 25 | elseif ($PSCmdlet.ParameterSetName -eq 'TableStyle') 26 | { 27 | return $Document.TableStyles[$TableStyle] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Em.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Em 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert pixels or millimeters into EMU 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Single])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Pixel')] 12 | [Alias('px')] 13 | [System.Single] $Pixel, 14 | 15 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Millimeter')] 16 | [Alias('mm','Millimetre')] 17 | [System.Single] $Millimeter 18 | ) 19 | process 20 | { 21 | if ($PSCmdlet.ParameterSetName -eq 'Pixel') 22 | { 23 | $em = $pixel * 9525 24 | return [System.Math]::Round($em, 0) 25 | } 26 | elseif ($PSCmdlet.ParameterSetName -eq 'Millimeter') 27 | { 28 | $em = $Millimeter / 4.23333333333333 29 | return [System.Math]::Round($em, 2) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Examples/Example19.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example19 = Document -Name 'PScribo Example 19' { 11 | 12 | <# 13 | Column widths can also be specified on -List tables. The total of the 14 | column widths must still total 100 (%), but only 2 widths are supported. 15 | 16 | NOTE: -List view also supports the standard 'Table' -Width parameter. 17 | 18 | The following example creates a table for the first 5 services 19 | registered on the local machine, and sets the first (property) column 20 | to 40% and the second (value) column to 60%. 21 | #> 22 | Get-Service | Select-Object -First 5 | Table -List -ColumnWidths 40,60 23 | } 24 | $example19 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 25 | -------------------------------------------------------------------------------- /Examples/Example14.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example14 = Document -Name 'PScribo Example 14' { 11 | 12 | <# 13 | By default, tables are configured to use 100% of available width. You can 14 | override the table width, by specifying the -Width parameter on the 'Table' 15 | cmdlet. The width is always set as a percentage of available space between 16 | the left and right page margins (adjusting for indentation). 17 | 18 | The following example creates a table with its with set at 66% of the available 19 | space. 20 | #> 21 | Get-Service | Table -Columns Name,DisplayName,Status -Headers 'Name','Display Name','State' -Width 66 22 | } 23 | $example14 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 24 | -------------------------------------------------------------------------------- /Pscribo.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pscribo 5 | PScribo 6 | 0.0.0 7 | Iain Brighton 8 | Virtual Engine 9 | PScribo documentation Powershell module/framework. 10 | PScribo is a documentation domain-specific language (DSL) for PowerShell, used to create a 'document' in a standardised format. 11 | http://github.com/iainbrighton/PScribo 12 | (c) 2025 Iain Brighton. All rights reserved. 13 | MIT 14 | false 15 | VirtualEngine PScribo Powershell Documentation Framework Virtual Engine Word Html 16 | 17 | 18 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboPageBreak.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboPageBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a PScribo page break object. 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [OutputType([System.Management.Automation.PSCustomObject])] 13 | param 14 | ( 15 | [Parameter(Position = 0)] 16 | [ValidateNotNullOrEmpty()] 17 | [System.String] $Id = [System.Guid]::NewGuid().ToString() 18 | ) 19 | process 20 | { 21 | $typeName = 'PScribo.PageBreak'; 22 | $pscriboDocument.Properties['PageBreaks']++; 23 | $pscriboDocument.Properties['Pages']++; 24 | $pscriboPageBreak = [PSCustomObject] @{ 25 | Id = $Id; 26 | Type = $typeName; 27 | } 28 | return $pscriboPageBreak; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Out-WordPageBreak.ps1: -------------------------------------------------------------------------------- 1 | function Out-WordPageBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Word page break. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Xml.XmlElement])] 9 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','PageBreak')] 10 | param 11 | ( 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [System.Management.Automation.PSObject] $PageBreak, 14 | 15 | [Parameter(Mandatory)] 16 | [System.Xml.XmlDocument] $XmlDocument 17 | ) 18 | process 19 | { 20 | $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' 21 | $p = $XmlDocument.CreateElement('w', 'p', $xmlns) 22 | $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) 23 | $br = $r.AppendChild($XmlDocument.CreateElement('w', 'br', $xmlns)) 24 | [ref] $null = $br.SetAttribute('type', $xmlns, 'page') 25 | 26 | return $p 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Out-HtmlImage.ps1: -------------------------------------------------------------------------------- 1 | function Out-HtmlImage 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output embedded Html image. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | ## PScribo Image object 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Image 14 | ) 15 | process 16 | { 17 | [System.Text.StringBuilder] $imageBuilder = New-Object -TypeName 'System.Text.StringBuilder' 18 | [ref] $null = $imageBuilder.AppendFormat('
', $Image.Align).AppendLine() 19 | $imageBase64 = [System.Convert]::ToBase64String($Image.Bytes) 20 | [ref] $null = $imageBuilder.AppendFormat('{2}', $Image.MimeType, $imageBase64, $Image.Text, $Image.Height, $Image.Width).AppendLine() 21 | [ref] $null = $imageBuilder.AppendLine('
') 22 | return $imageBuilder.ToString() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Plugins/Text/Out-TextBlankLine.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Text\Out-TextBlankLine' { 10 | 11 | ## Scaffold document options 12 | $pscriboDocument = Document -Name 'TestDocument' -ScriptBlock {}; 13 | 14 | It 'Defaults to a single blank line' { 15 | $expected = [System.Environment]::NewLine 16 | 17 | $l = BlankLine | Out-TextBlankLine 18 | 19 | $l | Should Be $expected 20 | } 21 | 22 | It 'Creates 3 blank lines' { 23 | $expected = '{0}{0}{0}' -f [System.Environment]::NewLine 24 | 25 | $l = BlankLine -Count 3 | Out-TextBlankLine 26 | 27 | $l | Should Be $expected 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Unit/Test-PScriboStyleColor.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $testRoot = Split-Path -Path $here -Parent; 3 | $moduleRoot = Split-Path -Path $testRoot -Parent; 4 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 5 | 6 | InModuleScope 'PScribo' { 7 | 8 | Describe 'Test-PScriboStyleColor' { 9 | 10 | It 'tests valid html color code' { 11 | Test-PscriboStyleColor -Color '012345' | Should Be $true 12 | Test-PscriboStyleColor -Color '#012345' | Should Be $true 13 | Test-PscriboStyleColor -Color '#D3D' | Should Be $true 14 | Test-PscriboStyleColor -Color D3D | Should Be $true 15 | } 16 | 17 | It 'tests invalid length html color code' { 18 | Test-PscriboStyleColor -Color abcd | Should Be $false 19 | Test-PscriboStyleColor -Color 1abcdef | Should Be $false 20 | } 21 | 22 | It 'tests invalid html color code' { 23 | Test-PscriboStyleColor -Color ghi | Should Be $false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-PScriboPreformattedTable.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-PScriboPreformattedTable 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a formatted table based upon table type for plugin output/rendering. 6 | 7 | .NOTES 8 | Maintains backwards compatibility with other plugins that do not require styling/formatting. 9 | #> 10 | [CmdletBinding()] 11 | [OutputType([System.Management.Automation.PSObject])] 12 | param 13 | ( 14 | [Parameter(Mandatory, ValueFromPipeline)] 15 | [System.Management.Automation.PSObject] $Table 16 | ) 17 | process 18 | { 19 | if ($Table.IsKeyedList) 20 | { 21 | Write-Output -InputObject (ConvertTo-PScriboFormattedKeyedListTable -Table $Table) 22 | } 23 | elseif ($Table.IsList) 24 | { 25 | Write-Output -InputObject (ConvertTo-PScriboFormattedListTable -Table $Table) 26 | } 27 | else 28 | { 29 | Write-Output -InputObject (ConvertTo-PScriboFormattedTable -Table $Table) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Examples/Example11.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example11 = Document -Name 'PScribo Example 11' { 11 | 12 | <# 13 | As the 'Table' keyword supports pipeline input, you can use the standard 14 | Powershell 'Sort-Object', 'Group-Object', 'Select-Object' and 'Where-Object' 15 | cmdlets etc. to filter, sort and/or group the input into the 'Table' cmdlet. 16 | 17 | The following example creates a table of services, displaying the service 18 | names, display names and service statuses. 19 | 20 | NOTE: Due the the length of the table cell content, text output may be 21 | truncated due to limitations/implementation of the 'Format-Table' cmdlet. 22 | #> 23 | Get-Service | Select-Object Name,DisplayName,Status | Table 24 | } 25 | $example11 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 26 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Get-PScriboListItemMaximumLength.ps1: -------------------------------------------------------------------------------- 1 | function Get-PScriboListItemMaximumLength 2 | { 3 | <# 4 | .SYNOPSIS 5 | Renders a List's item numbers to determine the maximum string/render width. 6 | #> 7 | [CmdletBinding()] 8 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','NumberStyle')] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $List, 13 | 14 | [Parameter(Mandatory, ValueFromPipelineByPropertyName)] 15 | [System.Management.Automation.PSObject] $NumberStyle 16 | ) 17 | process 18 | { 19 | $List.Items | 20 | Where-Object { $_.Type -eq 'PScribo.Item' } | 21 | ForEach-Object { 22 | $number = ConvertFrom-NumberStyle -Value $_.Index -NumberStyle $numberStyle 23 | Write-Output -InputObject $number.Length 24 | } | 25 | Measure-Object -Maximum | 26 | Select-Object -ExpandProperty Maximum 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Unit/BlankLine.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $testRoot = Split-Path -Path $here -Parent; 3 | $moduleRoot = Split-Path -Path $testRoot -Parent; 4 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 5 | 6 | InModuleScope 'PScribo' { 7 | 8 | Describe 'BlankLine' { 9 | $pscriboDocument = Document 'ScaffoldDocument' {} 10 | 11 | It 'returns a PSCustomObject object' { 12 | $b = BlankLine 13 | 14 | $b.GetType().Name | Should Be 'PSCustomObject' 15 | } 16 | 17 | It 'creates a PScribo.BlankLine type' { 18 | $b = BlankLine 19 | 20 | $b.Type | Should Be 'PScribo.BlankLine' 21 | } 22 | 23 | It 'creates blank line with no parameters' { 24 | $b = BlankLine 25 | 26 | $b.LineCount | Should Be 1 27 | } 28 | 29 | It 'creates blank line by named -Count parameter' { 30 | $count = 2 31 | 32 | $b = BlankLine -Count $count 33 | 34 | $b.LineCount | Should Be $count 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Src/Private/Set-PScriboSectionBreakEnd.ps1: -------------------------------------------------------------------------------- 1 | function Set-PScriboSectionBreakEnd 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sets the IsSectionBreakEnd on the last (nested) paragraph/subsection (required by Word plugin) 6 | #> 7 | [CmdletBinding()] 8 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $Section 13 | ) 14 | process 15 | { 16 | $Section.Sections | 17 | Where-Object { $_.Type -in 'PScribo.Section','PScribo.Paragraph','PScribo.Table' } | 18 | Select-Object -Last 1 | ForEach-Object { 19 | if ($PSItem.Type -in 'PScribo.Paragraph','PScribo.Table') 20 | { 21 | $PSItem.IsSectionBreakEnd = $true 22 | } 23 | else 24 | { 25 | Set-PScriboSectionBreakEnd -Section $PSItem 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Get-TextTableCaption.ps1: -------------------------------------------------------------------------------- 1 | function Get-TextTableCaption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates html

caption from a PScribo.Table object. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Table 14 | ) 15 | begin 16 | { 17 | ## Fix Set-StrictMode 18 | if (-not (Test-Path -Path Variable:\Options)) 19 | { 20 | $options = New-PScriboTextOption; 21 | } 22 | } 23 | process 24 | { 25 | $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style 26 | $convertToAlignedStringParams = @{ 27 | InputObject = '{0} {1} {2}' -f $tableStyle.CaptionPrefix, $Table.CaptionNumber, $Table.Caption 28 | Width = $options.TextWidth 29 | Tabs = $Table.Tabs 30 | Align = $tableStyle.Align 31 | } 32 | return (ConvertTo-AlignedString @convertToAlignedStringParams) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Examples/Example20.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example20 = Document -Name 'PScribo Example 20' { 11 | 12 | <# 13 | Hashtables can also be used in -List view along with the -Width 14 | and -ColumnWidths parameters. 15 | 16 | The following example creates three tables (one per hashtable) in -List 17 | view, from a collection of manually constructed hashtables 18 | #> 19 | $hashtableArray = @( 20 | [Ordered] @{ 'Column 1' = 'Some random text'; 'Column2' = 345; 'Custom Property' = $true; } 21 | [Ordered] @{ 'Column 1' = 'Random some text'; 'Column2' = 16; 'Custom Property' = $false; } 22 | [Ordered] @{ 'Column 1' = 'Text random some'; 'Column2' = 1GB; 'Custom Property' = $true; } 23 | ) 24 | Table -Hashtable $hashtableArray -Width 50 -ColumnWidths 40,60 -List 25 | } 26 | $example20 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 27 | -------------------------------------------------------------------------------- /Src/Plugins/Json/Out-JsonParagraph.ps1: -------------------------------------------------------------------------------- 1 | function Out-JsonParagraph 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted paragraph run. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Paragraph 14 | ) 15 | begin 16 | { 17 | ## Initializing string object 18 | [System.Text.StringBuilder] $paragraphBuilder = New-Object -TypeName 'System.Text.StringBuilder' 19 | } 20 | process 21 | { 22 | foreach ($paragraphRun in $Paragraph.Sections) 23 | { 24 | $text = Resolve-PScriboToken -InputObject $paragraphRun.Text 25 | [ref] $null = $paragraphBuilder.Append($text) 26 | 27 | if (($paragraphRun.IsParagraphRunEnd -eq $false) -and 28 | ($paragraphRun.NoSpace -eq $false)) 29 | { 30 | [ref] $null = $paragraphBuilder.Append(' ') 31 | } 32 | } 33 | 34 | 35 | return $paragraphBuilder.ToString() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Iain Brighton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Src/Private/Write-PScriboProcessSectionId.ps1: -------------------------------------------------------------------------------- 1 | function Write-PScriboProcessSectionId 2 | { 3 | [CmdletBinding()] 4 | param 5 | ( 6 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 7 | [System.String] $SectionId, 8 | 9 | [Parameter(Mandatory, ValueFromPipelineByPropertyName)] 10 | [System.String] $SectionType, 11 | 12 | [Parameter(ValueFromPipelineByPropertyName)] 13 | [System.Int32] $Length = 40, 14 | 15 | [Parameter(ValueFromPipelineByPropertyName)] 16 | [System.Int32] $Indent = 0 17 | ) 18 | process 19 | { 20 | if ($SectionId.Length -gt $Length) 21 | { 22 | $sectionDisplayName = '{0}[..]' -f $SectionId.Substring(0, ($Length -4)) 23 | } 24 | else 25 | { 26 | $sectionDisplayName = $SectionId 27 | } 28 | 29 | $writePScriboMessageParams = @{ 30 | Message = $localized.PluginProcessingSection -f $SectionType, $sectionDisplayName 31 | Indent = $Indent 32 | } 33 | Write-PScriboMessage @writePScriboMessageParams 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/Plugins/Json/Out-JsonTOC.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Json\Out-JsonTOC' { 10 | 11 | ## Section numbering is ignored and used regardless 12 | 13 | It 'outputs TOC' { 14 | $tocName = 'Table of contents' 15 | $heading1 = 'Heading 1' 16 | $heading2 = 'Heading 2' 17 | $Document = Document -Name 'TestDocument' -ScriptBlock { 18 | TOC -Name $tocName 19 | Section $heading1 -Style Heading1 { 20 | Section $heading2 -Style Heading2 { } 21 | } 22 | } 23 | $expected = '{{.*"{0}".*"{1}"}}' -f $heading1, $heading2 24 | 25 | $result = Out-JsonTOC | ConvertTo-Json -Depth 100 -Compress 26 | 27 | $result | Should MatchExactly $expected 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Plugins/Word/ConvertTo-WordColor.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginsRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginsRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Word\ConvertTo-WordColor' { 10 | 11 | It 'converts to "abcdef" to "ABCDEF"' { 12 | $result = ConvertTo-WordColor 'abcdef' 13 | 14 | $result | Should BeExactly 'ABCDEF' 15 | } 16 | 17 | It 'converts "#abcdef" to "ABCDEF"' { 18 | $result = ConvertTo-WordColor '#abcdef' 19 | 20 | $result | Should BeExactly 'ABCDEF' 21 | } 22 | 23 | It 'converts "abc" to "AABBCC"' { 24 | $result = ConvertTo-WordColor 'abc' 25 | 26 | $result | Should BeExactly 'AABBCC' 27 | } 28 | 29 | It 'converts "#abc" to "AABBCC"' { 30 | $result = ConvertTo-WordColor '#abc' 31 | 32 | $result | Should BeExactly 'AABBCC' 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/Unit/Get-PScriboImage.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent 2 | $testRoot = Split-Path -Path $here -Parent 3 | $moduleRoot = Split-Path -Path $testRoot -Parent 4 | Import-Module -Name "$moduleRoot\PScribo.psm1" -Force 5 | 6 | InModuleScope -ModuleName 'PScribo' -ScriptBlock { 7 | 8 | $testRoot = Split-Path -Path $PSScriptRoot -Parent 9 | 10 | Describe -Name 'Get-PScriboImage' -Fixture { 11 | 12 | $pscriboDocument = Document -Name 'ScaffoldDocument' -ScriptBlock { 13 | Image -Path (Join-Path -Path $testRoot -ChildPath 'TestImage.jpg') -Id 1 14 | Section Nested { 15 | Image -Path (Join-Path -Path $testRoot -ChildPath 'TestImage.png') -Id 2 16 | } 17 | } 18 | 19 | It 'finds all Images' { 20 | $result = Get-PScriboImage -Section $pscriboDocument.Sections 21 | 22 | $result.Count | Should Be 2 23 | } 24 | 25 | It 'finds Image by Id' { 26 | $result = @(Get-PScriboImage -Section $pscriboDocument.Sections -Id 2) 27 | 28 | $result.Count | Should Be 1 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Examples/Example10.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example10 = Document -Name 'PScribo Example 10' { 11 | 12 | <# 13 | PScribo supports insertion of tables into a document. In it's simplest form, a 14 | collection of objects can be added to a document by using the 'Table' cmdlet. By 15 | default, a table is created the full width (100%) of the document, with each 16 | column width being equally distributed. 17 | 18 | The following examples creates a table of all properties on all services. 19 | 20 | NOTE: Due the the vast number of properties, the table will not render in any 21 | meaningful way! Text output may also be truncated due to limitations/ 22 | implementation of the 'Format-Table' cmdlet. 23 | #> 24 | Table -InputObject (Get-Service | Select-Object -First 100) 25 | } 26 | $example10 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 27 | -------------------------------------------------------------------------------- /Examples/Example22.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example22 = Document -Name 'PScribo Example 22' { 11 | 12 | <# 13 | A grid can be applied to a table by setting the -BorderWidth property. The 14 | border width is specified in points (pt). 15 | 16 | NOTE: Table borders apply to both standard tables and -List view tables. 17 | 18 | Optionally, you may set a border/grid color with the -BorderColor property. 19 | If the border color is not specified, it will default to black (#000). 20 | 21 | The following example creates a table with an orange/red grid/border around 22 | all cells. 23 | #> 24 | TableStyle -Name 'BasicGrid' -HeaderStyle Normal -RowStyle Normal -BorderWidth 1 -BorderColor OrangeRed 25 | Get-Service | Select-Object -Property Name,DisplayName,Status -First 3 | Table -Style BasicGrid 26 | } 27 | $example22 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 28 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlTableCaption.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlTableCaption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates html

caption from a PScribo.Table object. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Table 14 | ) 15 | process 16 | { 17 | $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style 18 | 19 | ## Scaffold paragraph and paragraph run for table caption 20 | $paragraphId = '{0}{1}' -f $tableStyle.CaptionPrefix, $Table.Number 21 | $paragraph = New-PScriboParagraph -Id $paragraphId -Style $tableStyle.CaptionStyle -NoIncrementCounter 22 | $paragraphRunText = '{0} {1} {2}' -f $tableStyle.CaptionPrefix, $Table.CaptionNumber, $Table.Caption 23 | $paragraphRun = New-PScriboParagraphRun -Text $paragraphRunText 24 | $paragraphRun.IsParagraphRunEnd = $true 25 | [ref] $null = $paragraph.Sections.Add($paragraphRun) 26 | 27 | return (Out-HtmlParagraph -Paragraph $paragraph) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Src/Private/Get-UriBytes.ps1: -------------------------------------------------------------------------------- 1 | function Get-UriBytes 2 | { 3 | <# 4 | .SYNOPSIS 5 | Gets an image's content as a byte[] 6 | #> 7 | [CmdletBinding()] 8 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] 9 | [OutputType([System.Byte[]])] 10 | param 11 | ( 12 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 13 | [System.Uri] $Uri 14 | ) 15 | process 16 | { 17 | try 18 | { 19 | $webClient = New-Object -TypeName 'System.Net.WebClient' 20 | [System.IO.Stream] $contentStream = $webClient.OpenRead($uri.AbsoluteUri) 21 | [System.IO.MemoryStream] $memoryStream = New-Object System.IO.MemoryStream 22 | $contentStream.CopyTo($memoryStream) 23 | return $memoryStream.ToArray() 24 | } 25 | catch 26 | { 27 | $_ 28 | } 29 | finally 30 | { 31 | if ($null -ne $memoryStream) { $memoryStream.Close() } 32 | if ($null -ne $contentStream) { $contentStream.Close() } 33 | if ($null -ne $webClient) { $webClient.Dispose() } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlTablePaddingStyle.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlTablePaddingStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates html stylesheet style padding attributes from a PScribo document table style. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | ## PScribo document table style 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [System.Management.Automation.PSObject] $TableStyle 14 | ) 15 | process 16 | { 17 | $tableStyleBuilder = New-Object -TypeName 'System.Text.StringBuilder' 18 | [ref] $null = $tableStyleBuilder.AppendFormat(' padding: {0}rem {1}rem {2}rem {3}rem;', 19 | (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingTop)), 20 | (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingRight)), 21 | (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingBottom)), 22 | (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingLeft))) 23 | return $tableStyleBuilder.ToString() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/Plugins/Text/ConvertTo-IndentedString.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-IndentedString 2 | { 3 | <# 4 | .SYNOPSIS 5 | Indents a block of text using the number of tab stops. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [Object[]] $InputObject, 14 | 15 | ## Tab indent 16 | [Parameter()] 17 | [ValidateRange(0,10)] 18 | [System.Int32] $Tabs = 0, 19 | 20 | ## Tab size 21 | [Parameter()] 22 | [ValidateRange(0,10)] 23 | [System.Int32] $TabSize = 4 24 | ) 25 | process 26 | { 27 | $padding = ''.PadRight(($Tabs * $TabSize), ' ') 28 | ## Use a StringBuilder to write the table line by line (to indent it) 29 | [System.Text.StringBuilder] $builder = New-Object System.Text.StringBuilder 30 | 31 | foreach ($line in ($InputObject -split '\r\n?|\n')) 32 | { 33 | [ref] $null = $builder.Append($padding) 34 | [ref] $null = $builder.AppendLine($line.TrimEnd()) ## Trim trailing space (#67) 35 | } 36 | return $builder.ToString() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Src/Plugins/Xml/Out-XmlParagraph.ps1: -------------------------------------------------------------------------------- 1 | function Out-XmlParagraph 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Xml paragraph run. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | ## PScribo paragraph object 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Paragraph 14 | ) 15 | process 16 | { 17 | [System.Text.StringBuilder] $paragraphBuilder = New-Object -TypeName 'System.Text.StringBuilder' 18 | foreach ($paragraphRun in $Paragraph.Sections) 19 | { 20 | 21 | $text = Resolve-PScriboToken -InputObject $paragraphRun.Text 22 | [ref] $null = $paragraphBuilder.Append($text) 23 | 24 | if (($paragraphRun.IsParagraphRunEnd -eq $false) -and 25 | ($paragraphRun.NoSpace -eq $false)) 26 | { 27 | [ref] $null = $paragraphBuilder.Append(' ') 28 | } 29 | } 30 | 31 | $paragraphElement = $xmlDocument.CreateElement('paragraph') 32 | [ref] $null = $paragraphElement.AppendChild($xmlDocument.CreateTextNode($paragraphBuilder.ToString())) 33 | 34 | return $paragraphElement 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboListReference.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboListReference 2 | { 3 | <# 4 | .SYNOPSIS 5 | Initializes new PScribo list reference object. 6 | 7 | .DESCRIPTION 8 | Creates a placeholder reference to a list stored in $pscriboDocument.Lists. 9 | 10 | .NOTES 11 | This is an internal function and should not be called directly. 12 | #> 13 | [CmdletBinding()] 14 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 15 | [OutputType([System.Management.Automation.PSCustomObject])] 16 | param 17 | ( 18 | ## Display name used in verbose output when processing. 19 | [Parameter(ValueFromPipelineByPropertyName)] 20 | [System.String] $Name, 21 | 22 | [Parameter(ValueFromPipelineByPropertyName)] 23 | [System.Int32] $Number 24 | ) 25 | process 26 | { 27 | $pscriboListReference = [PSCustomObject] @{ 28 | Id = [System.Guid]::NewGuid().ToString() 29 | Name = $Name 30 | Type = 'PScribo.ListReference' 31 | Number = $Number 32 | } 33 | return $pscriboListReference 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Examples/Example18.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example18 = Document -Name 'PScribo Example 18' { 11 | 12 | <# 13 | If you need to list an object's or hashtable's values in a list format, you 14 | can specify the -List parameter on the 'Table' cmdlet. Rather than creating 15 | a table column for each property, it will create a two-column table, with a 16 | row for each property instead. 17 | 18 | This is useful if not all properties will fit across as page (like Services). 19 | However, if multiple objects are encountered, PScribo will create a separate 20 | two-column table for each object (similar in functionality to the 21 | 'Format-List' cmdlet). 22 | 23 | The following example will create a list table, detailing every property, 24 | for the first 10 services regiestered on the local machine. 25 | #> 26 | Get-Service | Select-Object -First 10 | Table -List 27 | } 28 | $example18 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 29 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordListLevel.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordListLevel 2 | { 3 | <# 4 | .SYNOPSIS 5 | Process (nested) lists and build index of number/bullet styles. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline)] 11 | [System.Management.Automation.PSObject] $List, 12 | 13 | [Parameter(ValueFromPipelineByPropertyName)] 14 | [System.Collections.Hashtable] $Levels = @{ } 15 | ) 16 | process 17 | { 18 | foreach ($item in $List.Items) 19 | { 20 | if ($item.Type -eq 'PScribo.Item') 21 | { 22 | $level = $item.Level -1 23 | if (-not $Levels.ContainsKey($level)) 24 | { 25 | $Levels[$level] = @{ 26 | IsNumbered = $List.IsNumbered 27 | NumberStyle = $List.NumberStyle 28 | BulletStyle = $List.BulletStyle 29 | } 30 | } 31 | } 32 | elseif ($item.Type -eq 'PScribo.List') 33 | { 34 | $Levels = Get-WordListLevel -List $item -Levels $Levels 35 | } 36 | } 37 | return $Levels 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Src/Private/Copy-Object.ps1: -------------------------------------------------------------------------------- 1 | function Copy-Object 2 | { 3 | <# 4 | .SYNOPSIS 5 | Clones a .NET object by serializing and deserializing it. 6 | .NOTES 7 | PowerShell 7.4 throws a "Type 'System.Management.Automation.PSObject' is not marked as serializable." exception. 8 | This function has been replaced by 'ConvertTo-Json | ConvertFrom-Json' to serialize and deserialize into a 9 | "clone" object. This works for PScribo objects but may not work for other nested objects in script blocks. 10 | #> 11 | [CmdletBinding()] 12 | param 13 | ( 14 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 15 | [System.Object] $InputObject 16 | ) 17 | process 18 | { 19 | try 20 | { 21 | $stream = New-Object IO.MemoryStream 22 | $formatter = New-Object Runtime.Serialization.Formatters.Binary.BinaryFormatter 23 | $formatter.Serialize($stream, $InputObject) 24 | $stream.Position = 0 25 | return $formatter.Deserialize($stream) 26 | } 27 | catch 28 | { 29 | Write-Error -ErrorRecord $_ 30 | } 31 | finally 32 | { 33 | $stream.Dispose() 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlTableColGroup.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlTableColGroup 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates Html

tags based on table column widths 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Table 14 | ) 15 | process 16 | { 17 | $colGroupBuilder = New-Object -TypeName 'System.Text.StringBuilder' 18 | 19 | if ($Table.ColumnWidths) 20 | { 21 | [ref] $null = $colGroupBuilder.Append('') 22 | foreach ($columnWidth in $Table.ColumnWidths) 23 | { 24 | if ($null -eq $columnWidth) 25 | { 26 | [ref] $null = $colGroupBuilder.Append('') 27 | } 28 | else 29 | { 30 | [ref] $null = $colGroupBuilder.AppendFormat('', $columnWidth) 31 | } 32 | } 33 | [ref] $null = $colGroupBuilder.AppendLine('') 34 | } 35 | 36 | return $colGroupBuilder.ToString() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/Unit/LineBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $testRoot = Split-Path -Path $here -Parent; 3 | $moduleRoot = Split-Path -Path $testRoot -Parent; 4 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 5 | 6 | InModuleScope 'PScribo' { 7 | 8 | Describe 'LineBreak' { 9 | $pscriboDocument = Document 'ScaffoldDocument' {}; 10 | 11 | It 'returns a PSCustomObject object' { 12 | $l = LineBreak 13 | 14 | $l.GetType().Name | Should Be 'PSCustomObject' 15 | } 16 | 17 | It 'creates a PScribo.LineBreak type' { 18 | $l = LineBreak 19 | 20 | $l.Type | Should Be 'PScribo.LineBreak' 21 | } 22 | 23 | It 'creates line break with no parameters' { 24 | $l = LineBreak 25 | 26 | $l.Id | Should Not Be $null 27 | } 28 | 29 | It 'creates line break by named -Id parameter' { 30 | $id = 'Test' 31 | 32 | $l = LineBreak -Id $id 33 | 34 | $l.Id | Should Be $id 35 | } 36 | 37 | It 'creates line break by positional -Id parameter' { 38 | $id = 'Test' 39 | 40 | $l = LineBreak $id 41 | 42 | $l.Id | Should Be $id 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /PScribo.psm1: -------------------------------------------------------------------------------- 1 | Set-StrictMode -Version Latest 2 | 3 | ## Import localisation strings based on UICulture 4 | $importLocalizedDataParams = @{ 5 | BindingVariable = 'localized' 6 | BaseDirectory = $PSScriptRoot 7 | FileName = 'PScribo.Resources.psd1' 8 | } 9 | Import-LocalizedData @importLocalizedDataParams -ErrorAction SilentlyContinue 10 | 11 | #Fallback to en-US culture strings 12 | if (-not (Test-Path -Path 'Variable:\localized')) 13 | { 14 | $importLocalizedDataParams['UICulture'] = 'en-US' 15 | Import-LocalizedData @importLocalizedDataParams -ErrorAction Stop 16 | } 17 | 18 | ## Dot source all the nested .ps1 files in the \Functions and \Plugin folders, excluding tests 19 | $pscriboRoot = Split-Path -Parent $PSCommandPath 20 | Get-ChildItem -Path "$pscriboRoot\Src\" -Include '*.ps1' -Recurse | 21 | ForEach-Object { 22 | Write-Debug ($localized.ImportingFile -f $_.FullName) 23 | ## https://becomelotr.wordpress.com/2017/02/13/expensive-dot-sourcing/ 24 | . ([System.Management.Automation.ScriptBlock]::Create( 25 | [System.IO.File]::ReadAllText($_.FullName) 26 | )) 27 | } 28 | 29 | Add-Type -AssemblyName 'System.Drawing' 30 | #Export-ModuleMember -Function $exportedFunctions -Alias $exportedAliases -Verbose:$false 31 | -------------------------------------------------------------------------------- /Tests/Unit/PageBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $testRoot = Split-Path -Path $here -Parent; 3 | $moduleRoot = Split-Path -Path $testRoot -Parent; 4 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 5 | 6 | InModuleScope 'PScribo' { 7 | 8 | Describe 'PageBreak' { 9 | 10 | $pscriboDocument = Document 'ScaffoldDocument' {} 11 | 12 | It 'returns a PSCustomObject object' { 13 | $p = PageBreak 14 | 15 | $p.GetType().Name | Should Be 'PSCustomObject' 16 | } 17 | 18 | It 'creates a PScribo.PageBreak type' { 19 | $p = PageBreak 20 | 21 | $p.Type | Should Be 'PScribo.PageBreak' 22 | } 23 | 24 | It 'creates page break with no parameters' { 25 | $p = PageBreak 26 | 27 | $p.Id | Should Not Be $null 28 | } 29 | 30 | It 'creates page break by named -Id parameter' { 31 | $id = 'Test' 32 | 33 | $p = PageBreak -Id $id 34 | 35 | $p.Id | Should Be $id 36 | } 37 | 38 | It 'creates page break by positional -Id parameter' { 39 | $id = 'Test' 40 | 41 | $p = PageBreak $id 42 | 43 | $p.Id | Should Be $id 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Out-WordLineBreak.ps1: -------------------------------------------------------------------------------- 1 | function Out-WordLineBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Word line break. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Xml.XmlElement])] 9 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','LineBreak')] 10 | param 11 | ( 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [System.Management.Automation.PSObject] $LineBreak, 14 | 15 | [Parameter(Mandatory)] 16 | [System.Xml.XmlDocument] $XmlDocument 17 | ) 18 | process 19 | { 20 | $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' 21 | $p = $XmlDocument.CreateElement('w', 'p', $xmlns) 22 | $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) 23 | $pBdr = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pBdr', $xmlns)) 24 | $bottom = $pBdr.AppendChild($XmlDocument.CreateElement('w', 'bottom', $xmlns)) 25 | [ref] $null = $bottom.SetAttribute('val', $xmlns, 'single') 26 | [ref] $null = $bottom.SetAttribute('sz', $xmlns, 6) 27 | [ref] $null = $bottom.SetAttribute('space', $xmlns, 1) 28 | [ref] $null = $bottom.SetAttribute('color', $xmlns, 'auto') 29 | 30 | return $p 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboTOC.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboTOC 2 | { 3 | <# 4 | .SYNOPSIS 5 | Initializes a new PScribo Table of Contents (TOC) object. 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [OutputType([System.Management.Automation.PSCustomObject])] 13 | param 14 | ( 15 | [Parameter(ValueFromPipeline)] 16 | [ValidateNotNullOrEmpty()] 17 | [System.String] $Name = 'Contents', 18 | 19 | [Parameter(ValueFromPipelineByPropertyName)] 20 | [ValidateNotNullOrEmpty()] 21 | [System.String] $ClassId = 'TOC' 22 | ) 23 | process 24 | { 25 | $typeName = 'PScribo.TOC'; 26 | if ($pscriboDocument.Options['ForceUppercaseSection']) 27 | { 28 | $Name = $Name.ToUpper(); 29 | } 30 | $pscriboDocument.Properties['TOCs']++; 31 | $pscriboTOC = [PSCustomObject] @{ 32 | Id = [System.Guid]::NewGuid().ToString(); 33 | Name = $Name; 34 | Type = $typeName; 35 | ClassId = $ClassId; 36 | } 37 | return $pscriboTOC; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Examples/Example24.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example24 = Document -Name 'PScribo Example 24' { 11 | 12 | <# 13 | The following example combines the creation of multiple custom styles with the 14 | definition of a custom table style. The custom "BlueZebra" table style is then 15 | applied to a table. 16 | #> 17 | Style -Name 'BlueZebraHeading' -Bold -Color 039 -Font 'Segoe UI' -Size 12 18 | Style -Name 'BlueZebraRow' -Color 669 -Font 'Lucida Sans Unicode' -BackgroundColor E8EDFF 19 | Style -Name 'BlueZebraAltRow' -Color 669 -Font 'Lucida Sans Unicode' 20 | TableStyle -Name 'BlueZebra' -HeaderStyle BlueZebraHeading -RowStyle BlueZebraRow -AlternateRowStyle BlueZebraAltRow -PaddingTop 4 -PaddingRight 4 -PaddingBottom 4 -PaddingLeft 4 21 | 22 | <# 23 | Create a standard table using the new "BlueZebra" table style. 24 | #> 25 | Get-Service | Select-Object -Last 10 | Table -Columns Name,DisplayName,Status -Headers Name,'Display Name','State' -Style BlueZebra 26 | } 27 | $example24 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 28 | -------------------------------------------------------------------------------- /Examples/Example12.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example12 = Document -Name 'PScribo Example 12' { 11 | <# 12 | The 'Table' cmdlet supports filtering of object properties by using the -Columns 13 | parameter. The -Columns parameter specifies the object properties you wish to 14 | display in the table, in the order required. 15 | 16 | NOTE: The -Columns parameter is required if you wish to override the values 17 | displayed in column headings. 18 | 19 | The following example displays the Name, DisplayName and Status properties of 20 | all services sent down the pipeline, ignoring all other properties. 21 | 22 | NOTE: The columns are displayed in the exact order that they are listed in 23 | the -Columns parameter. 24 | 25 | NOTE: Due the the length of the table cell content, text output may be 26 | truncated due to limitations/implementation of the 'Format-Table' cmdlet. 27 | #> 28 | Get-Service | Table -Columns Name,DisplayName,Status 29 | } 30 | $example12 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 31 | -------------------------------------------------------------------------------- /Examples/Example13.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example13 = Document -Name 'PScribo Example 13' { 11 | 12 | <# 13 | To override the column headers, the -Columns and -Headers parameters can be used 14 | together. 15 | 16 | The -Headers parameter specifies the heading names to apply to the corresponding 17 | (positional) -Columns parameter. This permits you to include spaces in the headers 18 | and/or change the properties' display values. 19 | 20 | The following example overrides the column names with the values supplied in the 21 | -Headers parameter - a space is introduced in the 'DisplayName' property and the 22 | 'Status' property is displayed as 'State' instead. 23 | 24 | NOTE: Due the the length of the table cell content, text output may be 25 | truncated due to limitations/implementation of the 'Format-Table' cmdlet. 26 | #> 27 | Get-Service | Table -Columns Name,DisplayName,Status -Headers Name,'Display Name',State 28 | } 29 | $example13 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru -Options @{ TextWidth = 160 } 30 | -------------------------------------------------------------------------------- /Examples/Example40.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example40 = Document -Name 'PScribo Example 40' { 11 | 12 | <# 13 | Numbered lists are supported and can be specified with the '-Numbered' parameter. 14 | #> 15 | List -Item 'Apples','Oranges','Bananas' -Numbered 16 | 17 | <# 18 | Multiple number styles are available: 'Number', 'Letter' and 'Roman'. If not specified, 19 | a numbered list will default to the 'Number' style. You can specify the required number 20 | format with the '-NumberStyle' parameter. 21 | #> 22 | List -Item 'Apples','Oranges','Bananas' -Numbered -NumberStyle Letter 23 | 24 | <# 25 | Multiple number styles are available: 'Number', 'Letter' and 'Roman'. If not specified, 26 | a numbered list will default to the 'Number' style. You can specify the required number 27 | format with the '-NumberStyle' parameter. 28 | #> 29 | List -Numbered -NumberStyle Roman { 30 | Item 'Apples' 31 | Item 'Bananas' 32 | Item 'Oranges' 33 | } 34 | 35 | } 36 | $example40 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 37 | -------------------------------------------------------------------------------- /Examples/Example07.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example7 = Document -Name 'PScribo Example 7' { 11 | 12 | <# 13 | Sections support automatic numbering, i.e. PScribo will automatically generate the section 14 | numbers/levels based on the nesting. To turn this on, use the 'DocumentOption' cmdlet. 15 | #> 16 | DocumentOption -EnableSectionNumbering -MarginTopAndBottom 72 -MarginLeftAndRight 54 17 | 18 | Section -Name 'First Section' -ScriptBlock { 19 | Paragraph 'This section should be labeled as "1 First Section".' 20 | } 21 | 22 | Section -Name 'Second "Styled" Section' -Style Heading1 -ScriptBlock { 23 | Paragraph 'This section should be labeled as "2 Second Styled Section".' 24 | } 25 | 26 | Section -Name 'Third "Styled" Section' -Style Heading1 -ScriptBlock { 27 | Paragraph 'This section should be labeled as "3 Third Section".' 28 | 29 | Section 'Sub Section' -Style Heading2 { 30 | Paragraph 'This section should be labeled as "3.1 Sub Section".' 31 | } 32 | } 33 | } 34 | $example7 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 35 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-Mm.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Mm 2 | { 3 | <# 4 | .SYNOPSIS 5 | Convert points, inches or pixels into millimeters 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Single])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Point')] 12 | [Alias('pt')] 13 | [System.Single] $Point, 14 | 15 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Inch')] 16 | [Alias('in')] 17 | [System.Single] $Inch, 18 | 19 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Pixel')] 20 | [Alias('px')] 21 | [System.Single] $Pixel, 22 | 23 | [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Pixel')] 24 | [System.Int16] $Dpi = 96 25 | ) 26 | process 27 | { 28 | if ($PSCmdlet.ParameterSetName -eq 'Point') 29 | { 30 | return [System.Math]::Round(($Point / 72) * 25.4, 2) 31 | } 32 | elseif ($PSCmdlet.ParameterSetName -eq 'Inch') 33 | { 34 | $mm = $Inch * 25.4 35 | return [System.Math]::Round($mm, 2) 36 | } 37 | elseif ($PSCmdlet.ParameterSetName -eq 'Pixel') 38 | { 39 | $mm = (25.4 / $Dpi) * $Pixel 40 | return [System.Math]::Round($mm, 2) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Examples/Example05.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example5 = Document -Name 'PScribo Example 5' { 11 | 12 | <# 13 | Rather than specifying styling options on individual paragraphs, PScribo supports defining (or 14 | overriding the default styles) with your own. Creating your own style is easy - just use the 15 | 'Style' cmdlet. 16 | 17 | The following command creates a new style called 'Funky': 18 | #> 19 | Style -Name 'Funky' -Font Arial -Size 16 -Color Pink -Align Right 20 | 21 | <# 22 | The style can then easily be applied to a paragraph by specifying the -Style parameter. 23 | #> 24 | Paragraph 'This paragraph is styled with the "Funky" style!' -Style Funky 25 | 26 | <# 27 | If no style is specified, the default 'Normal' is used. You can override the default style 28 | by defining your own style with the same name: 29 | #> 30 | Style -Name 'Normal' -Font Tahoma -Size 12 -Color 000 31 | Paragraph 'This paragraph will be styled with the custom "Normal" style defined earlier.' 32 | } 33 | $example5 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 34 | -------------------------------------------------------------------------------- /Examples/Example44.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Word', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example44 = Document -Name 'PScribo Example 44' { 11 | 12 | <# 13 | Custom numbered lists are registered with the 'NumberStyle' keyword, but only the Word and 14 | Text plugins are supported. All other plugins will render the number as a decimal (using the 15 | 'Number' format). 16 | 17 | Custom number lists can contain any wording and punctuation you require. 18 | 19 | NOTE: The '-Uppercase' and '-Suffix' parameters are ignored so you need to include any suffix 20 | in the number format definition. 21 | 22 | The '%' token is used to denote where the number will be placed. To include leading zeroes, 23 | use multiple '%' tokens, e.g. 'ab%%' for ab01, ab02 and 'XYZ-%%%' for XYZ-001, XYZ-002, etc.. 24 | #> 25 | NumberStyle -Id 'CustomNumberStyle' -Custom 'xYz-%%%.' -Indent 1500 -Hanging 200 -Align Left 26 | 27 | <# 28 | Output list using the 'Custom' number style 29 | #> 30 | 31 | List -Numbered -NumberStyle CustomNumberStyle -Item 'Apples','Bananas','Oranges' 32 | 33 | } 34 | $example44 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 35 | -------------------------------------------------------------------------------- /Examples/Example02.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.Management.Automation.SwitchParameter] $PassThru 4 | ) 5 | 6 | Import-Module PScribo -Force -Verbose:$false 7 | 8 | $example2 = Document -Name 'PScribo Example 2' { 9 | Paragraph 'This is an example that adds a single paragraph to the document that will be styled with the default style.' 10 | } 11 | 12 | <# 13 | Once we have a reference to a PScribo document, it can be exported with the Export-Document cmdlet. 14 | The Export-Document cmdlet can export to one or more formats: Word, Html, Text and Html are included 15 | by default. 16 | 17 | This command converts/exports the $example2 document to a text formatted file named 'PScribo Example 2.txt' on the desktop. 18 | #> 19 | Export-Document -Document $example2 -Format Text -Path ~\Desktop 20 | 21 | <# 22 | This command converts/exports the $example2 document to a html formatted file named 'PScribo Example 2.html' on the desktop. 23 | #> 24 | Export-Document -Document $example2 -Format Html -Path ~\Desktop 25 | 26 | <# 27 | This command exports the $example2 document to both Word formatted 'PScribo Example 2.docx' and html formatted 28 | 'PScribo Example 2.html' files on the desktop, via the pipeline. 29 | 30 | NOTE: This will overwrite the 'PScribo Example 2.html' created above without warning. 31 | #> 32 | $example2 | Export-Document -Format Word,Html -Path ~\Desktop -PassThru:$PassThru 33 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-RomanNumeral.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-RomanNumeral 2 | { 3 | <# 4 | .SYNOPSIS 5 | Converts a decimal number to Roman numerals. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 11 | [System.Int32] $Value 12 | ) 13 | begin 14 | { 15 | $conversionTable = [ordered] @{ 16 | 1000 = 'M' 17 | 900 = 'CM' 18 | 500 = 'D' 19 | 400 = 'CD' 20 | 100 = 'C' 21 | 90 = 'XC' 22 | 50 = 'L' 23 | 40 = 'XL' 24 | 10 = 'X' 25 | 9 = 'IX' 26 | 5 = 'V' 27 | 4 = 'IV' 28 | 1 = 'I' 29 | } 30 | } 31 | process 32 | { 33 | $romanNumeralBuilder = New-Object -TypeName System.Text.StringBuilder 34 | do 35 | { 36 | foreach ($romanNumeral in $conversionTable.GetEnumerator()) 37 | { 38 | if ($Value -ge $romanNumeral.Key) 39 | { 40 | [ref] $null = $romanNumeralBuilder.Append($romanNumeral.Value) 41 | $Value -= $romanNumeral.Key 42 | break 43 | } 44 | } 45 | 46 | } 47 | until ($Value -eq 0) 48 | return $romanNumeralBuilder.ToString() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Examples/Example30.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example30 = Document -Name 'PScribo Example 30' { 11 | 12 | <# 13 | When sending PScribo output as a HTML email body, Outlook does not necessarily 14 | display the page styling/look-and-feel correctly. This can be suppressed by 15 | passing an -Options hashtable with the 'NoPageLayoutStyle' key to the 16 | Export-Document function. 17 | 18 | Setting this option, removes the page layout styling, rendering the contents the 19 | full width of the web browser (or Outlook message) window. 20 | 21 | NOTE: Regardless, Outlook might not render the HTML5 content correctly due its 22 | usage of the Internet Explorer rendering engine. 23 | #> 24 | DocumentOption -Margin 18 25 | Style -Name StoppedService -Color White -BackgroundColor Firebrick 26 | 27 | $services = Get-Service 28 | $services | Where-Object { $_.Status -ne 'Running' } | Set-Style -Style 'StoppedService' 29 | Table -InputObject $services -Columns Name,DisplayName,Status -Headers 'Name','Display Name','State' 30 | } 31 | $example30 | Export-Document -Format $Format -Path $Path -Options @{ NoPageLayoutStyle = $true } -PassThru:$PassThru 32 | -------------------------------------------------------------------------------- /Examples/Example25.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example25 = Document -Name 'PScribo Example 25' { 11 | 12 | <# 13 | The following example combines the creation of multiple custom styles with the 14 | definition of a custom table style. The custom "BlueZebra" table style is then 15 | applied to a -List view table of the 11th to last service. 16 | 17 | NOTE: List view tables do apply the -AlternateRowStyle styling, but this style 18 | does not include an alternate row style. 19 | #> 20 | Style -Name 'BlueZebraHeading' -Bold -Color 039 -Font 'Segoe UI' -BackgroundColor E8EDFF 21 | Style -Name 'BlueZebraRow' -Color 669 -Font 'Lucida Sans Unicode' 22 | TableStyle -Name 'BlueZebra' -HeaderStyle BlueZebraHeading -RowStyle BlueZebraRow -PaddingTop 4 -PaddingRight 4 -PaddingBottom 4 -PaddingLeft 4 -BorderWidth 1 -BorderColor E8EDFF 23 | 24 | <# 25 | Create a standard table using the new "BlueZebra" table style. 26 | #> 27 | Get-Service | Select-Object -Last 1 -Skip 10 | Table -Columns 'Name','DisplayName','Status' -Headers 'Name','Display Name','State' -ColumnWidths 25,75 -Style BlueZebra -List 28 | } 29 | $example25 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 30 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboFormattedTableRowCell.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboFormattedTableRowCell 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a formatted table cell for plugin output/rendering. 6 | #> 7 | [CmdletBinding()] 8 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 9 | [OutputType([System.Management.Automation.PSObject])] 10 | param 11 | ( 12 | [Parameter(ValueFromPipeline)] 13 | [AllowNull()] 14 | [System.String[]] $Content, 15 | 16 | [Parameter(ValueFromPipelineByPropertyName)] 17 | [System.Uint16] $Width, 18 | 19 | [Parameter(ValueFromPipelineByPropertyName)] 20 | [AllowNull()] 21 | [System.String] $Style = $null 22 | ) 23 | process 24 | { 25 | $isStyleInherited = $true 26 | $combinedContent = $Content 27 | 28 | if ($Content -is [System.Array]) 29 | { 30 | $combinedContent = [System.String]::Join([System.Environment]::NewLine, $Content) 31 | } 32 | 33 | if (-not ([System.String]::IsNullOrEmpty($Style))) 34 | { 35 | ## Use the explictit style 36 | $isStyleInherited = $false 37 | } 38 | 39 | return [PSCustomObject] @{ 40 | Content = $combinedContent 41 | Width = $Width 42 | Style = $Style 43 | IsStyleInherited = $isStyleInherited 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/Plugins/Html/Out-HtmlPageBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Html\Out-HtmlPageBreak' { 10 | 11 | ## Scaffold new document to initialise options/styles 12 | $Document = Document -Name 'Test' -ScriptBlock { } 13 | $script:currentPageNumber = 1 14 | $text = Out-HtmlPageBreak -Orientation Portrait 15 | 16 | It 'closes previous tags' { 17 | $text.StartsWith('') | Should Be $true 18 | } 19 | 20 | It 'creates new
' { 21 | $text -match '
' | Should Be $true 22 | } 23 | 24 | It 'sets page class to default style' { 25 | $divStyleMatch = '
24 | $hashtableArray = @( 25 | [Ordered] @{ 'Column 1' = 'Some random text'; 'Column2' = 345; 'Custom Property' = $true; } 26 | [Ordered] @{ 'Column 1' = 'Random some text'; 'Column2' = 16; 'Custom Property' = $false; } 27 | [Ordered] @{ 'Column 1' = 'Text random some'; 'Column2' = 1GB; 'Custom Property' = $true; } 28 | ) 29 | Table -Hashtable $hashtableArray 30 | } 31 | $example17 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 32 | -------------------------------------------------------------------------------- /Src/Private/Get-PScriboImage.ps1: -------------------------------------------------------------------------------- 1 | function Get-PScriboImage 2 | { 3 | <# 4 | .SYNOPSIS 5 | Retrieves PScribo.Images in a document/section 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Management.Automation.PSObject])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 12 | [System.Management.Automation.PSObject[]] $Section, 13 | 14 | [Parameter(ValueFromPipelineByPropertyName)] 15 | [System.String[]] $Id 16 | ) 17 | process 18 | { 19 | foreach ($subSection in $Section) 20 | { 21 | if ($subSection.Type -eq 'PScribo.Image') 22 | { 23 | if ($PSBoundParameters.ContainsKey('Id')) 24 | { 25 | if ($subSection.Id -in $Id) 26 | { 27 | Write-Output -InputObject $subSection 28 | } 29 | } 30 | else 31 | { 32 | Write-Output -InputObject $subSection 33 | } 34 | } 35 | elseif ($subSection.Type -eq 'PScribo.Section') 36 | { 37 | if ($subSection.Sections.Count -gt 0) 38 | { 39 | ## Recursively search subsections 40 | $PSBoundParameters['Section'] = $subSection.Sections 41 | Get-PScriboImage @PSBoundParameters 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Out-HtmlTOC.ps1: -------------------------------------------------------------------------------- 1 | function Out-HtmlTOC 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates Html table of contents. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $TOC 13 | ) 14 | process 15 | { 16 | $tocBuilder = New-Object -TypeName 'System.Text.StringBuilder' 17 | [ref] $null = $tocBuilder.AppendFormat('

{1}

', $TOC.ClassId, $TOC.Name) 18 | #[ref] $null = $tocBuilder.AppendLine('
') 19 | [ref] $null = $tocBuilder.AppendLine('
') 20 | foreach ($tocEntry in $Document.TOC) 21 | { 22 | $sectionNumberIndent = '   ' * $tocEntry.Level 23 | if ($Document.Options['EnableSectionNumbering']) 24 | { 25 | [ref] $null = $tocBuilder.AppendFormat('', $tocEntry.Number, $sectionNumberIndent, $tocEntry.Id, $tocEntry.Name).AppendLine() 26 | } 27 | else 28 | { 29 | [ref] $null = $tocBuilder.AppendFormat('', $sectionNumberIndent, $tocEntry.Id, $tocEntry.Name).AppendLine() 30 | } 31 | } 32 | [ref] $null = $tocBuilder.AppendLine('
{0}{1}{3}
{0}{2}
') 33 | return $tocBuilder.ToString() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Src/Private/ConvertTo-PSObjectKeyedListTable.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-PSObjectKeyedListTable 2 | { 3 | <# 4 | .SYNOPSIS 5 | Converts a PScribo.Table to a [PSCustomObject] collection representing a keyed list table. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Management.Automation.PSObject])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Management.Automation.PSObject] $Table 13 | ) 14 | process 15 | { 16 | $listKey = $Table.ListKey 17 | $rowHeaders = $Table.Rows | Select-Object -ExpandProperty $listKey 18 | $columnHeaders = $Table.Rows | 19 | Select-Object -First 1 -Property * -ExcludeProperty '*__Style' | 20 | ForEach-Object { $_.PSObject.Properties.Name } | 21 | Where-Object { $_ -ne $listKey } 22 | 23 | foreach ($columnHeader in $columnHeaders) 24 | { 25 | $psCustomObjectParams = [Ordered] @{ 26 | $listKey = $columnHeader 27 | } 28 | foreach ($rowHeader in $rowHeaders) 29 | { 30 | $psCustomObjectParams[$rowHeader] = $Table.Rows | 31 | Where-Object { $_.$listKey -eq $rowHeader } | 32 | Select-Object -ExpandProperty $columnHeader 33 | } 34 | $psCustomObject = [PSCustomObject] $psCustomObjectParams 35 | Write-Output -InputObject $psCustomObject 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Examples/Example33.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example33 = Document -Name 'PScribo Example 33' { 11 | 12 | <# 13 | A keyed list combines a collection of objects by a single property (key) 14 | into a single table. All other properties are displayed as individual 15 | rows. 16 | #> 17 | 18 | $servers = @( 19 | [Ordered] @{ ComputerName = 'DC1'; DomainName = 'example.local'; FQDN = 'dc1.example.local'; IpAddress = '192.168.0.1' } 20 | [Ordered] @{ ComputerName = 'DC2'; DomainName = 'example.local'; FQDN = 'dc2.example.local'; IpAddress = '192.168.0.2' } 21 | [Ordered] @{ ComputerName = 'DC3'; DomainName = 'example.local'; FQDN = 'dc3.example.local'; IpAddress = '192.168.0.3' } 22 | ) 23 | 24 | Table -Hashtable $servers -List -Key 'ComputerName' 25 | 26 | <# 27 | The table above, will be rendered like so: 28 | 29 | ComputerName DC1 DC2 DC3 30 | ------------ --- --- --- 31 | DomainName example.local example.local example.local 32 | FQDN dc1.example.local dc2.example.local dc3.example.local 33 | IpAddress 192.168.0.1 192.168.0.2 192.168.0.3 34 | #> 35 | 36 | } 37 | $example33 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 38 | -------------------------------------------------------------------------------- /Src/Private/Invoke-PScriboSectionLevel.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PScriboSectionLevel 2 | { 3 | <# 4 | .SYNOPSIS 5 | Nested function that processes each document/TOC nested section 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory)] 11 | [ValidateNotNull()] 12 | [System.Management.Automation.PSObject] $Section, 13 | 14 | [Parameter(Mandatory)] 15 | [ValidateNotNullOrEmpty()] 16 | [System.String] $Number 17 | ) 18 | process 19 | { 20 | if ($pscriboDocument.Options['ForceUppercaseSection']) 21 | { 22 | $Section.Name = $Section.Name.ToUpper(); 23 | } 24 | 25 | ## Set this section's level 26 | $Section.Number = $Number; 27 | $Section.Level = $Number.Split('.').Count -1; 28 | ### Add to the TOC 29 | $tocEntry = [PScustomObject] @{ Id = $Section.Id; Number = $Number; Level = $Section.Level; Name = $Section.Name; } 30 | [ref] $null = $pscriboDocument.TOC.Add($tocEntry); 31 | ## Set sub-section level seed 32 | $minorNumber = 1; 33 | foreach ($s in $Section.Sections) 34 | { 35 | if ($s.Type -like '*.Section' -and -not $s.IsExcluded) 36 | { 37 | $sectionNumber = ('{0}.{1}' -f $Number, $minorNumber).TrimStart('.'); ## Calculate section version 38 | Invoke-PScriboSectionLevel -Section $s -Number $sectionNumber; 39 | $minorNumber++; 40 | } 41 | } #end foreach section 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Src/Plugins/Text/ConvertTo-JustifiedString.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-JustifiedString 2 | { 3 | <# 4 | .SYNOPSIS 5 | Justifies a block of text using the specified alignment. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Object[]] $InputObject, 14 | 15 | ## Text alignment 16 | [Parameter(ValueFromPipelineByPropertyName)] 17 | [ValidateSet('Left','Center','Right','Justify')] 18 | [System.String] $Align = 'Left', 19 | 20 | ## Text width 21 | [Parameter()] 22 | [System.Int32] $Width = ($Host.UI.RawUI.BufferSize.Width -1) 23 | ) 24 | process 25 | { 26 | foreach ($object in $InputObject) 27 | { 28 | $objectString = $object.ToString() 29 | if (($Align -eq 'Left') -or ($objectString.Length -ge $Width)) 30 | { 31 | $justifiedString = $objectString 32 | } 33 | else 34 | { 35 | $paddingLength = $Width - $objectString.Length 36 | if ($Align -eq 'Center') 37 | { 38 | $paddingLength = ($paddingLength /2) -as [System.Int32] 39 | } 40 | $padding = ''.PadRight($paddingLength, ' ') 41 | $justifiedString = '{0}{1}' -f $padding, $objectString 42 | } 43 | Write-Output -InputObject $justifiedString 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /en-US/about_HtmlPlugin.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | Html Plugin 3 | 4 | SYNOPSIS 5 | PScribo supports outputting hypertext markup language (.html) documents. 6 | 7 | DESCRIPTION 8 | PScribo can output HTML documents for rendering in a web broswer. 9 | 10 | KNOWN LIMITATIONS 11 | There are some restrictions emposed by Html documents which means that some PScribo functionality cannot 12 | either be partially or fully implemented: 13 | 14 | - Html output attempts to create a document-like experience. Paper sizes, page breaks and headers/footers are 15 | rendered, but page numbers cannot be faithfully recreated. 16 | - Html output does not support the 'Dash' bullet style. Dashes will be rendered using the the web broswer's 17 | defaults. 18 | - Html numbered lists only support the default '.' number style terminator/suffix. The use of custom number 19 | style terminators/suffixes i.e. ')', is not supported. 20 | 21 | PLUGIN OPTIONS 22 | The Html plugin accepts the following output customisation options: 23 | 24 | NoPageLayoutStyle [bool] : Removes the page layout style and renders the content the full width of the 25 | browser window. If not specified, defaults to '$true'. 26 | 27 | Output customisations are passed to the Export-Document cmdlet as a hashtable, e.g. 28 | 29 | PS> $document | Export-Document -Format Html -Options @{ NoPageLayoutStyle = $true } 30 | 31 | SEE ALSO 32 | about_PscriboPlugins 33 | about_TextPlugin 34 | about_JsonPlugin 35 | about_WordPlugin 36 | -------------------------------------------------------------------------------- /Examples/Example23.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example23 = Document -Name 'PScribo Example 23' { 11 | 12 | <# 13 | By default, PScribo adds padding to each table cell within a table. Different amounts 14 | of padding can be applied to the top, right, bottom and left of each table cell. The 15 | padding is specified in points (pt). 16 | 17 | NOTE: Table padding applies to both standard tables and -List view tables. 18 | 19 | The default padding attempts to ensure that table padding renders evenly in both 20 | Html and Word without adding too much space. The default values are as follows: 21 | 22 | Padding-Top : 1.0pt 23 | Padding-Right : 4.0pt 24 | Padding-Bottom: 4.0pt 25 | Padding-Left : 0.0pt 26 | 27 | The following example creates a custom table style that creates even padding around 28 | all table cells. 29 | 30 | NOTE: a -BorderWidth has been specified to demonstrate the padding but is not required! 31 | #> 32 | TableStyle -Name 'LargeGrid' -HeaderStyle Normal -RowStyle Normal -BorderWidth 1 -PaddingTop 4 -PaddingRight 4 -PaddingBottom 4 -PaddingLeft 4 33 | Get-Service | Select-Object -Property Name,DisplayName,Status -First 3 | Table -Style LargeGrid 34 | } 35 | $example23 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 36 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboHeaderFooter.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboHeaderFooter 2 | { 3 | <# 4 | .SYNOPSIS 5 | Initializes a new PScribo header/footer object. 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','Footer')] 13 | [OutputType([System.Management.Automation.PSCustomObject])] 14 | param 15 | ( 16 | [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Header')] 17 | [System.Management.Automation.SwitchParameter] $Header, 18 | 19 | [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Footer')] 20 | [System.Management.Automation.SwitchParameter] $Footer 21 | ) 22 | begin 23 | { 24 | ## Ignores dot-sourced script blocks, i.e. style scripts 25 | $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '' } 26 | if ($psCallStack[2].FunctionName -ne 'Document') 27 | { 28 | throw $localized.HeaderFooterDocumentRootError 29 | } 30 | } 31 | process 32 | { 33 | $pscriboHeaderFooter = [PSCustomObject] @{ 34 | Type = if ($Header) { 'PScribo.Header' } else { 'PScribo.Footer' } 35 | Sections = (New-Object -TypeName System.Collections.ArrayList) 36 | } 37 | return $pscriboHeaderFooter 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /en-US/about_WordPlugin.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | Word Plugin 3 | 4 | SYNOPSIS 5 | PScribo supports outputting Microsoft Word (.docx) documents. 6 | 7 | DESCRIPTION 8 | PScribo can output Office Open XML (OOXML) documents for rendering with Microsoft Word 2013 (and later). 9 | 10 | KNOWN LIMITATIONS 11 | There are some restrictions emposed by Word documents which means that some PScribo functionality cannot 12 | either be partially or fully implemented: 13 | 14 | - When opening documents that include a Table of Contents (TOC), Word displays a 'This document contains fields 15 | that may refer to other files.' warning message: 16 | - When the Word document is generated, page numbers are unknown. The 'updateFields' settings flag is used to 17 | instruct Word to update all field references, i.e. page numbers. 18 | - Selecting 'No' to this warning will render an empty Table of Contents 19 | - Whilst LibreOffice can open documents generated by PScribo, the following features are not currently 20 | implemented: 21 | - Table of Contents are not rendered as the 'updateFields' flag is not implemented/supported 22 | - Table cell font colors are not rendered correctly 23 | - Word does not support a mixture of formats at the same level within a bullet/numbered list. Therefore, only the 24 | first list type will be rendered. 25 | 26 | PLUGIN OPTIONS 27 | The Word plugin does not accept any output customisation options. 28 | 29 | SEE ALSO 30 | about_PscriboPlugins 31 | about_HtmlPlugin 32 | about_TextPlugin 33 | about_JsonPlugin 34 | -------------------------------------------------------------------------------- /Examples/Example15.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example15 = Document -Name 'PScribo Example 15' { 11 | 12 | <# 13 | You can optionally set the width of each column by using the -ColumnWidths 14 | parameter on the 'Table' cmdlet. 15 | 16 | NOTE: You can only specify column widths if you also specify the -Columns 17 | parameter. 18 | 19 | Just like the Table -Width parameter, the column widths are specified in 20 | percentages (of overall table width). 21 | 22 | NOTE: The total of all columns widths must total exactly 100 (%). 23 | 24 | The following example retrieves all local services, displaying the Name, 25 | DisplayName and Status properties. The column width for the Name property 26 | is set to 30%, the column width for the DisplayName property is set to 50% 27 | and the Status property column width set to 20%. 28 | 29 | NOTE: It is recommended that you set column widths on all tables to ensure 30 | consistent formatting between HTML and Word outputs. 31 | 32 | NOTE: Column widths are not implemented for Text output rendering as they 33 | are not supported/implemented in the 'Format-Table' cmdlet. 34 | #> 35 | Get-Service | Table -Columns Name,DisplayName,Status -ColumnWidths 30,50,20 36 | } 37 | $example15 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 38 | -------------------------------------------------------------------------------- /Examples/Example03.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | Document -Name 'PScribo Example 3' { 11 | 12 | <# 13 | By default, a PScribo document will default to an A4 page size. The default top and bottom 14 | pages margins are configured at 25.4mm (or 1 inch). The default left and right page margins 15 | are set at 19.05mm (or 3/4 inch). 16 | 17 | You can override the defaults with the 'DocumentOption' cmdlet/keyword. The following sets the 18 | page size to US Letter. 19 | #> 20 | DocumentOption -PageSize Letter 21 | 22 | <# 23 | The page margins are specified in points (pt). The follow cmdlet sets all page margins to 24 | 12.7mm (or 0.5 inch). 25 | #> 26 | DocumentOption -Margin 36 27 | 28 | <# 29 | The top and bottom page margins can be set independently from the left and right margin. The 30 | following configures the top/bottom page margin to 19.05mm (3/4 inch) and the left/right page 31 | margin to 12.7mm (0.5 inch). 32 | #> 33 | DocumentOption -MarginTopAndBottom 54 -MarginLeftAndRight 36 34 | 35 | <# 36 | Multiple options can be specified in a single call to 'DocumentOption', e.g. setting the 37 | page size and margins like so: 38 | #> 39 | DocumentOption -PageSize Letter -MarginTopAndBottom 54 -MarginLeftAndRight 36 40 | 41 | } | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 42 | -------------------------------------------------------------------------------- /Tests/Plugins/Html/Out-HtmlStyle.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Html\Out-HtmlStyle' { 10 | 11 | It 'creates ' | Should Be $true 17 | } 18 | 19 | It 'creates page layout style by default' { 20 | $Document = Document -Name 'Test' -ScriptBlock { } 21 | $text = Out-HtmlStyle -Styles $Document.Styles -TableStyles $Document.TableStyles 22 | 23 | $text -match 'html {' | Should Be $true 24 | $text -match 'page {' | Should Be $true 25 | $text -match '@media print {' | Should Be $true 26 | } 27 | 28 | It "suppresses page layout style when 'Options.NoPageLayoutSyle' specified" { 29 | $Document = Document -Name 'Test' -ScriptBlock { } 30 | $text = Out-HtmlStyle -Styles $Document.Styles -TableStyles $Document.TableStyles -NoPageLayoutStyle 31 | 32 | $text -match 'html {' | Should Be $false 33 | $text -match 'page {' | Should Be $false 34 | $text -match '@media print {' | Should Be $false 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/Plugins/Text/Out-TextLineBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | $isNix = $false 10 | if (($PSVersionTable['PSEdition'] -eq 'Core') -and (-not $IsWindows)) 11 | { 12 | $isNix = $true 13 | } 14 | 15 | 16 | Describe 'Plugins\Text\Out-TextLineBreak' { 17 | 18 | ## Scaffold document options 19 | $pscriboDocument = Document -Name 'TestDocument' -ScriptBlock {} 20 | $Options = New-PScriboTextOption; 21 | 22 | It 'Defaults to 120 and includes new line' { 23 | $expected = 122 24 | if ($isNix) { $expected -= 1 } 25 | 26 | $l = Out-TextLineBreak 27 | 28 | $l.Length | Should Be $expected 29 | } 30 | 31 | It 'Truncates to 40 and includes new line' { 32 | $Options = New-PScriboTextOption -TextWidth 40 -SeparatorWidth 40 33 | $expected = 42 34 | if ($isNix) { $expected -= 1 } 35 | 36 | $l = Out-TextLineBreak 37 | 38 | $l.Length | Should Be $expected 39 | } 40 | 41 | It 'Wraps lines and includes new line' { 42 | $Options = New-PScriboTextOption -TextWidth 40 -SeparatorWidth 80 43 | $expected = 84 44 | if ($isNix) { $expected -= 2 } 45 | 46 | $l = Out-TextLineBreak 47 | 48 | $l.Length | Should Be $expected 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Src/Plugins/Xml/Out-XmlTable.ps1: -------------------------------------------------------------------------------- 1 | function Out-XmlTable 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Xml table. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | ## PScribo table object 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Table 14 | ) 15 | process 16 | { 17 | $tableId = ($Table.Id -replace '[^a-z0-9-_\.]','').ToLower() 18 | $tableElement = $element.AppendChild($xmlDocument.CreateElement($tableId)) 19 | [ref] $null = $tableElement.SetAttribute('name', $Table.Name) 20 | 21 | foreach ($row in $Table.Rows) 22 | { 23 | $groupElement = $tableElement.AppendChild($xmlDocument.CreateElement('group')) 24 | foreach ($property in $row.PSObject.Properties) 25 | { 26 | if (-not ($property.Name).EndsWith('__Style', 'CurrentCultureIgnoreCase')) 27 | { 28 | $propertyId = ($property.Name -replace '[^a-z0-9-_\.]','').ToLower() 29 | $rowElement = $groupElement.AppendChild($xmlDocument.CreateElement($propertyId)) 30 | ## Only add the Name attribute if there's a difference 31 | if ($property.Name -ne $propertyId) 32 | { 33 | [ref] $null = $rowElement.SetAttribute('name', $property.Name) 34 | } 35 | [ref] $null = $rowElement.AppendChild($xmlDocument.CreateTextNode($row.($property.Name))) 36 | } 37 | } 38 | } 39 | 40 | return $tableElement 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordSettingsDocument.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordSettingsDocument 2 | { 3 | <# 4 | .SYNOPSIS 5 | Outputs Office Open XML settings document 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Xml.XmlDocument])] 9 | param 10 | ( 11 | [Parameter()] 12 | [System.Management.Automation.SwitchParameter] $UpdateFields 13 | ) 14 | process 15 | { 16 | ## Create the Style.xml document 17 | $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' 18 | $settingsDocument = New-Object -TypeName 'System.Xml.XmlDocument' 19 | [ref] $null = $settingsDocument.AppendChild($settingsDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) 20 | $settings = $settingsDocument.AppendChild($settingsDocument.CreateElement('w', 'settings', $xmlns)) 21 | 22 | ## Set compatibility mode to Word 2013 23 | $compat = $settings.AppendChild($settingsDocument.CreateElement('w', 'compat', $xmlns)) 24 | $compatSetting = $compat.AppendChild($settingsDocument.CreateElement('w', 'compatSetting', $xmlns)) 25 | [ref] $null = $compatSetting.SetAttribute('name', $xmlns, 'compatibilityMode') 26 | [ref] $null = $compatSetting.SetAttribute('uri', $xmlns, 'http://schemas.microsoft.com/office/word') 27 | [ref] $null = $compatSetting.SetAttribute('val', $xmlns, 15) 28 | 29 | if ($UpdateFields) 30 | { 31 | $wupdateFields = $settings.AppendChild($settingsDocument.CreateElement('w', 'updateFields', $xmlns)) 32 | [ref] $null = $wupdateFields.SetAttribute('val', $xmlns, 'true') 33 | } 34 | 35 | return $settingsDocument 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Examples/Example01.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String] $Path = '~\Desktop\Example1.CliXml', 4 | [System.Management.Automation.SwitchParameter] $PassThru 5 | ) 6 | 7 | <# 8 | From Powershell v3 onwards, the module should not need to be explicitly imported. It is included 9 | here to avoid any ambiguity. 10 | #> 11 | Import-Module PScribo -Force -Verbose:$false 12 | 13 | <# 14 | We create a PScribo document with the 'Document' cmdlet. Inside the supplied script block, you can include any 15 | standard Powershell code as well as additional PScribo content, such as paragraphs. The following code creates a 16 | new blank document named 'PScribo Example 1', storing it in the $example1 variable. 17 | 18 | When documents are exported, they are exported using the name of the document appended with the extension of the 19 | plugin. For example, exporting 'PScribo Example 1' to a Word document results in a 'PScribo Example 1.docx' file. 20 | #> 21 | 22 | $example1 = Document -Name 'PScribo Example 1' { 23 | <# 24 | This creates a single paragraph within the 'Example 1' document 25 | #> 26 | Paragraph 'This is an example that adds a single paragraph to the document that will be styled with the default style.' 27 | } 28 | 29 | <# 30 | If needed the PScribo document stored in variable $example1 could be exported via the Export-CliXml cmdlet. This would 31 | permit the creation of a document on one machine, and converted into an external document format on another. The following 32 | command will export the 'PScribo Example 1' document to a file named Example1.CliXml on the desktop. 33 | #> 34 | $example1 | Export-Clixml -Depth 128 -Path $Path -Force 35 | -------------------------------------------------------------------------------- /Tests/Plugins/Text/Out-TextPageBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | $isNix = $false 10 | if (($PSVersionTable['PSEdition'] -eq 'Core') -and (-not $IsWindows)) 11 | { 12 | $isNix = $true 13 | } 14 | 15 | 16 | Describe 'Plugins\Text\Out-TextPageBreak' { 17 | 18 | ## Scaffold document options 19 | $Document = Document -Name 'TestDocument' -ScriptBlock {} 20 | $script:currentPageNumber = 1 21 | $Options = New-PScriboTextOption 22 | 23 | It 'Defaults to 120 and includes 2 new lines' { 24 | $expected = 124 25 | if ($isNix) { $expected -= 2 } 26 | 27 | $l = Out-TextPageBreak 28 | 29 | $l.Length | Should Be $expected 30 | } 31 | 32 | It 'Truncates to 40 and includes 2 new lines' { 33 | $Options = New-PScriboTextOption -TextWidth 40 -SeparatorWidth 40 34 | $expected = 44 35 | if ($isNix) { $expected -= 2 } 36 | 37 | $l = Out-TextPageBreak 38 | 39 | $l.Length | Should Be $expected 40 | } 41 | 42 | It 'Wraps lines and includes new line' { 43 | $Options = New-PScriboTextOption -TextWidth 40 -SeparatorWidth 80 44 | $expected = 86 45 | if ($isNix) { $expected -= 3 } 46 | 47 | $l = Out-TextPageBreak 48 | 49 | $l.Length | Should Be $expected 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Src/Private/Merge-PScriboPluginOption.ps1: -------------------------------------------------------------------------------- 1 | function Merge-PScriboPluginOption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Merges the specified options along with the plugin-specific default options. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Collections.Hashtable])] 9 | param 10 | ( 11 | ## Default/document options 12 | [Parameter(Mandatory, ValueFromPipelineByPropertyName)] 13 | [AllowNull()] 14 | [System.Collections.Hashtable] $DocumentOptions, 15 | 16 | ## Default plugin options to merge 17 | [Parameter(ValueFromPipelineByPropertyName)] 18 | [System.Collections.Hashtable] $DefaultPluginOptions, 19 | 20 | ## Specified runtime plugin options 21 | [Parameter(ValueFromPipelineByPropertyName)] 22 | [AllowNull()] 23 | [System.Collections.Hashtable] $PluginOptions 24 | ) 25 | process 26 | { 27 | $mergedOptions = $DocumentOptions.Clone(); 28 | 29 | if ($null -ne $DefaultPluginOptions) 30 | { 31 | ## Overwrite the default document option with the plugin default option/value 32 | foreach ($option in $DefaultPluginOptions.GetEnumerator()) 33 | { 34 | $mergedOptions[$($option.Key)] = $option.Value; 35 | } 36 | } 37 | 38 | if ($null -ne $PluginOptions) 39 | { 40 | ## Overwrite the default document/plugin default option/value with the specified/runtime option 41 | foreach ($option in $PluginOptions.GetEnumerator()) 42 | { 43 | $mergedOptions[$($option.Key)] = $option.Value; 44 | } 45 | } 46 | return $mergedOptions; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tests/Plugins/Word/Out-WordPageBreak.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginsRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginsRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | function GetMatch 10 | { 11 | [CmdletBinding()] 12 | param 13 | ( 14 | [System.String] $String, 15 | [System.Management.Automation.SwitchParameter] $Complete 16 | ) 17 | Write-Verbose "Pre Match : '$String'" 18 | $matchString = $String.Replace('/','\/') 19 | if (-not $String.StartsWith('^')) 20 | { 21 | $matchString = $matchString.Replace('[..]','[\s\S]+') 22 | $matchString = $matchString.Replace('[??]','([\s\S]+)?') 23 | if ($Complete) 24 | { 25 | $matchString = '^{0}<\/w:test>$' -f $matchString 26 | } 27 | } 28 | Write-Verbose "Post Match: '$matchString'" 29 | return $matchString 30 | } 31 | 32 | Describe 'Plugins\Word\Out-WordPageBreak' { 33 | 34 | It 'outputs ""' { 35 | $document = Document -Name 'TestDocument' { 36 | PageBreak 37 | } 38 | 39 | $testDocument = Get-WordDocument -Document $document 40 | 41 | $expected = GetMatch '' 42 | $testDocument.OuterXml | Should Match $expected 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Examples/Example28.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example28 = Document -Name 'PScribo Example 28' { 11 | 12 | <# 13 | Custom styling can also be applied to individual cells within a table. To 14 | flag an individual object property to apply a different style, an additional 15 | property must be added to the object(s). Instead of adding a '__Style' property 16 | add a '__Style' string property. 17 | 18 | For example, to style a service 'Name' property with a custom style, add an 19 | additional property called 'Name__Style' with the style to apply. 20 | 21 | NOTE: Individual cell styling will override any row style applied via the 22 | '__Style' property. 23 | 24 | How the properties are added does not matter. However, the Set-Style cmdlet 25 | can also apply a specified style to one or more object properties with 26 | the -Property parameter. 27 | 28 | The following example styles the individual table cells wherever the service 29 | status is not 'Running'. 30 | #> 31 | Style -Name StoppedService -Color White -BackgroundColor Firebrick 32 | 33 | $services = Get-Service 34 | $services | Where-Object { $_.Status -ne 'Running' } | Set-Style -Style 'StoppedService' -Property 'Status' 35 | 36 | Table -InputObject $services -Columns Name,DisplayName,Status -Headers 'Name','Display Name','State' 37 | } 38 | $example28 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 39 | -------------------------------------------------------------------------------- /Examples/Example27.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example27 = Document -Name 'PScribo Example 27' { 11 | 12 | <# 13 | Custom styling can also be applied to rows of one or more hashtables. 14 | 15 | The following defines a custom style that we will apply to particular objects 16 | within the collection. 17 | #> 18 | Style -Name StoppedService -Color White -BackgroundColor Firebrick 19 | 20 | <# 21 | To apply a style to a particular hashtable, just ensure that a '__Style' key 22 | is defined with the required style name to apply to the row! 23 | 24 | In the following example, the first and last rows in the array of hashtables 25 | will be styled with the 'StoppedService' style. 26 | 27 | NOTE: How the '__Style' key is added to a hashtable is up to you. It could 28 | be added programatically etc. 29 | #> 30 | $hashtableArray = @( 31 | [Ordered] @{ 'Column 1' = 'Some random text'; 'Column2' = 345; 'Custom Property' = $true; '__Style' = 'StoppedService'; } 32 | [Ordered] @{ 'Column 1' = 'Random some text'; 'Column2' = 16; 'Custom Property' = $false; } 33 | [Ordered] @{ 'Column 1' = 'Random text some'; 'Column2' = 241; 'Custom Property' = $true; } 34 | [Ordered] @{ 'Column 1' = 'Text random some'; 'Column2' = 1GB; 'Custom Property' = $true; '__Style' = 'StoppedService'; } 35 | ) 36 | Table -Hashtable $hashtableArray 37 | } 38 | $example27 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 39 | -------------------------------------------------------------------------------- /en-US/about_PScribo.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | PScribo 3 | 4 | SYNOPSIS 5 | PScribo is a documentation framework for Windows PowerShell and PowerShell (Core). 6 | 7 | DESCRIPTION 8 | PScribo is an open source project that implements a documentation domain-specific-language (DSL) for Windows 9 | Powershell. The latest version is available at https://github.com/iainbrighton/PScribo. 10 | 11 | PScribo provides a set of functions that make it easy to create a document-like structure within Powershell without 12 | having to handle individual output formatting. A document's layout and contents only need to defined once. After 13 | creation, the document can be exported to one or more formats simultaneously. For more detailed infomation on the 14 | documentation DSL, see about_PScriboDocument. 15 | 16 | Pscribo can export documentation in a variety of formats and currently supports creation of text, Html and Word 17 | formats. Additional "plugins" can be created to support future formats if required. For more detailed information 18 | on plugins, see about_PScriboPlugins. 19 | 20 | PScribo is available as a Powershell module and supported on both Windows PowerShell and PowerShell (Core). 21 | 22 | CREATING A PSCRIBO DOCUMENT 23 | To start using PScribo, you need to either install the PScribo module in your Powershell module path or download 24 | the PScribo bundle. 25 | 26 | OTHER EXAMPLES 27 | Included in the PScribo module (not the bundle) are some heavily documented example scripts in the .\Examples 28 | directory. For an index of the examples, see about_PScriboExamples. 29 | 30 | SEE ALSO 31 | about_PScriboDocument 32 | about_PScriboStyles 33 | about_PScriboPlugins 34 | about_PScriboExamples 35 | -------------------------------------------------------------------------------- /en-US/about_JsonPlugin.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | Json Plugin 3 | 4 | SYNOPSIS 5 | PScribo supports outputting JSON-formatted text (.json) documents. 6 | 7 | DESCRIPTION 8 | PScribo can output JSON-formatted text documents with multiple file encodings. The benefit of JSON is it can be consumed by 3rd party tools after the fact to ingest the information contained in the report. 9 | 10 | KNOWN LIMITATIONS 11 | There are some restrictions which means that some PScribo functionality cannot either be partially or fully implemented: 12 | 13 | - PScribo style elements are ignored 14 | - This includes section numbering as it better ensures each section is unique which facilitates easier parsing. 15 | - The following PScribo types are not supported as there is no provision for them in JSON: 16 | - Image 17 | - PageBreak 18 | - LineBreak 19 | - BlankLine 20 | - Headers and footers are only shown once at the beginning/end of the generated document 21 | - Sections at the same hierarchical level using the NOTOC* header cannot have the same name. This is a PowerShell Dictionary limitation. 22 | 23 | PLUGIN OPTIONS 24 | The Text plugin accepts the following output customisation options: 25 | 26 | Encoding [string] : Specifies the file encoding to use. Supported values are 'ASCII', 'Unicode', 27 | 'UTF7' and 'UTF8'. If not specified, defaults to 'ASCII'. 28 | 29 | Output customisations are passed to the Export-Document cmdlet as a hashtable, e.g. 30 | 31 | PS> $document | Export-Document -Format Text -Options @{ Encoding = 'ASCII' } 32 | 33 | SEE ALSO 34 | about_PscriboPlugins 35 | about_HtmlPlugin 36 | about_WordPlugin 37 | about_TextPlugin 38 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordParagraphRun.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordParagraphRun 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns an array of strings, split on tokens (PageNumber/TotalPages). 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String[]])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 12 | [AllowEmptyString()] 13 | [System.String] $Text, 14 | 15 | [Parameter(ValueFromPipelineByPropertyName)] 16 | [System.Management.Automation.SwitchParameter] $NoSpace 17 | ) 18 | process 19 | { 20 | $pageNumberMatch = '' 21 | $totalPagesMatch = '' 22 | $runs = New-Object -TypeName 'System.Collections.ArrayList' 23 | 24 | if (-not $NoSpace) 25 | { 26 | $Text = '{0} ' -f $Text 27 | } 28 | 29 | $pageNumbers = $Text -isplit $pageNumberMatch 30 | for ($pn = 0; $pn -lt $pageNumbers.Count; $pn++) 31 | { 32 | $totalPages = $pageNumbers[$pn] -isplit $totalPagesMatch 33 | for ($tp = 0; $tp -lt $totalPages.Count; $tp++) 34 | { 35 | if (-not [System.String]::IsNullOrWhitespace($totalPages[$tp])) 36 | { 37 | $null = $runs.Add($totalPages[$tp]) 38 | } 39 | if ($tp -lt ($totalPages.Count -1)) 40 | { 41 | $null = $runs.Add('') 42 | } 43 | } 44 | 45 | if ($pn -lt ($pageNumbers.Count -1)) 46 | { 47 | $null = $runs.Add('') 48 | } 49 | } 50 | 51 | return $runs.ToArray() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/Plugins/Json/Out-JsonDocument.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Json\Out-JsonDocument' { 10 | 11 | $path = (Get-PSDrive -Name TestDrive).Root; 12 | 13 | It 'calls Out-JsonSection' { 14 | Mock -CommandName Out-JsonSection 15 | 16 | Document -Name 'TestDocument' -ScriptBlock { Section -Name 'TestSection' -ScriptBlock { } } | Out-JsonDocument -Path $path 17 | 18 | Assert-MockCalled -CommandName Out-JsonSection -Exactly 1 19 | } 20 | 21 | It 'calls Out-JsonParagraph' { 22 | Mock -CommandName Out-JsonParagraph 23 | 24 | Document -Name 'TestDocument' -ScriptBlock { Paragraph 'TestParagraph' } | Out-JsonDocument -Path $path 25 | 26 | Assert-MockCalled -CommandName Out-JsonParagraph -Exactly 1 27 | } 28 | 29 | It 'calls Out-JsonTable' { 30 | Mock -CommandName Out-JsonTable 31 | 32 | Document -Name 'TestDocument' -ScriptBlock { Get-Process | Select-Object -First 1 | Table 'TestTable' } | Out-JsonDocument -Path $path 33 | 34 | Assert-MockCalled -CommandName Out-JsonTable -Exactly 1 35 | } 36 | 37 | It 'calls Out-JsonTOC' { 38 | Mock -CommandName Out-JsonTOC 39 | 40 | Document -Name 'TestDocument' -ScriptBlock { TOC -Name 'TestTOC'; } | Out-JsonDocument -Path $path 41 | 42 | Assert-MockCalled -CommandName Out-JsonTOC -Exactly 1 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Src/Private/Invoke-PScriboListLevel.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PScriboListLevel 2 | { 3 | <# 4 | .SYNOPSIS 5 | Nested function that processes each nested list numbering. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory)] 11 | [ValidateNotNull()] 12 | [System.Management.Automation.PSObject] $List, 13 | 14 | [Parameter(Mandatory)] 15 | [AllowEmptyString()] 16 | [System.String] $Number 17 | ) 18 | process 19 | { 20 | $level = $Number.Split('.').Count +1 21 | $processingListPadding = ''.PadRight($level -1, ' ') 22 | $processingListMessage = $localized.ProcessingList -f $List.Name 23 | Write-PScriboMessage -Message ('{0}{1}' -f $processingListPadding, $processingListMessage) 24 | 25 | $itemNumber = 0 26 | $numberString = $Number 27 | $hasItem = $false 28 | 29 | foreach ($item in $List.Items) 30 | { 31 | if ($item.Type -eq 'PScribo.Item') 32 | { 33 | $itemNumber++ 34 | $item.Level = $level 35 | $item.Index = $itemNumber 36 | $item.Number = ('{0}.{1}' -f $Number, $itemNumber).TrimStart('.') 37 | 38 | $numberString = $item.Number 39 | $hasItem = $true 40 | } 41 | elseif ($item.Type -eq 'PScribo.List') 42 | { 43 | if ($hasItem) 44 | { 45 | Invoke-PScriboListLevel -List $item -Number $numberString 46 | } 47 | else 48 | { 49 | Write-PScriboMessage -Message $localized.NoPriorListItemWarning -IsWarning 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Src/Private/Get-PScriboHeaderFooter.ps1: -------------------------------------------------------------------------------- 1 | function Get-PScriboHeaderFooter 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns the specified PScribo header/footer object. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DefaultHeader')] 11 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageHeader')] 12 | [System.Management.Automation.SwitchParameter] $Header, 13 | 14 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DefaultFooter')] 15 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageFooter')] 16 | [System.Management.Automation.SwitchParameter] $Footer, 17 | 18 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageHeader')] 19 | [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageFooter')] 20 | [System.Management.Automation.SwitchParameter] $FirstPage 21 | ) 22 | process 23 | { 24 | if ($FirstPage) 25 | { 26 | if ($Header -and ($Document.Header.HasFirstPageHeader)) 27 | { 28 | return $Document.Header.FirstPageHeader 29 | } 30 | elseif ($Footer -and ($Document.Footer.HasFirstPageFooter)) 31 | { 32 | return $Document.Footer.FirstPageFooter 33 | } 34 | } 35 | else 36 | { 37 | if ($Header -and ($Document.Header.HasDefaultHeader)) 38 | { 39 | return $Document.Header.DefaultHeader 40 | } 41 | elseif ($Footer -and ($Document.Footer.HasDefaultFooter)) 42 | { 43 | return $Document.Footer.DefaultFooter 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordTableConditionalFormattingValue.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordTableConditionalFormattingValue 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates legacy table conditioning formatting value (for LibreOffice). 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Int32])] 9 | param 10 | ( 11 | [Parameter(ValueFromPipelineByPropertyName)] 12 | [System.Management.Automation.SwitchParameter] $HasFirstRow, 13 | 14 | [Parameter(ValueFromPipelineByPropertyName)] 15 | [System.Management.Automation.SwitchParameter] $HasLastRow, 16 | 17 | [Parameter(ValueFromPipelineByPropertyName)] 18 | [System.Management.Automation.SwitchParameter] $HasFirstColumn, 19 | 20 | [Parameter(ValueFromPipelineByPropertyName)] 21 | [System.Management.Automation.SwitchParameter] $HasLastColumn, 22 | 23 | [Parameter(ValueFromPipelineByPropertyName)] 24 | [System.Management.Automation.SwitchParameter] $NoHorizontalBand, 25 | 26 | [Parameter(ValueFromPipelineByPropertyName)] 27 | [System.Management.Automation.SwitchParameter] $NoVerticalBand 28 | ) 29 | process 30 | { 31 | $val = 0 32 | if ($HasFirstRow) 33 | { 34 | $val = $val -bor 0x20 35 | } 36 | if ($HasLastRow) 37 | { 38 | $val = $val -bor 0x40 39 | } 40 | if ($HasFirstColumn) 41 | { 42 | $val = $val -bor 0x80 43 | } 44 | if ($HasLastColumn) 45 | { 46 | $val = $val -bor 0x100 47 | } 48 | if ($NoHorizontalBand) 49 | { 50 | $val = $val -bor 0x200 51 | } 52 | if ($NoVerticalBand) 53 | { 54 | $val = $val -bor 0x400 55 | } 56 | return $val 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Src/Private/Get-ImageMimeType.ps1: -------------------------------------------------------------------------------- 1 | function Get-ImageMimeType 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns an image's Mime type 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.Drawing.Image] $Image 13 | ) 14 | process 15 | { 16 | if ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Jpeg)) 17 | { 18 | return 'image/jpeg' 19 | } 20 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Png)) 21 | { 22 | return 'image/png' 23 | } 24 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Bmp)) 25 | { 26 | return 'image/bmp' 27 | } 28 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Emf)) 29 | { 30 | return 'image/emf' 31 | } 32 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Gif)) 33 | { 34 | return 'image/gif' 35 | } 36 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Icon)) 37 | { 38 | return 'image/icon' 39 | } 40 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Tiff)) 41 | { 42 | return 'image/tiff' 43 | } 44 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Wmf)) 45 | { 46 | return 'image/wmf' 47 | } 48 | elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Exif)) 49 | { 50 | return 'image/exif' 51 | } 52 | else 53 | { 54 | return 'image/unknown' 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Examples/Example16.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example16 = Document -Name 'PScribo Example 16' { 11 | 12 | <# 13 | If you do not have a collection/array of objects that you wish to create 14 | a table from, PScribo supports creating a table from single ordered hashtable 15 | or an array of ordered hashtables. 16 | 17 | NOTE: You cannot pipe a single hashtable or an array of hashtables to the 18 | 'Table' cmdlet. You MUST pass the hashtable(s) via the -HashTable parameter. 19 | If you pipe the hashtable(s), the hashtable object's properties are 20 | displayed and not the hashtable key/value pairs! 21 | 22 | Creating a hashtable does permit the utilisation of spaces in the key names, 23 | requiring that the -Headers parameter does not necessarily need to be used. 24 | 25 | NOTE: This is not a standard hashtable, but a System.Collections.Specialized.OrderedDictionary 26 | object. These can be created with the [Ordered] attribute on the hashtable 27 | declaration, e.g. [Ordered] @{ Key1 = Value1; Key2 = Value2; } 28 | 29 | The following example creates a table with a single row with the hashtable keys 30 | used as the header values and the corresponding values as the first table row. 31 | #> 32 | $hashtable = [Ordered] @{ 33 | 'Column 1' = 'Some random text' 34 | 'Column2' = 345 35 | 'Custom Property' = $true 36 | } 37 | Table -Hashtable $hashtable 38 | } 39 | $example16 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 40 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordStyleRunPr.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordStyleRunPr 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates Word run (rPr) formatting properties 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Xml.XmlElement])] 9 | param 10 | ( 11 | [Parameter(Mandatory)] 12 | [System.Management.Automation.PSObject] $Style, 13 | 14 | [Parameter(Mandatory)] 15 | [System.Xml.XmlDocument] $XmlDocument 16 | ) 17 | process 18 | { 19 | $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' 20 | 21 | $rPr = $XmlDocument.CreateElement('w', 'rPr', $xmlns) 22 | 23 | $rFonts = $rPr.AppendChild($XmlDocument.CreateElement('w', 'rFonts', $xmlns)) 24 | [ref] $null = $rFonts.SetAttribute('ascii', $xmlns, $Style.Font[0]) 25 | [ref] $null = $rFonts.SetAttribute('hAnsi', $xmlns, $Style.Font[0]) 26 | 27 | if ($Style.Bold) 28 | { 29 | [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'b', $xmlns)) 30 | } 31 | 32 | if ($Style.Underline) 33 | { 34 | [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'u', $xmlns)) 35 | } 36 | 37 | if ($Style.Italic) 38 | { 39 | [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'i', $xmlns)) 40 | } 41 | 42 | $wordColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $Style.Color) 43 | $color = $rPr.AppendChild($XmlDocument.CreateElement('w', 'color', $xmlns)) 44 | [ref] $null = $color.SetAttribute('val', $xmlns, $wordColor) 45 | 46 | $sz = $rPr.AppendChild($XmlDocument.CreateElement('w', 'sz', $xmlns)) 47 | [ref] $null = $sz.SetAttribute('val', $xmlns, $Style.Size * 2) 48 | 49 | return $rPr 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Out-TextParagraph.ps1: -------------------------------------------------------------------------------- 1 | function Out-TextParagraph 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted paragraph run. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Paragraph 14 | ) 15 | begin 16 | { 17 | ## Fix Set-StrictMode 18 | if (-not (Test-Path -Path Variable:\Options)) 19 | { 20 | $options = New-PScriboTextOption; 21 | } 22 | } 23 | process 24 | { 25 | $convertToAlignedStringParams = @{ 26 | Width = $options.TextWidth 27 | Tabs = $Paragraph.Tabs 28 | Align = 'Left' 29 | } 30 | 31 | if (-not [System.String]::IsNullOrEmpty($Paragraph.Style)) 32 | { 33 | $paragraphStyle = Get-PScriboDocumentStyle -Style $Paragraph.Style 34 | $convertToAlignedStringParams['Align'] = $paragraphStyle.Align 35 | } 36 | 37 | [System.Text.StringBuilder] $paragraphBuilder = New-Object -TypeName 'System.Text.StringBuilder' 38 | foreach ($paragraphRun in $Paragraph.Sections) 39 | { 40 | $text = Resolve-PScriboToken -InputObject $paragraphRun.Text 41 | [ref] $null = $paragraphBuilder.Append($text) 42 | 43 | if (($paragraphRun.IsParagraphRunEnd -eq $false) -and 44 | ($paragraphRun.NoSpace -eq $false)) 45 | { 46 | [ref] $null = $paragraphBuilder.Append(' ') 47 | } 48 | } 49 | 50 | $convertToAlignedStringParams['InputObject'] = $paragraphBuilder.ToString() 51 | return (ConvertTo-AlignedString @convertToAlignedStringParams) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/Plugins/Json/Out-JsonTable.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Json\Out-JsonTable' { 10 | 11 | ## Scaffold document options 12 | $Document = Document -Name 'TestDocument' -ScriptBlock {} 13 | $script:currentPageNumber = 1 14 | $pscriboDocument = $Document 15 | 16 | ## Context (list vs table) doesn't make a difference as they are treated the same 17 | 18 | $services = @( 19 | [Ordered] @{ Name = 'TestService1'; 'Service Name' = 'Test 1'; 'Display Name' = 'Test Service 1'; } 20 | [Ordered] @{ Name = 'TestService3'; 'Service Name' = 'Test 3'; 'Display Name' = 'Test Service 3'; } 21 | [Ordered] @{ Name = 'TestService2'; 'Service Name' = 'Test 2'; 'Display Name' = 'Test Service 2'; } 22 | ) 23 | 24 | It 'Input matches output' { 25 | $expected = 3 26 | 27 | $table = Table -Hashtable $services 'Test Table' | Out-JsonTable 28 | 29 | $table.Count | Should Be $expected 30 | } 31 | 32 | It 'Caption present' { 33 | $expected = $true 34 | 35 | $table = Table -Hashtable $services 'Test Table' -Caption 'Test' | Out-JsonTable 36 | 37 | $table[0].Caption | Should Be $expected 38 | } 39 | 40 | It 'Input matches output with caption present' { 41 | $expected = 3 42 | 43 | $table = Table -Hashtable $services 'Test Table' -Caption 'Test' | Out-JsonTable 44 | 45 | $table[1].Count | Should Be $expected 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlTableStyle.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlTableStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates html stylesheet style attributes from a PScribo document table style. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | ## PScribo document table style 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [System.Management.Automation.PSObject] $TableStyle 14 | ) 15 | process 16 | { 17 | $tableStyleBuilder = New-Object -TypeName 'System.Text.StringBuilder' 18 | [ref] $null = $tableStyleBuilder.AppendFormat((Get-HtmlTablePaddingStyle -TableStyle $TableStyle)) 19 | [ref] $null = $tableStyleBuilder.AppendFormat(' border-style: {0};', $TableStyle.BorderStyle.ToLower()) 20 | 21 | if ($TableStyle.BorderWidth -gt 0) 22 | { 23 | $invariantBorderWidth = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.BorderWidth) 24 | [ref] $null = $tableStyleBuilder.AppendFormat(' border-width: {0}rem;', $invariantBorderWidth) 25 | 26 | $borderColor = Resolve-PScriboStyleColor -Color $TableStyle.BorderColor 27 | [ref] $null = $tableStyleBuilder.AppendFormat(' border-color: #{0};', $borderColor.ToLower()) 28 | } 29 | 30 | [ref] $null = $tableStyleBuilder.Append(' border-collapse: collapse;') 31 | ## is deprecated in Html5 32 | if ($TableStyle.Align -eq 'Center') 33 | { 34 | [ref] $null = $tableStyleBuilder.Append(' margin-left: auto; margin-right: auto;') 35 | } 36 | elseif ($TableStyle.Align -eq 'Right') 37 | { 38 | [ref] $null = $tableStyleBuilder.Append(' margin-left: auto; margin-right: 0;') 39 | } 40 | 41 | return $tableStyleBuilder.ToString() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Examples/Example21.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example21 = Document -Name 'PScribo Example 21' { 11 | 12 | <# 13 | Styling can also be applied to tables - just like Paragraphs and Sections. Tables 14 | support additional styling options such as borders and cell padding options. 15 | 16 | NOTE: There is a built-in "TableDefault" style. You can override the default table 17 | style just like overriding the default "Normal" paragraph style. 18 | 19 | Each table style also requires three styles; one applied to the header row, one 20 | applied to each table row and an optional alternating row style. 21 | 22 | Before table styles can be defined, the individual styles must already be defined 23 | in the document via the 'Style' cmdlet/keyword. 24 | 25 | NOTE: The "TableDefault" style uses a style called "TableDefaultHeading" for the 26 | header row, a style called "TableDefaultRow" for the row style and a style 27 | called "TableDefaultAltRow" for the alternating row style. 28 | 29 | The following defines a very simple table style named "Basic" that uses the "Normal" 30 | style for the header and all other rows, e.g. no styling! 31 | 32 | NOTE: When -AlternateRowStyle is not specified, the -RowStyle property value is 33 | used for -AlternateRowStyle property. 34 | #> 35 | TableStyle -Name 'Basic' -HeaderStyle Normal -RowStyle Normal 36 | Get-Service | Select-Object -Property Name,DisplayName,Status -First 3 | Table -Style Basic 37 | } 38 | $example21 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 39 | -------------------------------------------------------------------------------- /Examples/Example29.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example29 = Document -Name 'PScribo Example 29' { 11 | 12 | <# 13 | Custom styling can also be applied to individual keys of one or more 14 | hashtables. 15 | 16 | To apply a style to a particular hashtable key, just ensure that a 17 | '__Style' key is also defined with the style name you wish to to apply 18 | to the particular table cell. 19 | 20 | For example, to apply a "MyCustomStyle" to a 'Name' key, ensure that a 21 | 'Name__Style' key exists in the hashtable with a value of "MyCustomStyle". 22 | 23 | In the following example, the the middle two rows where key 'Custom Property' is 24 | set to $false will be highlighted with the in the array of hashtables 25 | will be styled with the 'Warning' custom style. 26 | 27 | NOTE: How the 'Custom Property__Style' key is added to a hashtable is up to you! 28 | #> 29 | Style -Name Warning -Color White -BackgroundColor Firebrick 30 | 31 | $hashtableArray = @( 32 | [Ordered] @{ 'Column 1' = 'Some random text'; 'Column2' = 345; 'Custom Property' = $true; } 33 | [Ordered] @{ 'Column 1' = 'Random some text'; 'Column2' = 16; 'Custom Property' = $false; 'Custom Property__Style' = 'Warning'; } 34 | [Ordered] @{ 'Column 1' = 'Random text some'; 'Column2' = 241; 'Custom Property' = $false; 'Custom Property__Style' = 'Warning'; } 35 | [Ordered] @{ 'Column 1' = 'Text random some'; 'Column2' = 1GB; 'Custom Property' = $true; } 36 | ) 37 | Table -Hashtable $hashtableArray 38 | } 39 | $example29 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 40 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboFormattedTableRow.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboFormattedTableRow 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a formatted table row for plugin output/rendering. 6 | #> 7 | [CmdletBinding()] 8 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 9 | [OutputType([System.Management.Automation.PSObject])] 10 | param 11 | ( 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [System.String] $TableStyle, 14 | 15 | [Parameter(ValueFromPipeline)] 16 | [AllowNull()] 17 | [System.String] $Style = $null, 18 | 19 | [Parameter(ValueFromPipeline, ParameterSetName = 'Header')] 20 | [System.Management.Automation.SwitchParameter] $IsHeaderRow, 21 | 22 | [Parameter(ValueFromPipeline, ParameterSetName = 'Row')] 23 | [System.Management.Automation.SwitchParameter] $IsAlternateRow 24 | ) 25 | process 26 | { 27 | if (-not ([System.String]::IsNullOrEmpty($Style))) 28 | { 29 | ## Use the explictit style 30 | $IsStyleInherited = $false 31 | } 32 | elseif ($IsHeaderRow) 33 | { 34 | $Style = $document.TableStyles[$TableStyle].HeaderStyle 35 | $IsStyleInherited = $true 36 | } 37 | elseif ($IsAlternateRow) 38 | { 39 | $Style = $document.TableStyles[$TableStyle].AlternateRowStyle 40 | $IsStyleInherited = $true 41 | } 42 | else 43 | { 44 | $Style = $document.TableStyles[$TableStyle].RowStyle 45 | $IsStyleInherited = $true 46 | } 47 | 48 | return [PSCustomObject] @{ 49 | Style = $Style; 50 | IsStyleInherited = $IsStyleInherited; 51 | Cells = New-Object -TypeName System.Collections.ArrayList; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/Unit/Image.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent 2 | $testRoot = Split-Path -Path $here -Parent 3 | $moduleRoot = Split-Path -Path $testRoot -Parent 4 | Import-Module -Name "$moduleRoot\PScribo.psm1" -Force 5 | 6 | InModuleScope -ModuleName 'PScribo' -ScriptBlock { 7 | 8 | $testRoot = Split-Path -Path $PSScriptRoot -Parent 9 | 10 | Describe -Name 'Image' -Fixture { 11 | 12 | $pscriboDocument = Document -Name 'ScaffoldDocument' -ScriptBlock { } 13 | $testJpgFile = Join-Path -Path $testRoot -ChildPath 'TestImage.jpg' 14 | $testPngFile = Join-Path -Path $testRoot -ChildPath 'TestImage.png' 15 | 16 | It -name 'returns "PSCustomObject" object.' -test { 17 | $p = Image -Path $testJpgFile 18 | $p.GetType().Name | Should -eq 'PSCustomObject' 19 | } 20 | 21 | It -name 'creates "PScribo.Image" type.' -test { 22 | $p = Image -Path $testJpgFile 23 | $p.Type | Should -eq 'PScribo.Image' 24 | } 25 | 26 | It -name 'set MIME type "jpeg"' -test { 27 | $p = Image -Path $testJpgFile 28 | $p.MIMEType | Should -eq 'image/jpeg' 29 | } 30 | 31 | It -name 'set MIME type "png"' -test { 32 | $p = Image -Path $testPngFile 33 | $p.MIMEType | Should -eq 'image/png' 34 | } 35 | 36 | It -name 'creates image number' { 37 | $p = Image -Path $testJpgFile 38 | $p.Name | Should -match '^Image\d+$' 39 | } 40 | 41 | It -name 'sets Emu width' -test { 42 | $p = Image -Path $testJpgFile -Height 61 -Width 250 43 | $p.WidthEm | Should -eq 2381250 44 | } 45 | 46 | It -name 'sets Emu Height' -test { 47 | $p = Image -Path $testJpgFile -Height 61 -Width 250 48 | $p.HeightEm | Should -eq 581025 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordNumberingDocument.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordNumberingDocument 2 | { 3 | <# 4 | .SYNOPSIS 5 | Outputs Office Open XML numbering document 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.Xml.XmlDocument])] 9 | param 10 | ( 11 | ## PScribo document styles 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [AllowEmptyCollection()] 14 | [System.Collections.ArrayList] $Lists 15 | ) 16 | process 17 | { 18 | ## Create the numbering.xml document 19 | $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' 20 | $xmlDocument = New-Object -TypeName 'System.Xml.XmlDocument' 21 | [ref] $null = $xmlDocument.AppendChild($XmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) 22 | $numbering = $xmlDocument.AppendChild($xmlDocument.CreateElement('w', 'numbering', $xmlns)) 23 | [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006') 24 | [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:w14', 'http://schemas.microsoft.com/office/word/2010/wordml') 25 | 26 | foreach ($list in $Lists.GetEnumerator()) 27 | { 28 | $abstractNum = Get-WordNumberStyle -List $list -XmlDocument $xmlDocument 29 | [ref] $null = $numbering.AppendChild($abstractNum) 30 | } 31 | 32 | foreach ($list in $Lists.GetEnumerator()) 33 | { 34 | $num = $numbering.AppendChild($xmlDocument.CreateElement('w', 'num', $xmlns)) 35 | [ref] $null = $num.SetAttribute('numId', $xmlns, $list.Number) 36 | $abstractNumId = $num.AppendChild($xmlDocument.CreateElement('w', 'abstractNumId', $xmlns)) 37 | [ref] $null = $abstractNumId.SetAttribute('val', $xmlns, $list.Number -1) 38 | } 39 | 40 | return $xmlDocument 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Examples/Example36.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example36 = Document -Name 'PScribo Example 36' { 11 | 12 | <# 13 | Headers and footers are displayed inside the page margin. To increase 14 | the available space, the top and bottom margins can be adjusted to 15 | compensate. 16 | #> 17 | DocumentOption -MarginTopAndBottom 36 -MarginLeftAndRight 54 18 | 19 | <# 20 | There are no default styles defined for headers and footers, but they 21 | can be styled in the standard way we style paragraphs and tables. 22 | #> 23 | Style -Name 'Header' -Size 12 -Color 0072af -Align Center -Bold 24 | Header -Default -IncludeOnFirstPage { 25 | Paragraph -Style 'Header' 'PScribo Example 36' 26 | } 27 | 28 | <# 29 | It is also possible to define a custom style and assign it to a 30 | header and/or footer. 31 | #> 32 | Style -Name 'CustomFooter' -Size 11 -Color 0072af -Align Center -Italic 33 | Footer -Default { 34 | Paragraph -Style 'CustomFooter' 'Page of ' 35 | } 36 | 37 | Get-Service | 38 | Select-Object -First 25 -Property 'Name','DisplayName','Status' | 39 | Table -ColumnWidths 42,42,16 40 | 41 | PageBreak 42 | 43 | Get-Service | 44 | Select-Object -First 25 -Skip 25 -Property 'Name','DisplayName','Status' | 45 | Table -ColumnWidths 42,42,16 46 | 47 | PageBreak 48 | 49 | Get-Service | 50 | Select-Object -First 25 -Skip 50 -Property 'Name','DisplayName','Status' | 51 | Table -ColumnWidths 42,42,16 52 | 53 | } 54 | $example36 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 55 | -------------------------------------------------------------------------------- /Tests/Unit/TOC.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $testRoot = Split-Path -Path $here -Parent; 3 | $moduleRoot = Split-Path -Path $testRoot -Parent; 4 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 5 | 6 | InModuleScope 'PScribo' { 7 | 8 | Describe 'TOC' { 9 | $pscriboDocument = Document 'ScaffoldDocument' {}; 10 | 11 | Context 'By Named Parameter' { 12 | 13 | It 'returns a PSCustomObject object' { 14 | $t = TOC -Name Test 15 | 16 | $t.GetType().Name | Should Be 'PSCustomObject' 17 | $t.Type | Should Be 'PScribo.TOC' 18 | } 19 | 20 | It 'creates a TOC by named -Name parameter' { 21 | $text = 'Simple paragraph.' 22 | 23 | $t = TOC -Name $text 24 | 25 | $t.Name | Should Be $text 26 | } 27 | 28 | It 'creates a TOC by named -Name parameter with enabled -ForceUppercaseSection option' { 29 | $pscriboDocument.Options['ForceUppercaseSection'] = $true 30 | $text = 'Simple paragraph.' 31 | 32 | $t = TOC -Name $text 33 | 34 | $t.Name | Should Be $text.ToUpper() 35 | } 36 | } 37 | 38 | Context 'By Positional Parameter' { 39 | 40 | It 'creates a TOC by named -Name parameter' { 41 | $text = 'Simple paragraph.' 42 | 43 | $t = TOC $text 44 | 45 | $t.Name | Should Be $text 46 | } 47 | 48 | It 'creates a TOC by named -Name parameter with enabled -ForceUppercaseSection option' { 49 | $pscriboDocument.Options['ForceUppercaseSection'] = $true 50 | $text = 'Simple paragraph.' 51 | 52 | $t = TOC $text 53 | 54 | $t.Name | Should Be $text.ToUpper() 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Examples/Example06.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example6 = Document -Name 'PScribo Example 6' { 11 | 12 | <# 13 | You can create a document structure with the 'Section' cmdlet. This is the only PScribo 14 | cmdlet that supports nesting of script blocks. 15 | 16 | Sections are used to create a hierarchy within the document that are used when generating 17 | a table of contents. If you have no requirement for a table of contents, then you may 18 | not need to use the 'Section' keyword. 19 | 20 | You can define sections with the 'Section' keyword, a name and script block. You can nest 21 | sections within sections. 22 | #> 23 | Section -Name 'First Section' -ScriptBlock { 24 | Paragraph 'This section will not have any explicit styling applied.' 25 | } 26 | 27 | <# 28 | Styles can also be applied to Sections with the -Style parameter. PScribo defines the 29 | following heading styles: Heading1, Heading2 and Heading3. Just like the built-in 30 | "Normal" style you can define your own styles or override the default ones. 31 | #> 32 | Section -Name 'Second Styled Section' -Style Heading1 -ScriptBlock { 33 | Paragraph 'This section heading will be styled with the built-in "Heading1" style.' 34 | } 35 | 36 | Section -Name 'Third Section' -Style Heading1 -ScriptBlock { 37 | Paragraph 'This paragraph is nested within "Third Section".' 38 | 39 | Section 'Sub Section' -Style Heading2 { 40 | Paragraph 'This paragraph is nested beneath the "Third Section\Sub Section".' 41 | } 42 | } 43 | } 44 | $example6 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 45 | -------------------------------------------------------------------------------- /Examples/Example41.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example41 = Document -Name 'PScribo Example 41' { 11 | 12 | <# 13 | Multi-level bullet lists can be created by nesting one or more 'List' keywords. 14 | 15 | NOTE: There must be an 'Item' keyword defined before a nested 'List' can be used. 16 | #> 17 | List { 18 | Item 'Apples' 19 | List { 20 | Item 'Jazz' 21 | Item 'Granny smith' 22 | Item 'Pink lady' 23 | } 24 | Item 'Bananas' 25 | Item 'Oranges' 26 | List { 27 | Item 'Jaffa' 28 | Item 'Tangerine' 29 | Item 'Clementine' 30 | } 31 | } 32 | 33 | <# 34 | Each 'List' can have its own bullet style defined. 35 | 36 | NOTE: Word does not support a mixture of bullet/number formats at the same level within a 37 | list. Therefore, only the first list type will be rendered at each level - in this 38 | example the 'Disc' style will be used. 39 | 40 | NOTE: Html output does not support the 'Dash' bullet style. Dashes will be rendered using 41 | the the web broswer's defaults. 42 | #> 43 | List -BulletStyle Square { 44 | Item 'Apples' 45 | List -BulletStyle Disc { 46 | Item 'Jazz' 47 | Item 'Granny smith' 48 | Item 'Pink lady' 49 | } 50 | Item 'Bananas' 51 | Item 'Oranges' 52 | List -BulletStyle Dash { 53 | Item 'Jaffa' 54 | Item 'Tangerine' 55 | Item 'Clementine' 56 | } 57 | } 58 | 59 | } 60 | $example41 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 61 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlTableDiv.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlTableDiv 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates Html
tags based upon table width, columns and indentation 6 | .NOTES 7 | A
is required to ensure that the table stays within the "page" boundaries/margins. 8 | #> 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Table 14 | ) 15 | process 16 | { 17 | $divBuilder = New-Object -TypeName 'System.Text.StringBuilder' 18 | [ref] $null = $divBuilder.Append('
tag 26 | [ref] $null = $divBuilder.AppendFormat('">
', [System.String]::Join(' ', $styleElements)) 43 | } 44 | else 45 | { 46 | [ref] $null = $divBuilder.Append('>') 47 | } 48 | 49 | return $divBuilder.ToString() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Src/Public/Set-Style.ps1: -------------------------------------------------------------------------------- 1 | function Set-Style 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sets the style for an individual table row or cell. 6 | #> 7 | [CmdletBinding()] 8 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 9 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidMultipleTypeAttributes','')] 10 | [OutputType([System.Object])] 11 | param 12 | ( 13 | ## PSCustomObject to apply the style to 14 | [Parameter(Mandatory, ValueFromPipeline)] 15 | [System.Object[]] [Ref] $InputObject, 16 | 17 | ## PScribo style Id to apply 18 | [Parameter(Mandatory, ValueFromPipelineByPropertyName)] 19 | [System.String] $Style, 20 | 21 | ## Property name(s) to apply the selected style to. Leave blank to apply the style to the entire row. 22 | [Parameter(ValueFromPipelineByPropertyName)] 23 | [ValidateNotNullOrEmpty()] 24 | [System.String[]] $Property = '', 25 | 26 | ## Passes the modified object back to the pipeline 27 | [Parameter(ValueFromPipelineByPropertyName)] 28 | [System.Management.Automation.SwitchParameter] $PassThru 29 | ) 30 | begin 31 | { 32 | if (-not (Test-PScriboStyle -Name $Style)) 33 | { 34 | Write-Error ($localized.UndefinedStyleError -f $Style) 35 | return 36 | } 37 | } 38 | process 39 | { 40 | foreach ($object in $InputObject) 41 | { 42 | foreach ($p in $Property) 43 | { 44 | ## If $Property not set, __Style will apply to the whole row. 45 | $propertyName = '{0}__Style' -f $p 46 | $object | Add-Member -MemberType NoteProperty -Name $propertyName -Value $Style -Force 47 | } 48 | } 49 | if ($PassThru) 50 | { 51 | return $object 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlListItemInlineStyle.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlListItemInlineStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates inline Html style attribute from PScribo list Item style overrides. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [ValidateNotNull()] 13 | [System.Management.Automation.PSObject] $Item 14 | ) 15 | process 16 | { 17 | $itemStyleBuilder = New-Object -TypeName System.Text.StringBuilder 18 | 19 | if ($Item.Font) 20 | { 21 | $fontList = $List.Font -Join "','" 22 | [ref] $null = $itemStyleBuilder.AppendFormat(" font-family: '{0}';", $fontList) 23 | } 24 | 25 | if ($Item.Size -gt 0) 26 | { 27 | ## Create culture invariant decimal https://github.com/iainbrighton/PScribo/issues/6 28 | $invariantItemSize = ConvertTo-InvariantCultureString -Object ($Item.Size / 12) -Format 'f2' 29 | [ref] $null = $itemStyleBuilder.AppendFormat(' font-size: {0}rem;', $invariantItemSize) 30 | } 31 | 32 | if ($Item.Bold -eq $true) 33 | { 34 | [ref] $null = $itemStyleBuilder.Append(' font-weight: bold;') 35 | } 36 | 37 | if ($Item.Italic -eq $true) 38 | { 39 | [ref] $null = $itemStyleBuilder.Append(' font-style: italic;') 40 | } 41 | 42 | if ($Item.Underline -eq $true) 43 | { 44 | [ref] $null = $itemStyleBuilder.Append(' text-decoration: underline;') 45 | } 46 | 47 | if (-not [System.String]::IsNullOrEmpty($Item.Color)) 48 | { 49 | $color = Resolve-PScriboStyleColor -Color $Item.Color 50 | [ref] $null = $itemStyleBuilder.AppendFormat(' color: #{0};', $color) 51 | } 52 | 53 | return $itemStyleBuilder.ToString().TrimStart() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Src/Plugins/Word/Get-WordTableRenderWidthMm.ps1: -------------------------------------------------------------------------------- 1 | function Get-WordTableRenderWidthMm 2 | { 3 | <# 4 | .SYNOPSIS 5 | Calcualtes a table's (maximum) rendering size in millimetres. 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 11 | [ValidateRange(0, 100)] 12 | [System.UInt16] $TableWidth, 13 | 14 | [Parameter(Mandatory, ValueFromPipelineByPropertyName)] 15 | [ValidateRange(0, 10)] 16 | [System.UInt16] $Tabs, 17 | 18 | [Parameter(Mandatory, ValueFromPipeline)] 19 | [System.String] $Orientation 20 | ) 21 | process 22 | { 23 | if ($TableWidth -eq 0) 24 | { 25 | ## Word will autofit contents as necessary, but LibreOffice won't 26 | ## so we just have assume that we're using all available width 27 | $TableWidth = 100 28 | } 29 | if ($Orientation -eq 'Portrait') 30 | { 31 | $pageWidthMm = $Document.Options['PageWidth'] - ($Document.Options['MarginLeft'] + $Document.Options['MarginRight']) 32 | } 33 | elseif ($Orientation -eq 'Landscape') 34 | { 35 | $pageWidthMm = $Document.Options['PageHeight'] - ($Document.Options['MarginLeft'] + $Document.Options['MarginRight']) 36 | } 37 | $indentWidthMm = ConvertTo-Mm -Point ($Tabs * 36) 38 | $tableWidthRenderMm = (($pageWidthMm / 100) * $TableWidth) + $indentWidthMm 39 | if ($tableWidthRenderMm -gt $pageWidthMm) 40 | { 41 | ## We now need to deal with tables being pushed outside the page margin so just return the maximum permitted 42 | $tableWidthRenderMm = $pageWidthMm - $indentWidthMm 43 | Write-PScriboMessage -Message ($localized.TableWidthOverflowWarning -f $tableWidthRenderMm) -IsWarning 44 | } 45 | return $tableWidthRenderMm 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/Plugins/Xml/Out-XmlDocument.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | ## Scaffold root XmlDocument 10 | $xmlDocument = New-Object -TypeName System.Xml.XmlDocument; 11 | [ref] $null = $xmlDocument.AppendChild($xmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')); 12 | $element = $xmlDocument.AppendChild($xmlDocument.CreateElement('testdocument')); 13 | [ref] $null = $element.SetAttribute('name', 'testdocument'); 14 | 15 | Describe 'Plugins\Text\Out-XmlDocument' { 16 | 17 | It 'calls Out-XmlTable' { 18 | Mock -CommandName Out-XmlTable -MockWith { return $xmlDocument.CreateElement('TestTable') } 19 | 20 | Document -Name 'TestDocument' -ScriptBlock { Get-Process | Select-Object -First 1 | Table 'TestTable' } | Out-XmlDocument -Path $testDrive 21 | 22 | Assert-MockCalled -CommandName Out-XmlTable -Exactly 1 23 | } 24 | 25 | It 'calls Out-XmlParagraph' { 26 | Mock -CommandName Out-XmlParagraph -MockWith { return $xmlDocument.CreateElement('TestParagraph') } 27 | 28 | Document -Name 'TestDocument' -ScriptBlock { Paragraph 'TestParagraph' } | Out-XmlDocument -Path $testDrive 29 | 30 | Assert-MockCalled -CommandName Out-XmlParagraph -Exactly 1 31 | } 32 | 33 | It 'calls Out-XmlSection' { 34 | Mock -CommandName Out-XmlSection -MockWith { return $xmlDocument.CreateElement('TestSection'); } 35 | 36 | Document -Name 'TestDocument' -ScriptBlock { Section -Name 'TestSection' -ScriptBlock { } } | Out-XmlDocument -Path $testDrive 37 | 38 | Assert-MockCalled -CommandName Out-XmlSection -Exactly 1 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PScribo.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | RootModule = 'PScribo.psm1' 3 | ModuleVersion = '0.11.1' 4 | GUID = '058eab05-b7bc-4f8b-a2d1-737cc664b12b' 5 | Author = 'Iain Brighton' 6 | CompanyName = 'Virtual Engine' 7 | Copyright = '(c) 2025 Iain Brighton. All rights reserved.' 8 | Description = 'PScribo documentation Powershell module/framework.' 9 | PowerShellVersion = '3.0' 10 | FunctionsToExport = @( 11 | 'BlankLine', 12 | 'Document', 13 | 'DocumentOption', 14 | 'Export-Document', 15 | 'Footer', 16 | 'Header', 17 | 'Image', 18 | 'Item', 19 | 'LineBreak', 20 | 'List', 21 | 'NumberStyle', 22 | 'PageBreak', 23 | 'Paragraph', 24 | 'Section', 25 | 'Set-Style', 26 | 'Style', 27 | 'Table', 28 | 'TableStyle', 29 | 'Text', 30 | 'TOC', 31 | 'Write-PScriboMessage', 32 | 'Get-WordListLevel' 33 | ) 34 | AliasesToExport = @( 35 | 'GlobalOption' 36 | ) 37 | PrivateData = @{ 38 | PSData = @{ 39 | Tags = @('Powershell','PScribo','Documentation','Framework','VirtualEngine','Windows','Linux','MacOS','PSEdition_Desktop','PSEdition_Core','Word','Html') 40 | LicenseUri = 'https://raw.githubusercontent.com/iainbrighton/PScribo/master/LICENSE' 41 | ProjectUri = 'http://github.com/iainbrighton/PScribo' 42 | # IconUri = ''; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Src/Private/ConvertFrom-NumberStyle.ps1: -------------------------------------------------------------------------------- 1 | function ConvertFrom-NumberStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Converts a number to its string representation, based upon the number style 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline)] 11 | [System.Int32] $Value, 12 | 13 | [Parameter(Mandatory, ValueFromPipeline)] 14 | [System.Management.Automation.PSObject] $NumberStyle 15 | ) 16 | process 17 | { 18 | switch ($NumberStyle.Format) 19 | { 20 | 'Number' 21 | { 22 | $numberString = $Value.ToString() 23 | } 24 | 'Letter' 25 | { 26 | $numberString = [System.Char] ($Value + 64) 27 | } 28 | 'Roman' 29 | { 30 | $numberString = ConvertTo-RomanNumeral -Value $Value 31 | } 32 | 'Custom' 33 | { 34 | $numberCount = 0 35 | $customStringChars = $numberStyle.Custom.ToCharArray() 36 | $customStringLength = $numberStyle.Custom.Length 37 | for ($n = $customStringLength - 1; $n -ge 0; $n--) 38 | { 39 | if ($customStringChars[$n] -eq '%') 40 | { 41 | $numberCount++ 42 | } 43 | } 44 | 45 | $searchString = ''.PadRight($numberCount, '%') 46 | $replacementString = $Value.ToString().PadLeft($numberCount, '0') 47 | return $numberStyle.Custom.Replace($searchString, $replacementString) 48 | } 49 | } 50 | 51 | $numberString = '{0}{1}' -f $numberString, $NumberStyle.Suffix 52 | if ($NumberStyle.Uppercase) 53 | { 54 | return $numberString.ToUpper() 55 | } 56 | else 57 | { 58 | return $numberString.ToLower() 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Examples/Example42.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example42 = Document -Name 'PScribo Example 42' { 11 | 12 | <# 13 | Multi-level numbered lists can be created by nesting one or more 'List' keywords in 14 | combination with the '-Numbered' parameter. 15 | 16 | NOTE: A 'List' defaults to a bulleted list by default, so the '-Numbered' switch needs 17 | to be specified at each level - where required. 18 | #> 19 | List -Numbered { 20 | Item 'Apples' 21 | List -Numbered { 22 | Item 'Jazz' 23 | Item 'Granny smith' 24 | Item 'Pink lady' 25 | } 26 | Item 'Bananas' 27 | Item 'Oranges' 28 | List -Numbered { 29 | Item 'Jaffa' 30 | Item 'Tangerine' 31 | Item 'Clementine' 32 | } 33 | } 34 | 35 | <# 36 | Like bullet lists, each 'List' can have its own number style defined. 37 | 38 | NOTE: Word does not support a mixture of bullet/number formats at the same level within a 39 | list. Therefore, only the first list type will be rendered at each level - in this 40 | example the 'Letter' style will be used for the second nested numbered list. 41 | #> 42 | List -Numbered { 43 | Item 'Apples' 44 | List -Numbered -NumberStyle Letter { 45 | Item 'Jazz' 46 | Item 'Granny smith' 47 | Item 'Pink lady' 48 | } 49 | Item 'Bananas' 50 | Item 'Oranges' 51 | List -Numbered -NumberStyle Roman { 52 | Item 'Jaffa' 53 | Item 'Tangerine' 54 | Item 'Clementine' 55 | } 56 | } 57 | 58 | } 59 | $example42 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 60 | -------------------------------------------------------------------------------- /Examples/Example04.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example4 = Document -Name 'PScribo Example 4' { 11 | 12 | <# 13 | NOTE: This example is the 'legacy' paragraph implementation. For 14 | advanced paragraph styling options see Example38.ps1 15 | #> 16 | Paragraph 'The default built-in style that is applied to paragraphs is named "Normal".' 17 | Paragraph 'You can apply an alternative color to a particular paragraph by specifying a html color code to the -Color parameter.' -Color f00 18 | Paragraph 'You can also supply Word constant colors to a paragraph!' -Color SteelBlue 19 | Paragraph 'You can apply bold styling to a particular paragraph by specifying the -Bold switch parameter.' -Bold 20 | Paragraph 'You can apply italic styling to a particular paragraph by specifying the -Italic switch parameter.' -Italic 21 | Paragraph 'You can apply underline styling to a particular paragraph by specifying the -Underline switch parameter.' -Underline 22 | Paragraph 'You can alter the default font size of a particular paragraph by specifying the font point size with the -Size parameter' -Size 16 23 | Paragraph 'You can alter the default font typeface of a particular paragraph by specifying -Font parameter. This parameter takes an array of font names. Typically, only the first font name is used in Word output, but all names are used in Html output.' -Font Arial 24 | Paragraph 'If you wish to indent a paragraph, specify the -Tabs parameter. This is an integer number that indents a paragraph at 12.7mm/0.5inch intervals.' -Tabs 1 25 | Paragraph 'Of course, you are free to combine multiple styling parameters to a single paragraph :).' -Color DarkOrchid -Size 14 -Bold 26 | } 27 | $example4 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 28 | -------------------------------------------------------------------------------- /Examples/Example39.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | # 'List' single-level bullet lists 11 | $example39 = Document -Name 'PScribo Example 39' { 12 | 13 | <# 14 | A bulleted list is defined by the 'List' keyword and can contain one or more items. 15 | 16 | A simple single-level list can be defined with the '-Item' parameter and passing an 17 | array of strings ([string[]]). 18 | #> 19 | List -Item 'Apples','Oranges','Bananas' 20 | 21 | <# 22 | A list can also be created using a script block and nesting one or 'Item' keywords 23 | within it. 24 | #> 25 | List { 26 | Item 'Apples' 27 | Item 'Bananas' 28 | Item 'Oranges' 29 | } 30 | 31 | <# 32 | Bullet styles can be applied to a list, e.g. 'Dash', 'Circle', 'Disc' and 'Square'. If 33 | not specified, the bullet list defaults to the 'Disc' style. 34 | #> 35 | List -BulletStyle Square { 36 | Item 'Apples' 37 | Item 'Bananas' 38 | Item 'Oranges' 39 | } 40 | 41 | <# 42 | Formatting styles can be applied to all items in a list. 43 | #> 44 | List -Style Caption { 45 | Item 'Apples' 46 | Item 'Bananas' 47 | Item 'Oranges' 48 | } 49 | 50 | <# 51 | Styles can be applied to indiviual items in a list. 52 | #> 53 | List { 54 | Item 'Apples' 55 | Item 'Bananas' -Style Caption 56 | Item 'Oranges' 57 | } 58 | 59 | <# 60 | Inline styles can also be applied to indiviual items in a list. 61 | #> 62 | List { 63 | Item 'Apples' -Bold 64 | Item 'Bananas' -Italic 65 | Item 'Oranges' -Color Firebrick 66 | } 67 | 68 | } 69 | $example39 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 70 | -------------------------------------------------------------------------------- /Src/Plugins/Text/Out-TextTOC.ps1: -------------------------------------------------------------------------------- 1 | function Out-TextTOC 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Table of Contents 6 | #> 7 | [CmdletBinding()] 8 | param 9 | ( 10 | [Parameter(Mandatory, ValueFromPipeline)] 11 | [System.Management.Automation.PSObject] $TOC 12 | ) 13 | begin 14 | { 15 | ## Fix Set-StrictMode 16 | if (-not (Test-Path -Path Variable:\Options)) 17 | { 18 | $options = New-PScriboTextOption 19 | } 20 | } 21 | process 22 | { 23 | $tocBuilder = New-Object -TypeName System.Text.StringBuilder 24 | [ref] $null = $tocBuilder.AppendLine($TOC.Name) 25 | [ref] $null = $tocBuilder.AppendLine(''.PadRight($options.SeparatorWidth, $options.SectionSeparator)) 26 | 27 | if ($Options.ContainsKey('EnableSectionNumbering')) 28 | { 29 | $maxSectionNumberLength = $Document.TOC.Number | ForEach-Object { $_.Length } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum 30 | foreach ($tocEntry in $Document.TOC) 31 | { 32 | $sectionNumberPaddingLength = $maxSectionNumberLength - $tocEntry.Number.Length 33 | $sectionNumberIndent = ''.PadRight($tocEntry.Level, ' ') 34 | $sectionPadding = ''.PadRight($sectionNumberPaddingLength, ' ') 35 | [ref] $null = $tocBuilder.AppendFormat('{0}{1} {2}{3}', $tocEntry.Number, $sectionPadding, $sectionNumberIndent, $tocEntry.Name).AppendLine() 36 | } 37 | } 38 | else 39 | { 40 | $maxSectionNumberLength = $Document.TOC.Level | Sort-Object | Select-Object -Last 1 41 | foreach ($tocEntry in $Document.TOC) 42 | { 43 | $sectionNumberIndent = ''.PadRight($tocEntry.Level, ' ') 44 | [ref] $null = $tocBuilder.AppendFormat('{0}{1}', $sectionNumberIndent, $tocEntry.Name).AppendLine() 45 | } 46 | } 47 | 48 | return $tocBuilder.ToString() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Src/Plugins/Text/New-PScriboTextOption.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboTextOption 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sets the text plugin specific formatting/output options. 6 | 7 | .NOTES 8 | All plugin options should be prefixed with the plugin name. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [OutputType([System.Collections.Hashtable])] 13 | param 14 | ( 15 | ## Text/output width. 0 = none/no wrap. 16 | [Parameter(ValueFromPipelineByPropertyName)] 17 | [ValidateNotNull()] 18 | [System.Int32] $TextWidth = 120, 19 | 20 | ## Document header separator character. 21 | [Parameter(ValueFromPipelineByPropertyName)] 22 | [ValidateLength(1,1)] 23 | [System.String] $HeaderSeparator = '=', 24 | 25 | ## Document section separator character. 26 | [Parameter(ValueFromPipelineByPropertyName)] 27 | [ValidateLength(1,1)] 28 | [System.String] $SectionSeparator = '-', 29 | 30 | ## Document section separator character. 31 | [Parameter(ValueFromPipelineByPropertyName)] 32 | [ValidateLength(1,1)] 33 | [System.String] $LineBreakSeparator = '_', 34 | 35 | ## Default header/section separator width. 36 | [Parameter(ValueFromPipelineByPropertyName)] 37 | [ValidateNotNull()] 38 | [System.Int32] $SeparatorWidth = $TextWidth, 39 | 40 | ## Text encoding 41 | [Parameter(ValueFromPipelineByPropertyName)] 42 | [ValidateSet('ASCII','Unicode','UTF7','UTF8')] 43 | [System.String] $Encoding = 'ASCII' 44 | ) 45 | process 46 | { 47 | return @{ 48 | TextWidth = $TextWidth 49 | HeaderSeparator = $HeaderSeparator 50 | SectionSeparator = $SectionSeparator 51 | LineBreakSeparator = $LineBreakSeparator 52 | SeparatorWidth = $SeparatorWidth 53 | Encoding = $Encoding 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Out-HtmlPageBreak.ps1: -------------------------------------------------------------------------------- 1 | function Out-HtmlPageBreak 2 | { 3 | <# 4 | .SYNOPSIS 5 | Output formatted Html page break. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | [Parameter(Mandatory, ValueFromPipeline)] 12 | [System.String] $Orientation 13 | ) 14 | process 15 | { 16 | [System.Text.StringBuilder] $pageBreakBuilder = New-Object 'System.Text.StringBuilder' 17 | 18 | [ref] $null = $pageBreakBuilder.Append('') ## Canvas 19 | $isFirstPage = $currentPageNumber -eq 1 20 | [ref] $null = $pageBreakBuilder.Append((Out-HtmlHeaderFooter -Footer -FirstPage:$isFirstPage)) 21 | 22 | $script:currentPageNumber++ 23 | [ref] $null = $pageBreakBuilder.Append('') 24 | $topMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginTop']) -Format 'f2' 25 | $leftMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginLeft']) -Format 'f2' 26 | $bottomMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginBottom']) -Format 'f2' 27 | $rightMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginRight']) -Format 'f2' 28 | [ref] $null = $pageBreakBuilder.AppendFormat('
', $Orientation.ToLower()) 29 | [ref] $null = $pageBreakBuilder.AppendFormat('
', $Document.DefaultStyle, $topMargin, $leftMargin, $bottomMargin, $rightMargin).AppendLine() 30 | 31 | $canvasHeight = Get-HtmlCanvasHeight -Orientation $Orientation 32 | [ref] $null = $pageBreakBuilder.AppendFormat('
', $canvasHeight) 33 | [ref] $null = $pageBreakBuilder.Append((Out-HtmlHeaderFooter -Header)) 34 | 35 | return $pageBreakBuilder.ToString() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/Plugins/Word/Out-WordBlankLine.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginsRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginsRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | 8 | 9 | InModuleScope 'PScribo' { 10 | 11 | function GetMatch 12 | { 13 | [CmdletBinding()] 14 | param 15 | ( 16 | [System.String] $String, 17 | [System.Management.Automation.SwitchParameter] $Complete 18 | ) 19 | Write-Verbose "Pre Match : '$String'" 20 | $matchString = $String.Replace('/','\/') 21 | if (-not $String.StartsWith('^')) 22 | { 23 | $matchString = $matchString.Replace('[..]','[\s\S]+') 24 | $matchString = $matchString.Replace('[??]','([\s\S]+)?') 25 | if ($Complete) 26 | { 27 | $matchString = '^{0}<\/w:test>$' -f $matchString 28 | } 29 | } 30 | Write-Verbose "Post Match: '$matchString'" 31 | return $matchString 32 | } 33 | 34 | Describe 'Plugins\Word\Out-WordBlankLine' { 35 | 36 | It 'appends paragraph ""' { 37 | $document = Document -Name 'TestDocument' { 38 | BlankLine 39 | } 40 | 41 | $testDocument = Get-WordDocument -Document $document 42 | 43 | $expected = GetMatch '' 44 | $testDocument.DocumentElement.OuterXml | Should Match $expected 45 | } 46 | 47 | It 'appends paragraph "" per blankline' { 48 | $document = Document -Name 'TestDocument' { 49 | BlankLine -Count 2 50 | } 51 | 52 | $testDocument = Get-WordDocument -Document $document 53 | 54 | $expected = GetMatch '' 55 | $testDocument.DocumentElement.OuterXml | Should Match $expected 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Src/Plugins/Html/Get-HtmlStyle.ps1: -------------------------------------------------------------------------------- 1 | function Get-HtmlStyle 2 | { 3 | <# 4 | .SYNOPSIS 5 | Generates html stylesheet style attributes from a PScribo document style. 6 | #> 7 | [CmdletBinding()] 8 | [OutputType([System.String])] 9 | param 10 | ( 11 | ## PScribo document style 12 | [Parameter(Mandatory, ValueFromPipeline)] 13 | [System.Management.Automation.PSObject] $Style 14 | ) 15 | process 16 | { 17 | $styleBuilder = New-Object -TypeName System.Text.StringBuilder 18 | [ref] $null = $styleBuilder.AppendFormat(" font-family: '{0}';", $Style.Font -join "','") 19 | ## Create culture invariant decimal https://github.com/iainbrighton/PScribo/issues/6 20 | $invariantFontSize = ConvertTo-InvariantCultureString -Object ($Style.Size / 12) -Format 'f2' 21 | [ref] $null = $styleBuilder.AppendFormat(' font-size: {0}rem;', $invariantFontSize) 22 | [ref] $null = $styleBuilder.AppendFormat(' text-align: {0};', $Style.Align.ToLower()) 23 | 24 | if ($Style.Bold) 25 | { 26 | [ref] $null = $styleBuilder.Append(' font-weight: bold;') 27 | } 28 | else 29 | { 30 | [ref] $null = $styleBuilder.Append(' font-weight: normal;') 31 | } 32 | 33 | if ($Style.Italic) 34 | { 35 | [ref] $null = $styleBuilder.Append(' font-style: italic;') 36 | } 37 | 38 | if ($Style.Underline) 39 | { 40 | [ref] $null = $styleBuilder.Append(' text-decoration: underline;') 41 | } 42 | 43 | if ($Style.Color) 44 | { 45 | $color = Resolve-PScriboStyleColor -Color $Style.Color 46 | [ref] $null = $styleBuilder.AppendFormat(' color: #{0};', $color) 47 | } 48 | 49 | if ($Style.BackgroundColor) 50 | { 51 | $backgroundColor = Resolve-PScriboStyleColor -Color $Style.BackgroundColor 52 | [ref] $null = $styleBuilder.AppendFormat(' background-color: #{0};', $backgroundColor) 53 | } 54 | 55 | return $styleBuilder.ToString() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Src/Private/New-PScriboParagraph.ps1: -------------------------------------------------------------------------------- 1 | function New-PScriboParagraph 2 | { 3 | <# 4 | .SYNOPSIS 5 | Initializes a new PScribo paragraph object. 6 | 7 | .NOTES 8 | This is an internal function and should not be called directly. 9 | #> 10 | [CmdletBinding()] 11 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] 12 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','ScriptBlock')] 13 | [OutputType([System.Management.Automation.PSCustomObject])] 14 | param 15 | ( 16 | ## PScribo paragraph run script block. 17 | [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] 18 | [ValidateNotNull()] 19 | [System.Management.Automation.ScriptBlock] $ScriptBlock, 20 | 21 | ## Paragraph Id. 22 | [Parameter(ValueFromPipelineByPropertyName)] 23 | [ValidateNotNull()] 24 | [System.String] $Id = [System.Guid]::NewGuid().ToString(), 25 | 26 | ## Paragraph style Name/Id reference. 27 | [Parameter(ValueFromPipelineByPropertyName)] 28 | [AllowNull()] 29 | [System.String] $Style = $null, 30 | 31 | ## Tab indent 32 | [Parameter(ValueFromPipelineByPropertyName)] 33 | [ValidateRange(0,10)] 34 | [System.Int32] $Tabs = 0, 35 | 36 | [Parameter(ValueFromPipelineByPropertyName)] 37 | [System.Management.Automation.SwitchParameter] $NoIncrementCounter 38 | ) 39 | process 40 | { 41 | if (-not $NoIncrementCounter) 42 | { 43 | $pscriboDocument.Properties['Paragraphs']++ 44 | } 45 | 46 | $pscriboParagraph = [PSCustomObject] @{ 47 | Id = $Id 48 | Type = 'PScribo.Paragraph' 49 | Style = $Style 50 | Tabs = $Tabs 51 | Sections = (New-Object -TypeName System.Collections.ArrayList) 52 | Orientation = $script:currentOrientation 53 | IsSectionBreakEnd = $false 54 | } 55 | return $pscriboParagraph 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Examples/Example34.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [System.String[]] $Format = 'Html', 4 | [System.String] $Path = '~\Desktop', 5 | [System.Management.Automation.SwitchParameter] $PassThru 6 | ) 7 | 8 | Import-Module PScribo -Force -Verbose:$false 9 | 10 | $example34 = Document -Name 'PScribo Example 34' { 11 | 12 | <# 13 | Captions can be added to all tables. Note: List tables with a single 14 | row support table captions (as they output a separate table per row 15 | and the table numbering is then broken!). 16 | 17 | The default position is below the table, but this can be overridden 18 | by defining a custom table style using -CaptionLocation 'Above'. 19 | 20 | Table captions are prefixed with the word 'Table'. The prefix can be 21 | changed by defining a custom table style using the -CaptionPrefix 22 | parameter. 23 | 24 | Captions can be styled by defining the required style and assigning 25 | it to the table style's -CaptionStyle parameter. 26 | #> 27 | 28 | $servers = @( 29 | [Ordered] @{ 'Computer Name' = 'DC1'; 'Domain Name' = 'example.local'; FQDN = 'dc1.example.local'; 'IP Address' = '192.168.0.1' } 30 | [Ordered] @{ 'Computer Name' = 'DC2'; 'Domain Name' = 'example.local'; FQDN = 'dc2.example.local'; 'IP Address' = '192.168.0.2' } 31 | [Ordered] @{ 'Computer Name' = 'DC3'; 'Domain Name' = 'example.local'; FQDN = 'dc3.example.local'; 'IP Address' = '192.168.0.3' } 32 | ) 33 | 34 | Table -Hashtable $servers -List -Key 'Computer Name' -Caption '- Server Information' 35 | 36 | <# 37 | The table above, will be rendered like so: 38 | 39 | Computer Name DC1 DC2 DC3 40 | ------------- --- --- --- 41 | DomainName example.local example.local example.local 42 | FQDN dc1.example.local dc2.example.local dc3.example.local 43 | IpAddress 192.168.0.1 192.168.0.2 192.168.0.3 44 | 45 | Table 1 - Server Information 46 | #> 47 | } 48 | $example34 | Export-Document -Path $Path -Format $Format -PassThru:$PassThru 49 | -------------------------------------------------------------------------------- /Tests/Plugins/Json/Out-JsonSection.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Path $MyInvocation.MyCommand.Path -Parent; 2 | $pluginRoot = Split-Path -Path $here -Parent; 3 | $testRoot = Split-Path -Path $pluginRoot -Parent; 4 | $moduleRoot = Split-Path -Path $testRoot -Parent; 5 | Import-Module "$moduleRoot\PScribo.psm1" -Force; 6 | 7 | InModuleScope 'PScribo' { 8 | 9 | Describe 'Plugins\Json\Out-JsonSection' { 10 | 11 | $Document = Document -Name 'TestDocument' -ScriptBlock { } 12 | $pscriboDocument = $Document 13 | 14 | It 'calls Out-JsonParagraph' { 15 | Mock -CommandName Out-JsonParagraph 16 | 17 | Section -Name TestSection -ScriptBlock { Paragraph 'TestParagraph' } | Out-JsonSection 18 | 19 | Assert-MockCalled -CommandName Out-JsonParagraph -Exactly 1 20 | } 21 | 22 | It 'calls Out-JsonParagraph twice' { 23 | Mock -CommandName Out-JsonParagraph 24 | 25 | Section -Name TestSection -ScriptBlock { Paragraph 'TestParagraph'; Paragraph 'TestParagraph'; } | Out-JsonSection 26 | 27 | Assert-MockCalled -CommandName Out-JsonParagraph -Exactly 3 28 | } 29 | 30 | It 'calls Out-JsonTable' { 31 | Mock -CommandName Out-JsonTable 32 | 33 | Section -Name TestSection -ScriptBlock { Get-Process | Select-Object -First 3 | Table TestTable } | Out-JsonSection 34 | 35 | Assert-MockCalled -CommandName Out-JsonTable -Exactly 1 36 | } 37 | 38 | It 'warns on call Out-JsonTOC' { 39 | Mock -CommandName Out-JsonTOC 40 | 41 | Section -Name TestSection -ScriptBlock { TOC 'TestTOC' } | Out-JsonSection -WarningAction SilentlyContinue 42 | 43 | Assert-MockCalled Out-JsonTOC -Exactly 0 44 | } 45 | 46 | It 'calls nested Out-JsonSection' { 47 | ## Note this must be called last in the Describe script block as the Out-XmlSection gets mocked! 48 | Mock -CommandName Out-JsonSection 49 | 50 | Section -Name TestSection -ScriptBlock { Section -Name SubSection { } } | Out-JsonSection 51 | 52 | Assert-MockCalled -CommandName Out-JsonSection -Exactly 1 53 | } 54 | } 55 | } 56 | --------------------------------------------------------------------------------