Src/Plugins/Word/Word.ps1
function ConvertTo-WordColor { <# .SYNOPSIS Converts an HTML color to RRGGBB value as Word does not support short Html color codes #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.String] $Color ) process { $Color = $Color.TrimStart('#') if ($Color.Length -eq 3) { $Color = '{0}{0}{1}{1}{2}{2}' -f $Color[0], $Color[1], $Color[2] } return $Color.ToUpper() } } function Get-WordDocument { <# .SYNOPSIS Outputs Office Open XML document #> [CmdletBinding()] [OutputType([System.Xml.XmlDocument])] param ( # The PScribo document object to convert to a Word xml document [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Document ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $xmlnswpdrawing = 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing' $xmlnsdrawing = 'http://schemas.openxmlformats.org/drawingml/2006/main' $xmlnspicture = 'http://schemas.openxmlformats.org/drawingml/2006/picture' $xmlnsrelationships = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' $xmlnsofficeword14 = 'http://schemas.microsoft.com/office/drawing/2010/main' $xmlnsmath = 'http://schemas.openxmlformats.org/officeDocument/2006/math' $xmlnsmc = 'http://schemas.openxmlformats.org/markup-compatibility/2006' # required for custom number lists $xmlnsw14 = 'http://schemas.microsoft.com/office/word/2010/wordml' # required for custom number lists $xmlDocument = New-Object -TypeName 'System.Xml.XmlDocument' [ref] $null = $xmlDocument.AppendChild($xmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) $documentXml = $xmlDocument.AppendChild($xmlDocument.CreateElement('w', 'document', $xmlns)) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:xml', 'http://www.w3.org/XML/1998/namespace') [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:pic', $xmlnspicture) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:wp', $xmlnswpdrawing) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:a', $xmlnsdrawing) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:r', $xmlnsrelationships) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:m', $xmlnsmath) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:a14', $xmlnsofficeword14) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:mc', $xmlnsmc) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:w14', $xmlnsw14) $body = $documentXml.AppendChild($xmlDocument.CreateElement('w', 'body', $xmlns)) $script:pscriboIsFirstSection = $false foreach ($subSection in $Document.Sections.GetEnumerator()) { $currentIndentationLevel = 1 if ($null -ne $subSection.PSObject.Properties['Level']) { $currentIndentationLevel = $subSection.Level + 1 } Write-PScriboProcessSectionId -SectionId $subSection.Id -SectionType $subSection.Type -Indent $currentIndentationLevel switch ($subSection.Type) { 'PScribo.Section' { Out-WordSection -Section $subSection -Element $body -XmlDocument $xmlDocument } 'PScribo.Paragraph' { [ref] $null = $body.AppendChild((Out-WordParagraph -Paragraph $subSection -XmlDocument $xmlDocument)) } 'PScribo.Image' { $Images += @($subSection) [ref] $null = $body.AppendChild((Out-WordImage -Image $subSection -XmlDocument $xmlDocument)) } 'PScribo.PageBreak' { [ref] $null = $body.AppendChild((Out-WordPageBreak -PageBreak $subSection -XmlDocument $xmlDocument)) } 'PScribo.LineBreak' { [ref] $null = $body.AppendChild((Out-WordLineBreak -LineBreak $subSection -XmlDocument $xmlDocument)) } 'PScribo.Table' { Out-WordTable -Table $subSection -XmlDocument $xmlDocument -Element $body } 'PScribo.TOC' { [ref] $null = $body.AppendChild((Out-WordTOC -TOC $subSection -XmlDocument $xmlDocument)) } 'PScribo.BlankLine' { Out-WordBlankLine -BlankLine $subSection -XmlDocument $xmlDocument -Element $body } 'PScribo.ListReference' { Out-WordList -List $Document.Lists[$subSection.Number -1] -Element $body -XmlDocument $xmlDocument -NumberId $subSection.Number } Default { Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning } } } ## Last section's properties are a child element of body element $lastSectionOrientation = $Document.Sections | Where-Object { $_.Type -in 'PScribo.Section','PScribo.Paragraph' } | Select-Object -Last 1 -ExpandProperty Orientation if ($null -eq $lastSectionOrientation) { $lastSectionOrientation = $Document.Options['PageOrientation'] } $sectionPrParams = @{ PageHeight = $Document.Options['PageHeight'] PageWidth = $Document.Options['PageWidth'] PageMarginTop = $Document.Options['MarginTop'] PageMarginBottom = $Document.Options['MarginBottom'] PageMarginLeft = $Document.Options['MarginLeft'] PageMarginRight = $Document.Options['MarginRight'] Orientation = $lastSectionOrientation } $wordSectionPr = Get-WordSectionPr @sectionPrParams -XmlDocument $xmlDocument [ref] $null = $body.AppendChild($wordSectionPr) return $xmlDocument } } function Get-WordHeaderFooterDocument { <# .SYNOPSIS Outputs Office Open XML footer document #> [CmdletBinding()] [OutputType([System.Xml.XmlDocument])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $HeaderFooter, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Header')] [System.Management.Automation.SwitchParameter] $IsHeader, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Footer')] [System.Management.Automation.SwitchParameter] $IsFooter ) process { ## Create the Style.xml document $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $headerFooterDocument = New-Object -TypeName 'System.Xml.XmlDocument' [ref] $null = $headerFooterDocument.AppendChild($headerFooterDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) if ($IsHeader -eq $true) { $elementName = 'hdr' } elseif ($IsFooter -eq $true) { $elementName = 'ftr' } $element = $headerFooterDocument.CreateElement('w', $elementName, $xmlns) foreach ($subSection in $HeaderFooter.Sections) { switch ($subSection.Type) { 'PScribo.Paragraph' { [ref] $null = $element.AppendChild((Out-WordParagraph -Paragraph $subSection -XmlDocument $headerFooterDocument)) } 'PScribo.Table' { Out-WordTable -Table $subSection -XmlDocument $headerFooterDocument -Element $element } 'PScribo.BlankLine' { Out-WordBlankLine -BlankLine $subSection -XmlDocument $headerFooterDocument -Element $element } 'PScribo.LineBreak' { [ref] $null = $element.AppendChild((Out-WordLineBreak -LineBreak $subSection -XmlDocument $headerFooterDocument)) } } } [ref] $null = $headerFooterDocument.AppendChild($element) return $headerFooterDocument } } function Get-WordListLevel { <# .SYNOPSIS Process (nested) lists and build index of number/bullet styles. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $List, [Parameter(ValueFromPipelineByPropertyName)] [System.Collections.Hashtable] $Levels = @{ } ) process { foreach ($item in $List.Items) { if ($item.Type -eq 'PScribo.Item') { $level = $item.Level -1 if (-not $Levels.ContainsKey($level)) { $Levels[$level] = @{ IsNumbered = $List.IsNumbered NumberStyle = $List.NumberStyle BulletStyle = $List.BulletStyle } } } elseif ($item.Type -eq 'PScribo.List') { $Levels = Get-WordListLevel -List $item -Levels $Levels } } return $Levels } } function Get-WordNumberingDocument { <# .SYNOPSIS Outputs Office Open XML numbering document #> [CmdletBinding()] [OutputType([System.Xml.XmlDocument])] param ( ## PScribo document styles [Parameter(Mandatory, ValueFromPipeline)] [AllowEmptyCollection()] [System.Collections.ArrayList] $Lists ) process { ## Create the numbering.xml document $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $xmlDocument = New-Object -TypeName 'System.Xml.XmlDocument' [ref] $null = $xmlDocument.AppendChild($XmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) $numbering = $xmlDocument.AppendChild($xmlDocument.CreateElement('w', 'numbering', $xmlns)) [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006') [ref] $null = $xmlDocument.DocumentElement.SetAttribute('xmlns:w14', 'http://schemas.microsoft.com/office/word/2010/wordml') foreach ($list in $Lists.GetEnumerator()) { $abstractNum = Get-WordNumberStyle -List $list -XmlDocument $xmlDocument [ref] $null = $numbering.AppendChild($abstractNum) } foreach ($list in $Lists.GetEnumerator()) { $num = $numbering.AppendChild($xmlDocument.CreateElement('w', 'num', $xmlns)) [ref] $null = $num.SetAttribute('numId', $xmlns, $list.Number) $abstractNumId = $num.AppendChild($xmlDocument.CreateElement('w', 'abstractNumId', $xmlns)) [ref] $null = $abstractNumId.SetAttribute('val', $xmlns, $list.Number -1) } return $xmlDocument } } function Get-WordNumberStyle { <# .SYNOPSIS Outputs Office Open XML numbering document #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( ## PScribo document styles [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $List, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Xml.XmlDocument] $XmlDocument ) begin { $bulletMatrix = @{ Disc = [PSCustomObject] @{ Text = '?'; Font = 'Symbol'; } Circle = [PSCustomObject] @{ Text = 'o'; Font = 'Courier New'; } Square = [PSCustomObject] @{ Text = '?'; Font = 'Wingdings'; } Dash = [PSCustomObject] @{ Text = '-'; Font = 'Courier New' } } } process { ## Create the Numbering.xml document $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $xmlnsmc = 'http://schemas.openxmlformats.org/markup-compatibility/2006' $abstractNum = $xmlDocument.CreateElement('w', 'abstractNum', $xmlns) [ref] $null = $abstractNum.SetAttribute('abstractNumId', $xmlns, $list.Number -1) $multiLevelType = $abstractNum.AppendChild($xmlDocument.CreateElement('w', 'multiLevelType', $xmlns)) [ref] $null = $multiLevelType.SetAttribute('val', $xmlns, 'hybridMultilevel') $listLevels = Get-WordListLevel -List $List foreach ($level in ($listLevels.Keys | Sort-Object)) { $listLevel = $listLevels[$level] if ($listLevel.IsNumbered) { $numberStyle = $Document.NumberStyles[$listLevel.NumberStyle] } $lvl = $abstractNum.AppendChild($xmlDocument.CreateElement('w', 'lvl', $xmlns)) [ref] $null = $lvl.SetAttribute('ilvl', $xmlns, $level) $start = $lvl.AppendChild($xmlDocument.CreateElement('w', 'start', $xmlns)) [ref] $null = $start.SetAttribute('val', $xmlns, 1) if ($listLevel.IsNumbered) { switch ($numberStyle.Format) { Number { $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'decimal') [ref] $null = $lvlText.SetAttribute('val', $xmlns, ('%{0}{1}' -f ($level +1), $numberStyle.Suffix)) } Letter { $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) if ($numberStyle.Uppercase) { [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'upperLetter') } else { [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'lowerLetter') } [ref] $null = $lvlText.SetAttribute('val', $xmlns, ('%{0}{1}' -f ($level +1), $numberStyle.Suffix)) } Roman { $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) if ($numberStyle.Uppercase) { [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'upperRoman') } else { [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'lowerRoman') } [ref] $null = $lvlText.SetAttribute('val', $xmlns, ('%{0}{1}' -f ($level +1), $numberStyle.Suffix)) } Custom { $regexMatch = [Regex]::Match($numberStyle.Custom, '%+') if ($regexMatch.Success -eq $true) { $numberString = $regexMatch.Value if ($numberString.Length -eq 1) { $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'decimal') } elseif ($numberString.Length -eq 2) { $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'decimalZero') } else { $AlternateContent = $lvl.AppendChild($xmlDocument.CreateElement('mc', 'AlternateContent', $xmlnsmc)) $Choice = $AlternateContent.AppendChild($xmlDocument.CreateElement('mc', 'Choice', $xmlnsmc)) [ref] $null = $Choice.SetAttribute('Requires', 'w14') $numFmtC = $Choice.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmtC.SetAttribute('val', $xmlns, 'custom') if ($numberString.Length -eq 3) { [ref] $null = $numFmtC.SetAttribute('format', $xmlns, '001, 002, 003, ...') } elseif ($numberString.Length -eq 4) { [ref] $null = $numFmtC.SetAttribute('format', $xmlns, '0001, 0002, 0003, ...') } elseif ($numberString.Length -ge 5) { [ref] $null = $numFmtC.SetAttribute('format', $xmlns, '00001, 00002, 00003, ...') } $Fallback = $AlternateContent.AppendChild($xmlDocument.CreateElement('mc', 'Fallback', $xmlnsmc)) $numFmtF = $Fallback.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmtF.SetAttribute('val', $xmlns, 'decimal') $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) } } else { Write-PScriboMessage 'Invalid custom number format' -IsWarning $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'none') } $replacementString = '%{0}' -f ($level +1) $wordNumberString = $numberStyle.Custom -replace '%+', $replacementString [ref] $null = $lvlText.SetAttribute('val', $xmlns, $wordNumberString) } } } else { $numFmt = $lvl.AppendChild($xmlDocument.CreateElement('w', 'numFmt', $xmlns)) [ref] $null = $numFmt.SetAttribute('val', $xmlns, 'bullet') $lvlText = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlText', $xmlns)) [ref] $null = $lvlText.SetAttribute('val', $xmlns, $bulletMatrix[$listLevel.BulletStyle].Text) $rPr = $lvl.AppendChild($xmlDocument.CreateElement('w', 'rPr', $xmlns)) $rFonts = $rPr.AppendChild($xmlDocument.CreateElement('w', 'rFonts', $xmlns)) [ref] $null = $rFonts.SetAttribute('ascii', $xmlns, $bulletMatrix[$listLevel.BulletStyle].Font) [ref] $null = $rFonts.SetAttribute('hAnsi', $xmlns, $bulletMatrix[$listLevel.BulletStyle].Font) [ref] $null = $rFonts.SetAttribute('hint', $xmlns, 'default') } $lvlJc = $lvl.AppendChild($xmlDocument.CreateElement('w', 'lvlJc', $xmlns)) if ($listLevel.IsNumbered) { $indent = $numberStyle.Indent $hanging = $numberStyle.Hanging if ($numberStyle.Align -eq 'Left') { [ref] $null = $lvlJc.SetAttribute('val', $xmlns,'start') if ($indent -eq 0) { $indent = ($level + 1) * 680 } if ($hanging -eq 0) { $hanging = 400 } } elseif ($numberStyle.Align -eq 'Right') { [ref] $null = $lvlJc.SetAttribute('val', $xmlns, 'end') if ($indent -eq 0) { $indent = ($level + 1) * 680 } if ($hanging -eq 0) { $hanging = 120 } } } else { [ref] $null = $lvlJc.SetAttribute('val', $xmlns, 'Right') $indent = ($level + 1) * 640 $hanging = 280 } $pPr = $lvl.AppendChild($xmlDocument.CreateElement('w', 'pPr', $xmlns)) $ind = $pPr.AppendChild($xmlDocument.CreateElement('w', 'ind', $xmlns)) [ref] $null = $ind.SetAttribute('left', $xmlns, $indent) [ref] $null = $ind.SetAttribute('hanging', $xmlns, $hanging) } return $abstractNum } } function Get-WordParagraphRun { <# .SYNOPSIS Returns an array of strings, split on tokens (PageNumber/TotalPages). #> [CmdletBinding()] [OutputType([System.String[]])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [AllowEmptyString()] [System.String] $Text, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoSpace ) process { $pageNumberMatch = '<!#\s*PageNumber\s*#!>' $totalPagesMatch = '<!#\s*TotalPages\s*#!>' $runs = New-Object -TypeName 'System.Collections.ArrayList' if (-not $NoSpace) { $Text = '{0} ' -f $Text } $pageNumbers = $Text -isplit $pageNumberMatch for ($pn = 0; $pn -lt $pageNumbers.Count; $pn++) { $totalPages = $pageNumbers[$pn] -isplit $totalPagesMatch for ($tp = 0; $tp -lt $totalPages.Count; $tp++) { if (-not [System.String]::IsNullOrWhitespace($totalPages[$tp])) { $null = $runs.Add($totalPages[$tp]) } if ($tp -lt ($totalPages.Count -1)) { $null = $runs.Add('<!#TOTALPAGES#!>') } } if ($pn -lt ($pageNumbers.Count -1)) { $null = $runs.Add('<!#PAGENUMBER#!>') } } return $runs.ToArray() } } function Get-WordParagraphRunPr { <# .SYNOPSIS Outputs paragraph text/run (rPr) properties. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Management.Automation.PSObject] $ParagraphRun, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $rPr = $XmlDocument.CreateElement('w', 'rPr', $xmlns) if ($ParagraphRun.HasStyle -eq $true) { $rStyle = $rPr.AppendChild($XmlDocument.CreateElement('w', 'rStyle', $xmlns)) $characterStyle = '{0}Char' -f $ParagraphRun.Style [ref] $null = $rStyle.SetAttribute('val', $xmlns, $characterStyle) } if ($ParagraphRun.HasInlineStyle -eq $true) { if ($null -ne $ParagraphRun.Font) { $rFonts = $rPr.AppendChild($XmlDocument.CreateElement('w', 'rFonts', $xmlns)) [ref] $null = $rFonts.SetAttribute('ascii', $xmlns, $ParagraphRun.Font[0]) [ref] $null = $rFonts.SetAttribute('hAnsi', $xmlns, $ParagraphRun.Font[0]) } if ($ParagraphRun.Bold -eq $true) { [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'b', $xmlns)) } if ($ParagraphRun.Underline -eq $true) { $u = $rPr.AppendChild($XmlDocument.CreateElement('w', 'u', $xmlns)) [ref] $null = $u.SetAttribute('val', $xmlns, 'single') } if ($ParagraphRun.Italic -eq $true) { [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'i', $xmlns)) } if (-not [System.String]::IsNullOrEmpty($ParagraphRun.Color)) { $wordColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $ParagraphRun.Color) $color = $rPr.AppendChild($XmlDocument.CreateElement('w', 'color', $xmlns)) [ref] $null = $color.SetAttribute('val', $xmlns, $wordColor) } if (($null -ne $ParagraphRun.Size) -and ($ParagraphRun.Size -gt 0)) { $sz = $rPr.AppendChild($XmlDocument.CreateElement('w', 'sz', $xmlns)) [ref] $null = $sz.SetAttribute('val', $xmlns, $ParagraphRun.Size * 2) } } return $rPr } } function Get-WordSectionPr { <# .SYNOPSIS Outputs Office Open XML section element to set page size and margins. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory)] [System.Single] $PageWidth, [Parameter(Mandatory)] [System.Single] $PageHeight, [Parameter(Mandatory)] [System.Single] $PageMarginTop, [Parameter(Mandatory)] [System.Single] $PageMarginLeft, [Parameter(Mandatory)] [System.Single] $PageMarginBottom, [Parameter(Mandatory)] [System.Single] $PageMarginRight, [Parameter(Mandatory)] [System.String] $Orientation, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { if ($Orientation -eq 'Portrait') { $alignedPageHeight = $PageHeight $alignedPageWidth = $PageWidth } elseif ($Orientation -eq 'Landscape') { $alignedPageHeight = $PageWidth $alignedPageWidth = $PageHeight } $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $sectPr = $XmlDocument.CreateElement('w', 'sectPr', $xmlns) $pgSz = $sectPr.AppendChild($XmlDocument.CreateElement('w', 'pgSz', $xmlns)) [ref] $null = $pgSz.SetAttribute('w', $xmlns, (ConvertTo-Twips -Millimeter $alignedPageWidth)) [ref] $null = $pgSz.SetAttribute('h', $xmlns, (ConvertTo-Twips -Millimeter $alignedPageHeight)) [ref] $null = $pgSz.SetAttribute('orient', $xmlns, $Orientation.ToLower()) $pgMar = $sectPr.AppendChild($XmlDocument.CreateElement('w', 'pgMar', $xmlns)) [ref] $null = $pgMar.SetAttribute('top', $xmlns, (ConvertTo-Twips -Millimeter $PageMarginTop)) [ref] $null = $pgMar.SetAttribute('header', $xmlns, (ConvertTo-Twips -Millimeter $PageMarginTop)) [ref] $null = $pgMar.SetAttribute('bottom', $xmlns, (ConvertTo-Twips -Millimeter $PageMarginBottom)) [ref] $null = $pgMar.SetAttribute('footer', $xmlns, (ConvertTo-Twips -Millimeter $PageMarginBottom)) [ref] $null = $pgMar.SetAttribute('left', $xmlns, (ConvertTo-Twips -Millimeter $PageMarginLeft)) [ref] $null = $pgMar.SetAttribute('right', $xmlns, (ConvertTo-Twips -Millimeter $PageMarginRight)) if ($Document.Header.HasFirstPageHeader) { $headerReference = $sectPr.AppendChild($xmlDocument.CreateElement('w', 'headerReference', $xmlns)) [ref] $null = $headerReference.SetAttribute('type', $xmlns, 'first') [ref] $null = $headerReference.SetAttribute('id', $xmlnsrelationships, 'rId3') } if ($Document.Header.HasDefaultHeader) { $headerReference = $sectPr.AppendChild($xmlDocument.CreateElement('w', 'headerReference', $xmlns)) [ref] $null = $headerReference.SetAttribute('type', $xmlns, 'default') [ref] $null = $headerReference.SetAttribute('id', $xmlnsrelationships, 'rId4') } if ($Document.Footer.HasFirstPageFooter) { $footerReference = $sectPr.AppendChild($xmlDocument.CreateElement('w', 'footerReference', $xmlns)) [ref] $null = $footerReference.SetAttribute('type', $xmlns, 'first') [ref] $null = $footerReference.SetAttribute('id', $xmlnsrelationships, 'rId5') } if ($Document.Footer.HasDefaultFooter) { $footerReference = $sectPr.AppendChild($xmlDocument.CreateElement('w', 'footerReference', $xmlns)) [ref] $null = $footerReference.SetAttribute('type', $xmlns, 'default') [ref] $null = $footerReference.SetAttribute('id', $xmlnsrelationships, 'rId6') } if (-not $script:pscriboIsFirstSection) { [ref] $null = $sectPr.AppendChild($xmlDocument.CreateElement('w', 'titlePg', $xmlns)) $script:pscriboIsFirstSection = $true } return $sectPr } } function Get-WordSettingsDocument { <# .SYNOPSIS Outputs Office Open XML settings document #> [CmdletBinding()] [OutputType([System.Xml.XmlDocument])] param ( [Parameter()] [System.Management.Automation.SwitchParameter] $UpdateFields ) process { ## Create the Style.xml document $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $settingsDocument = New-Object -TypeName 'System.Xml.XmlDocument' [ref] $null = $settingsDocument.AppendChild($settingsDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) $settings = $settingsDocument.AppendChild($settingsDocument.CreateElement('w', 'settings', $xmlns)) ## Set compatibility mode to Word 2013 $compat = $settings.AppendChild($settingsDocument.CreateElement('w', 'compat', $xmlns)) $compatSetting = $compat.AppendChild($settingsDocument.CreateElement('w', 'compatSetting', $xmlns)) [ref] $null = $compatSetting.SetAttribute('name', $xmlns, 'compatibilityMode') [ref] $null = $compatSetting.SetAttribute('uri', $xmlns, 'http://schemas.microsoft.com/office/word') [ref] $null = $compatSetting.SetAttribute('val', $xmlns, 15) if ($UpdateFields) { $wupdateFields = $settings.AppendChild($settingsDocument.CreateElement('w', 'updateFields', $xmlns)) [ref] $null = $wupdateFields.SetAttribute('val', $xmlns, 'true') } return $settingsDocument } } function Get-WordStyle { <# .SYNOPSIS Generates Word Xml style element from a PScribo document style. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( ## PScribo document style [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Style, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument, [Parameter(Mandatory)] [ValidateSet('Paragraph', 'Character')] [System.String] $Type ) begin { ## Semi hide styles behind table styles (except default style!) $hiddenStyleIds = $Document.TableStyles.Values | ForEach-Object -Process { $_.HeaderStyle; $_.RowStyle; $_.AlternateRowStyle } } process { if ($Type -eq 'Paragraph') { $styleId = $Style.Id $styleName = $Style.Name $linkId = '{0}Char' -f $Style.Id } else { $styleId = '{0}Char' -f $Style.Id $styleName = '{0} Char' -f $Style.Name $linkId = $Style.Id } $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $documentStyle = $XmlDocument.CreateElement('w', 'style', $xmlns) [ref] $null = $documentStyle.SetAttribute('type', $xmlns, $Type.ToLower()) [ref] $null = $documentStyle.SetAttribute('styleId', $xmlns, $styleId) if ($Style.Id -eq $Document.DefaultStyle) { ## Set as default style [ref] $null = $documentStyle.SetAttribute('default', $xmlns, 1) $uiPriority = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'uiPriority', $xmlns)) [ref] $null = $uiPriority.SetAttribute('val', $xmlns, 1) } elseif ($Style.Hidden -eq $true) { ## Semi hide style (headers and footers etc) [ref] $null = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'semiHidden', $xmlns)) } elseif ($hiddenStyleIds -contains $Style.Id) { [ref] $null = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'semiHidden', $xmlns)) } $documentStyleName = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'name', $xmlns)) [ref] $null = $documentStyleName.SetAttribute('val', $xmlns, $styleName) $basedOn = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'basedOn', $xmlns)) [ref] $null = $basedOn.SetAttribute('val', $xmlns, 'Normal') $link = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'link', $xmlns)) [ref] $null = $link.SetAttribute('val', $xmlns, $linkId) $next = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'next', $xmlns)) [ref] $null = $next.SetAttribute('val', $xmlns, 'Normal') [ref] $null = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'qFormat', $xmlns)) $pPr = $documentStyle.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) [ref] $null = $pPr.AppendChild($XmlDocument.CreateElement('w', 'keepLines', $xmlns)) $spacing = $pPr.AppendChild($XmlDocument.CreateElement('w', 'spacing', $xmlns)) [ref] $null = $spacing.SetAttribute('before', $xmlns, 0) [ref] $null = $spacing.SetAttribute('after', $xmlns, 0) $jc = $pPr.AppendChild($XmlDocument.CreateElement('w', 'jc', $xmlns)) if ($Style.Align.ToLower() -eq 'justify') { [ref] $null = $jc.SetAttribute('val', $xmlns, 'distribute') } else { [ref] $null = $jc.SetAttribute('val', $xmlns, $Style.Align.ToLower()) } if ($Style.BackgroundColor) { $backgroundColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $Style.BackgroundColor) $shd = $pPr.AppendChild($XmlDocument.CreateElement('w', 'shd', $xmlns)) [ref] $null = $shd.SetAttribute('val', $xmlns, 'clear') [ref] $null = $shd.SetAttribute('color', $xmlns, 'auto') [ref] $null = $shd.SetAttribute('fill', $xmlns, $backgroundColor) } $rPr = Get-WordStyleRunPr -Style $Style -XmlDocument $XmlDocument [ref] $null = $documentStyle.AppendChild($rPr) return $documentStyle } } function Get-WordStyleRunPr { <# .SYNOPSIS Generates Word run (rPr) formatting properties #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory)] [System.Management.Automation.PSObject] $Style, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $rPr = $XmlDocument.CreateElement('w', 'rPr', $xmlns) $rFonts = $rPr.AppendChild($XmlDocument.CreateElement('w', 'rFonts', $xmlns)) [ref] $null = $rFonts.SetAttribute('ascii', $xmlns, $Style.Font[0]) [ref] $null = $rFonts.SetAttribute('hAnsi', $xmlns, $Style.Font[0]) if ($Style.Bold) { [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'b', $xmlns)) } if ($Style.Underline) { [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'u', $xmlns)) } if ($Style.Italic) { [ref] $null = $rPr.AppendChild($XmlDocument.CreateElement('w', 'i', $xmlns)) } $wordColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $Style.Color) $color = $rPr.AppendChild($XmlDocument.CreateElement('w', 'color', $xmlns)) [ref] $null = $color.SetAttribute('val', $xmlns, $wordColor) $sz = $rPr.AppendChild($XmlDocument.CreateElement('w', 'sz', $xmlns)) [ref] $null = $sz.SetAttribute('val', $xmlns, $Style.Size * 2) return $rPr } } function Get-WordStylesDocument { <# .SYNOPSIS Outputs Office Open XML style document #> [CmdletBinding()] [OutputType([System.Xml.XmlDocument])] param ( ## PScribo document styles [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] $Styles, ## PScribo document tables styles [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] $TableStyles ) process { ## Create the Style.xml document $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $xmlDocument = New-Object -TypeName 'System.Xml.XmlDocument' [ref] $null = $xmlDocument.AppendChild($XmlDocument.CreateXmlDeclaration('1.0', 'utf-8', 'yes')) $documentStyles = $xmlDocument.AppendChild($xmlDocument.CreateElement('w', 'styles', $xmlns)) ## Create the document default style $defaultStyle = $Styles[$Document.DefaultStyle] $docDefaults = $documentStyles.AppendChild($xmlDocument.CreateElement('w', 'docDefaults', $xmlns)) $rPrDefault = $docDefaults.AppendChild($xmlDocument.CreateElement('w', 'rPrDefault', $xmlns)) $rPr = Get-WordStyleRunPr -Style $defaultStyle -XmlDocument $XmlDocument [ref] $null = $rPrDefault.AppendChild($rPr) $pPrDefault = $docDefaults.AppendChild($xmlDocument.CreateElement('w', 'pPrDefault', $xmlns)) $pPr = $pPrDefault.AppendChild($xmlDocument.CreateElement('w', 'pPr', $xmlns)) $spacing = $pPr.AppendChild($xmlDocument.CreateElement('w', 'spacing', $xmlns)) [ref] $null = $spacing.SetAttribute('after', $xmlns, '0') ## Create default style (will inherit from the document default style) $documentStyle = $documentStyles.AppendChild($xmlDocument.CreateElement('w', 'style', $xmlns)) [ref] $null = $documentStyle.SetAttribute('type', $xmlns, 'paragraph') [ref] $null = $documentStyle.SetAttribute('default', $xmlns, '1') [ref] $null = $documentStyle.SetAttribute('styleId', $xmlns, $defaultStyle.Id) $documentStyleName = $documentStyle.AppendChild($xmlDocument.CreateElement('w', 'name', $xmlns)) [ref] $null = $documentStyleName.SetAttribute('val', $xmlns, $defaultStyle.Id) [ref] $null = $documentStyle.AppendChild($xmlDocument.CreateElement('w', 'qFormat', $xmlns)) ## Create default character style (will inherit from the document default style) $documentCharacterStyleId = '{0}Char' -f $defaultStyle.Id $documentCharacterStyle = $documentStyles.AppendChild($xmlDocument.CreateElement('w', 'style', $xmlns)) [ref] $null = $documentCharacterStyle.SetAttribute('type', $xmlns, 'character') [ref] $null = $documentCharacterStyle.SetAttribute('default', $xmlns, '1') [ref] $null = $documentCharacterStyle.SetAttribute('styleId', $xmlns, 'name') $documentCharacterStyleName = $documentCharacterStyle.AppendChild($xmlDocument.CreateElement('w', 'name', $xmlns)) [ref] $null = $documentCharacterStyleName.SetAttribute('val', $xmlns, $documentCharacterStyleId) $nonDefaultStyles = $Styles.Values | Where-Object { $_.Id -ne $defaultStyle.Id } foreach ($Style in $nonDefaultStyles) { $documentParagraphStyle = Get-WordStyle -Style $Style -XmlDocument $xmlDocument -Type Paragraph [ref] $null = $documentStyles.AppendChild($documentParagraphStyle) $documentCharacterStyle = Get-WordStyle -Style $Style -XmlDocument $xmlDocument -Type Character [ref] $null = $documentStyles.AppendChild($documentCharacterStyle) } foreach ($tableStyle in $TableStyles.Values) { $documentTableStyle = Get-WordTableStyle -TableStyle $tableStyle -XmlDocument $XmlDocument [ref] $null = $documentStyles.AppendChild($documentTableStyle) } return $xmlDocument } } function Get-WordTableCaption { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Table, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style $caption = ' {0}' -f $Table.Caption $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $p = $XmlDocument.CreateElement('w', 'p', $xmlns) $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) $pStyle = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pStyle', $xmlns)) [ref] $null = $pStyle.SetAttribute('val', $xmlns, $tableStyle.CaptionStyle) $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $t = $r.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') $paddedTableCaption = '{0} ' -f $tableStyle.CaptionPrefix [ref] $null = $t.AppendChild($XmlDocument.CreateTextNode($paddedTableCaption)) $fldSimple = $p.AppendChild($XmlDocument.CreateElement('w', 'fldSimple', $xmlns)) [ref] $null = $fldSimple.SetAttribute('instr', $xmlns, ' SEQ Table \* ARABIC ') $r2 = $fldSimple.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $rPr2 = $r2.AppendChild($XmlDocument.CreateElement('w', 'rPr', $xmlns)) [ref] $null = $rPr2.AppendChild($XmlDocument.CreateElement('w', 'noProof', $xmlns)) $t2 = $r2.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t2.AppendChild($XmlDocument.CreateTextNode($Table.CaptionNumber)) $r3 = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $rPr3 = $r3.AppendChild($XmlDocument.CreateElement('w', 'rPr', $xmlns)) [ref] $null = $rPr3.AppendChild($XmlDocument.CreateElement('w', 'noProof', $xmlns)) $t3 = $r3.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t3.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') [ref] $null = $t3.AppendChild($XmlDocument.CreateTextNode($caption)) return $p } } function Get-WordTableConditionalFormattingValue { <# .SYNOPSIS Generates legacy table conditioning formatting value (for LibreOffice). #> [CmdletBinding()] [OutputType([System.Int32])] param ( [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $HasFirstRow, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $HasLastRow, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $HasFirstColumn, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $HasLastColumn, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoHorizontalBand, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoVerticalBand ) process { $val = 0 if ($HasFirstRow) { $val = $val -bor 0x20 } if ($HasLastRow) { $val = $val -bor 0x40 } if ($HasFirstColumn) { $val = $val -bor 0x80 } if ($HasLastColumn) { $val = $val -bor 0x100 } if ($NoHorizontalBand) { $val = $val -bor 0x200 } if ($NoVerticalBand) { $val = $val -bor 0x400 } return $val } } function Get-WordTablePr { <# .SYNOPSIS Creates a scaffold Word <w:tblPr> element #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Table, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $tableStyle = $Document.TableStyles[$Table.Style] $tblPr = $XmlDocument.CreateElement('w', 'tblPr', $xmlns) $tblStyle = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblStyle', $xmlns)) [ref] $null = $tblStyle.SetAttribute('val', $xmlns, $tableStyle.Id) $tblInd = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblInd', $xmlns)) [ref] $null = $tblInd.SetAttribute('w', $xmlns, (720 * $Table.Tabs)) [ref] $null = $tblInd.SetAttribute('type', $xmlns, 'dxa') $getWordTableRenderWidthMmParams = @{ TableWidth = $Table.Width Tabs = $Table.Tabs Orientation = $Table.Orientation } $tableRenderWidthMm = Get-WordTableRenderWidthMm @getWordTableRenderWidthMmParams $tableRenderWidthTwips = ConvertTo-Twips -Millimeter $tableRenderWidthMm $tblLayout = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblLayout', $xmlns)) if ($Table.ColumnWidths -or ($Table.Width -gt 0 -and $Table.Width -le 100)) { [ref] $null = $tblLayout.SetAttribute('type', $xmlns, 'fixed') } else { [ref] $null = $tblLayout.SetAttribute('type', $xmlns, 'autofit') } $tableAlignment = @{ Left = 'start'; Center = 'center'; Right = 'end'; } $jc = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'jc', $xmlns)) [ref] $null = $jc.SetAttribute('val', $xmlns, $tableAlignment[$tableStyle.Align]) if ($Table.Width -gt 0) { $tblW = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblW', $xmlns)) [ref] $null = $tblW.SetAttribute('w', $xmlns, $tableRenderWidthTwips) [ref] $null = $tblW.SetAttribute('type', $xmlns, 'dxa') } $tblLook = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblLook', $xmlns)) $isFirstRow = ($Table.IsKeyedList -eq $true -or $Table.IsList -eq $false) $isFirstColumn = ($Table.IsKeyedList -eq $true -or $Table.IsList -eq $true) ## LibreOffice requires legacy conditional formatting value (#99) $val = Get-WordTableConditionalFormattingValue -HasFirstRow:$isFirstRow -HasFirstColumn:$isFirstColumn -NoVerticalBand [ref] $null = $tblLook.SetAttribute('val', $xmlns, ('{0:x4}' -f $val)) [ref] $null = $tblLook.SetAttribute('firstRow', $xmlns, ($isFirstRow -as [System.Int32])) [ref] $null = $tblLook.SetAttribute('lastRow', $xmlns, '0') [ref] $null = $tblLook.SetAttribute('firstColumn', $xmlns, ($isFirstColumn -as [System.Int32])) [ref] $null = $tblLook.SetAttribute('lastColumn', $xmlns, '0') [ref] $null = $tblLook.SetAttribute('noHBand', $xmlns, '0') [ref] $null = $tblLook.SetAttribute('noVBand', $xmlns, '1') if ($tableStyle.BorderWidth -gt 0) { $tblBorders = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblBorders', $xmlns)) foreach ($border in @('top', 'bottom', 'start', 'end', 'insideH', 'insideV')) { $borderType = $tblBorders.AppendChild($XmlDocument.CreateElement('w', $border, $xmlns)) [ref] $null = $borderType.SetAttribute('sz', $xmlns, (ConvertTo-Octips $tableStyle.BorderWidth)) [ref] $null = $borderType.SetAttribute('val', $xmlns, 'single') [ref] $null = $borderType.SetAttribute('color', $xmlns, (ConvertTo-WordColor -Color $tableStyle.BorderColor)) } } $tblCellMar = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblCellMar', $xmlns)) $top = $tblCellMar.AppendChild($XmlDocument.CreateElement('w', 'top', $xmlns)) [ref] $null = $top.SetAttribute('w', $xmlns, (ConvertTo-Twips $tableStyle.PaddingTop)) [ref] $null = $top.SetAttribute('type', $xmlns, 'dxa') $start = $tblCellMar.AppendChild($XmlDocument.CreateElement('w', 'start', $xmlns)) [ref] $null = $start.SetAttribute('w', $xmlns, (ConvertTo-Twips $tableStyle.PaddingLeft)) [ref] $null = $start.SetAttribute('type', $xmlns, 'dxa') $bottom = $tblCellMar.AppendChild($XmlDocument.CreateElement('w', 'bottom', $xmlns)) [ref] $null = $bottom.SetAttribute('w', $xmlns, (ConvertTo-Twips $tableStyle.PaddingBottom)) [ref] $null = $bottom.SetAttribute('type', $xmlns, 'dxa') $end = $tblCellMar.AppendChild($XmlDocument.CreateElement('w', 'end', $xmlns)) [ref] $null = $end.SetAttribute('w', $xmlns, (ConvertTo-Twips $tableStyle.PaddingRight)) [ref] $null = $end.SetAttribute('type', $xmlns, 'dxa') $spacing = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'spacing', $xmlns)) [ref] $null = $spacing.SetAttribute('before', $xmlns, 72) [ref] $null = $spacing.SetAttribute('after', $xmlns, 72) return $tblPr } } function Get-WordTableRenderWidthMm { <# .SYNOPSIS Calcualtes a table's (maximum) rendering size in millimetres. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateRange(0, 100)] [System.UInt16] $TableWidth, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateRange(0, 10)] [System.UInt16] $Tabs, [Parameter(Mandatory, ValueFromPipeline)] [System.String] $Orientation ) process { if ($TableWidth -eq 0) { ## Word will autofit contents as necessary, but LibreOffice won't ## so we just have assume that we're using all available width $TableWidth = 100 } if ($Orientation -eq 'Portrait') { $pageWidthMm = $Document.Options['PageWidth'] - ($Document.Options['MarginLeft'] + $Document.Options['MarginRight']) } elseif ($Orientation -eq 'Landscape') { $pageWidthMm = $Document.Options['PageHeight'] - ($Document.Options['MarginLeft'] + $Document.Options['MarginRight']) } $indentWidthMm = ConvertTo-Mm -Point ($Tabs * 36) $tableWidthRenderMm = (($pageWidthMm / 100) * $TableWidth) + $indentWidthMm if ($tableWidthRenderMm -gt $pageWidthMm) { ## We now need to deal with tables being pushed outside the page margin so just return the maximum permitted $tableWidthRenderMm = $pageWidthMm - $indentWidthMm Write-PScriboMessage -Message ($localized.TableWidthOverflowWarning -f $tableWidthRenderMm) -IsWarning } return $tableWidthRenderMm } } function Get-WordTableStyle { <# .SYNOPSIS Generates Word Xml table style element from a PScribo document table style. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( ## PScribo document table style [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $TableStyle, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $style = $XmlDocument.CreateElement('w', 'style', $xmlns) [ref] $null = $style.SetAttribute('type', $xmlns, 'table') [ref] $null = $style.SetAttribute('customStyle', $xmlns, '1') [ref] $null = $style.SetAttribute('styleId', $xmlns, $tableStyle.Id) $name = $style.AppendChild($XmlDocument.CreateElement('w', 'name', $xmlns)) [ref] $null = $name.SetAttribute('val', $xmlns, $TableStyle.Id) $basedOn = $style.AppendChild($XmlDocument.CreateElement('w', 'basedOn', $xmlns)) [ref] $null = $basedOn.SetAttribute('val', $xmlns, 'TableNormal') $tblPr = $style.AppendChild($XmlDocument.CreateElement('w', 'tblPr', $xmlns)) $tblStyleRowBandSize = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblStyleRowBandSize', $xmlns)) [ref] $null = $tblStyleRowBandSize.SetAttribute('val', $xmlns, 1) if ($TableStyle.BorderWidth -gt 0) { $tblBorders = $tblPr.AppendChild($XmlDocument.CreateElement('w', 'tblBorders', $xmlns)) foreach ($border in @('top', 'bottom', 'start', 'end', 'insideH', 'insideV')) { $borderType = $tblBorders.AppendChild($XmlDocument.CreateElement('w', $border, $xmlns)) [ref] $null = $borderType.SetAttribute('sz', $xmlns, (ConvertTo-Octips $tableStyle.BorderWidth)) [ref] $null = $borderType.SetAttribute('val', $xmlns, 'single') $borderColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $tableStyle.BorderColor) [ref] $null = $borderType.SetAttribute('color', $xmlns, (ConvertTo-WordColor -Color $borderColor)) } } [ref] $null = $Style.AppendChild((Get-WordTableStylePr -Style $Document.Styles[$TableStyle.HeaderStyle] -Type HeaderRow -XmlDocument $XmlDocument)) [ref] $null = $Style.AppendChild((Get-WordTableStylePr -Style $Document.Styles[$TableStyle.HeaderStyle] -Type HeaderColumn -XmlDocument $XmlDocument)) [ref] $null = $Style.AppendChild((Get-WordTableStylePr -Style $Document.Styles[$TableStyle.RowStyle] -Type Row -XmlDocument $XmlDocument)) [ref] $null = $Style.AppendChild((Get-WordTableStylePr -Style $Document.Styles[$TableStyle.AlternateRowStyle] -Type AlternateRow -XmlDocument $XmlDocument)) return $style } } function Get-WordTableStylePr { <# .SYNOPSIS Generates Word table style (tblStylePr) formatting properties for specified table style type #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory)] [System.Management.Automation.PSObject] $Style, [Parameter(Mandatory)] [ValidateSet('HeaderRow', 'HeaderColumn', 'Row', 'AlternateRow')] [System.String] $Type, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { if ($Type -eq 'HeaderRow') { $tblStylePrType = 'firstRow' } elseif ($Type -eq 'HeaderColumn') { $tblStylePrType = 'firstCol' } elseif ($Type -eq 'Row') { $tblStylePrType = 'band1Horz' } elseif ($Type -eq 'AlternateRow') { $tblStylePrType = 'band2Horz' } $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $tblStylePr = $XmlDocument.CreateElement('w', 'tblStylePr', $xmlns) [ref] $null = $tblStylePr.SetAttribute('type', $xmlns, $tblStylePrType) $rPr = Get-WordStyleRunPr -Style $Style -XmlDocument $XmlDocument [ref] $null = $tblStylePr.AppendChild($rPr) $null = $tblStylePr.AppendChild($XmlDocument.CreateElement('w', 'tblPr', $xmlns)) $tcPr = $tblStylePr.AppendChild($XmlDocument.CreateElement('w', 'tcPr', $xmlns)) if (-not [System.String]::IsNullOrEmpty($Style.BackgroundColor)) { $shd = $tcPr.AppendChild($XmlDocument.CreateElement('w', 'shd', $xmlns)) [ref] $null = $shd.SetAttribute('val', $xmlns, 'clear') [ref] $null = $shd.SetAttribute('color', $xmlns, 'auto') $backgroundColor = ConvertTo-WordColor -Color $Style.BackgroundColor [ref] $null = $shd.SetAttribute('fill', $xmlns, $backgroundColor) } $pPr = $tblStylePr.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) $jc = $pPr.AppendChild($XmlDocument.CreateElement('w', 'jc', $xmlns)) if ($Style.Align.ToLower() -eq 'justify') { [ref] $null = $jc.SetAttribute('val', $xmlns, 'distribute') } else { [ref] $null = $jc.SetAttribute('val', $xmlns, $Style.Align.ToLower()) } return $tblStylePr } } function Out-WordBlankLine { <# .SYNOPSIS Output formatted Word xml blank line (empty paragraph). #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $BlankLine, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument, [Parameter(Mandatory)] [System.Xml.XmlElement] $Element ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' for ($i = 0; $i -lt $BlankLine.LineCount; $i++) { [ref] $null = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) } } } function Out-WordDocument { <# .SYNOPSIS Microsoft Word output plugin for PScribo. .DESCRIPTION Outputs a Word document representation of a PScribo document object. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'pluginName')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','Options')] [OutputType([System.IO.FileInfo])] param ( ## ThePScribo document object to convert to a text document [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Document, ## Output directory path for the .txt file [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.String] $Path, ### Hashtable of all plugin supported options [Parameter()] [AllowNull()] [System.Collections.Hashtable] $Options ) process { $pluginName = 'Word' $stopwatch = [Diagnostics.Stopwatch]::StartNew() Write-PScriboMessage -Message ($localized.DocumentProcessingStarted -f $Document.Name) ## Generate the Word 'document.xml' document part $documentXml = Get-WordDocument -Document $Document ## Generate the Word 'styles.xml' document part $stylesXml = Get-WordStylesDocument -Styles $Document.Styles -TableStyles $Document.TableStyles ## Generate the Word 'settings.xml' document part $updateFields = (($Document.Properties['TOCs']) -and ($Document.Properties['TOCs'] -gt 0)) $settingsXml = Get-WordSettingsDocument -UpdateFields:$updateFields #Convert relative or PSDrive based path to the absolute filesystem path $absolutePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) $destinationPath = Join-Path -Path $absolutePath ('{0}.docx' -f $Document.Name) if ($PSVersionTable['PSEdition'] -ne 'Core') { ## "Unable to determine the identity of the domain" fix (https://github.com/AsBuiltReport/AsBuiltReport.Core/issues/17) $currentAppDomain = [System.Threading.Thread]::GetDomain() $securityIdentityField = $currentAppDomain.GetType().GetField("_SecurityIdentity", ([System.Reflection.BindingFlags]::Instance -bOr [System.Reflection.BindingFlags]::NonPublic)) $replacementEvidence = New-Object -TypeName 'System.Security.Policy.Evidence' $securityZoneMyComputer = New-Object -TypeName 'System.Security.Policy.Zone' -ArgumentList @([System.Security.SecurityZone]::MyComputer) $replacementEvidence.AddHost($securityZoneMyComputer) $securityIdentityField.SetValue($currentAppDomain, $replacementEvidence) ## WindowsBase.dll is not included in Core PowerShell Add-Type -AssemblyName WindowsBase } try { $package = [System.IO.Packaging.Package]::Open($destinationPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite) } catch { Write-PScriboMessage -Message ($localized.OpenPackageError -f $destinationPath) -IsWarning throw $_ } ## Create document.xml part $documentUri = New-Object -TypeName System.Uri -ArgumentList ('/word/document.xml', [System.UriKind]::Relative) Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $documentUri) $documentPart = $package.CreatePart($documentUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($documentPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $documentUri) $documentXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() ## Create styles.xml part $stylesUri = New-Object -TypeName System.Uri -ArgumentList ('/word/styles.xml', [System.UriKind]::Relative) Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $stylesUri) $stylesPart = $package.CreatePart($stylesUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($stylesPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $stylesUri) $stylesXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() ## Create settings.xml part $settingsUri = New-Object -TypeName System.Uri -ArgumentList ('/word/settings.xml', [System.UriKind]::Relative) Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $settingsUri) $settingsPart = $package.CreatePart($settingsUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($settingsPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $settingsUri) $settingsXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() Out-WordHeaderFooterDocument -Document $Document -Package $package ## Create the Package relationships Write-PScriboMessage -Message $localized.GeneratingPackageRelationships $officeDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument' $stylesDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles' $settingsDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings' [ref] $null = $package.CreateRelationship($documentUri, [System.IO.Packaging.TargetMode]::Internal, $officeDocumentUri, 'rId1') [ref] $null = $documentPart.CreateRelationship($stylesUri, [System.IO.Packaging.TargetMode]::Internal, $stylesDocumentUri, 'rId1') [ref] $null = $documentPart.CreateRelationship($settingsUri, [System.IO.Packaging.TargetMode]::Internal, $settingsDocumentUri, 'rId2') $headerDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header' if ($Document.Header.HasFirstPageHeader) { $firstPageHeaderUri = New-Object -TypeName System.Uri -ArgumentList ('/word/firstPageHeader.xml', [System.UriKind]::Relative) [ref] $null = $documentPart.CreateRelationship($firstPageHeaderUri, [System.IO.Packaging.TargetMode]::Internal, $headerDocumentUri, 'rId3') } if ($Document.Header.HasDefaultHeader) { $defaultHeaderUri = New-Object -TypeName System.Uri -ArgumentList ('/word/defaultHeader.xml', [System.UriKind]::Relative) [ref] $null = $documentPart.CreateRelationship($defaultHeaderUri, [System.IO.Packaging.TargetMode]::Internal, $headerDocumentUri, 'rId4') } $footerDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer' if ($Document.Footer.HasFirstPageFooter) { $firstPageFooterUri = New-Object -TypeName System.Uri -ArgumentList ('/word/firstPageFooter.xml', [System.UriKind]::Relative) [ref] $null = $documentPart.CreateRelationship($firstPageFooterUri, [System.IO.Packaging.TargetMode]::Internal, $footerDocumentUri, 'rId5') } if ($Document.Footer.HasDefaultFooter) { $defaultFooterUri = New-Object -TypeName System.Uri -ArgumentList ('/word/defaultFooter.xml', [System.UriKind]::Relative) [ref] $null = $documentPart.CreateRelationship($defaultFooterUri, [System.IO.Packaging.TargetMode]::Internal, $footerDocumentUri, 'rId6') } ## Create numbering.xml part $numberingUri = New-Object -TypeName System.Uri -ArgumentList ('/word/numbering.xml', [System.UriKind]::Relative) Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $numberingUri) $numberingPart = $package.CreatePart($numberingUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($numberingPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $numberingUri) $numberingXml = Get-WordNumberingDocument -Lists $Document.Lists $numberingXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() $numberingDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering' [ref] $null = $documentPart.CreateRelationship($numberingUri, [System.IO.Packaging.TargetMode]::Internal, $numberingDocumentUri, 'rId7') ## Process images (assuming we have a section, e.g. example03.ps1) if ($Document.Sections.Count -gt 0) { $imageDocumentUri = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image' foreach ($image in (Get-PScriboImage -Section $Document.Sections)) { try { $uri = ('/word/media/{0}' -f $image.Name) $partName = New-Object -TypeName 'System.Uri' -ArgumentList ($uri, [System.UriKind]'Relative') $part = $package.CreatePart($partName, $image.MimeType) $stream = $part.GetStream() $stream.Write($image.Bytes, 0, $image.Bytes.Length) $stream.Close() [ref] $null = $documentPart.CreateRelationship($partName, [System.IO.Packaging.TargetMode]::Internal, $imageDocumentUri, $image.Name) } catch { throw $_ } finally { if ($null -ne $stream) { $stream.Close() } } } } $package.Flush() $package.Close() $stopwatch.Stop() Write-PScriboMessage -Message ($localized.DocumentProcessingCompleted -f $Document.Name) if ($stopwatch.Elapsed.TotalSeconds -gt 90) { Write-PScriboMessage -Message ($localized.TotalProcessingTimeMinutes -f $stopwatch.Elapsed.TotalMinutes) } else { Write-PScriboMessage -Message ($localized.TotalProcessingTimeSeconds -f $stopwatch.Elapsed.TotalSeconds) } Write-Output -InputObject (Get-Item -Path $destinationPath) } } function Out-WordHeaderFooterDocument { [CmdletBinding()] param ( ## ThePScribo document object to convert to a text document [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Document, [Parameter(Mandatory)] [System.IO.Packaging.Package] $Package ) process { ## Create headers if ($Document.Header.HasFirstPageHeader) { $firstPageHeaderUri = New-Object -TypeName System.Uri -ArgumentList ('/word/firstPageHeader.xml', [System.UriKind]::Relative) $firstPageHeaderXml = Get-WordHeaderFooterDocument -HeaderFooter $Document.Header.FirstPageHeader -IsHeader Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $firstPageHeaderUri) $headerPart = $Package.CreatePart($firstPageHeaderUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($headerPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $firstPageHeaderUri) $firstPageHeaderXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() } if ($Document.Header.HasDefaultHeader) { $defaultHeaderUri = New-Object -TypeName System.Uri -ArgumentList ('/word/defaultHeader.xml', [System.UriKind]::Relative) $defaultHeaderXml = Get-WordHeaderFooterDocument -HeaderFooter $Document.Header.DefaultHeader -IsHeader Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $defaultHeaderUri) $headerPart = $Package.CreatePart($defaultHeaderUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($headerPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $defaultHeaderUri) $defaultHeaderXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() } ## Create footers if ($Document.Footer.HasFirstPageFooter) { $firstPageFooterUri = New-Object -TypeName System.Uri -ArgumentList ('/word/firstPageFooter.xml', [System.UriKind]::Relative) $firstPageFooterXml = Get-WordHeaderFooterDocument -HeaderFooter $Document.Footer.FirstPageFooter -IsFooter Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $firstPageFooterUri) $footerPart = $Package.CreatePart($firstPageFooterUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($footerPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $firstPageFooterUri) $firstPageFooterXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() } if ($Document.Footer.HasDefaultFooter) { $defaultFooterUri = New-Object -TypeName System.Uri -ArgumentList ('/word/defaultFooter.xml', [System.UriKind]::Relative) $defaultFooterXml = Get-WordHeaderFooterDocument -HeaderFooter $Document.Footer.DefaultFooter -IsFooter Write-PScriboMessage -Message ($localized.ProcessingDocumentPart -f $defaultFooterUri) $footerPart = $Package.CreatePart($defaultFooterUri, 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml') $streamWriter = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($footerPart.GetStream([System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite)) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) Write-PScriboMessage -Message ($localized.WritingDocumentPart -f $defaultFooterUri) $defaultFooterXml.Save($xmlWriter) $xmlWriter.Dispose() $streamWriter.Close() } } } function Out-WordImage { <# .SYNOPSIS Output Image to Word. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Image, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlnsMain = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $xmlnswpDrawingWordProcessing = 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing' $xmlnsDrawingMain = 'http://schemas.openxmlformats.org/drawingml/2006/main' $xmlnsDrawingPicture = 'http://schemas.openxmlformats.org/drawingml/2006/picture' $xmlnsRelationships = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' $p = $XmlDocument.CreateElement('w', 'p', $xmlnsMain) $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlnsMain)) $spacing = $pPr.AppendChild($XmlDocument.CreateElement('w', 'spacing', $xmlnsMain)) [ref] $null = $spacing.SetAttribute('before', $xmlnsMain, 0) [ref] $null = $spacing.SetAttribute('after', $xmlnsMain, 0) $jc = $pPr.AppendChild($XmlDocument.CreateElement('w', 'jc', $xmlnsMain)) [ref] $null = $jc.SetAttribute('val', $xmlnsMain, $Image.Align.ToLower()) $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlnsMain)) [ref] $null = $r.AppendChild($XmlDocument.CreateElement('w', 'rPr', $xmlnsMain)) $drawing = $r.AppendChild($XmlDocument.CreateElement('w', 'drawing', $xmlnsMain)) $inline = $drawing.AppendChild($XmlDocument.CreateElement('wp', 'inline', $xmlnswpDrawingWordProcessing)) [ref] $null = $inline.SetAttribute('distT', '0') [ref] $null = $inline.SetAttribute('distB', '0') [ref] $null = $inline.SetAttribute('distL', '0') [ref] $null = $inline.SetAttribute('distR', '0') $extent = $inline.AppendChild($XmlDocument.CreateElement('wp', 'extent', $xmlnswpDrawingWordProcessing)) [ref] $null = $extent.SetAttribute('cx', $Image.WidthEm) [ref] $null = $extent.SetAttribute('cy', $Image.HeightEm) $effectExtent = $inline.AppendChild($XmlDocument.CreateElement('wp', 'effectExtent', $xmlnswpDrawingWordProcessing)) [ref] $null = $effectExtent.SetAttribute('l', '0') [ref] $null = $effectExtent.SetAttribute('t', '0') [ref] $null = $effectExtent.SetAttribute('r', '0') [ref] $null = $effectExtent.SetAttribute('b', '0') $docPr = $inline.AppendChild($XmlDocument.CreateElement('wp', 'docPr', $xmlnswpDrawingWordProcessing)) [ref] $null = $docPr.SetAttribute('id', $Image.ImageNumber) [ref] $null = $docPr.SetAttribute('name', $Image.Name) [ref] $null = $docPr.SetAttribute('descr', $Image.Name) $cNvGraphicFramePr = $inline.AppendChild($XmlDocument.CreateElement('wp', 'cNvGraphicFramePr', $xmlnswpDrawingWordProcessing)) $graphicFrameLocks = $cNvGraphicFramePr.AppendChild($XmlDocument.CreateElement('a', 'graphicFrameLocks', $xmlnsDrawingMain)) [ref] $null = $graphicFrameLocks.SetAttribute('noChangeAspect', '1') $graphic = $inline.AppendChild($XmlDocument.CreateElement('a', 'graphic', $xmlnsDrawingMain)) $graphicData = $graphic.AppendChild($XmlDocument.CreateElement('a', 'graphicData', $xmlnsDrawingMain)) [ref] $null = $graphicData.SetAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/picture') $pic = $graphicData.AppendChild($XmlDocument.CreateElement('pic', 'pic', $xmlnsDrawingPicture)) $nvPicPr = $pic.AppendChild($XmlDocument.CreateElement('pic', 'nvPicPr', $xmlnsDrawingPicture)) $cNvPr = $nvPicPr.AppendChild($XmlDocument.CreateElement('pic', 'cNvPr', $xmlnsDrawingPicture)) [ref] $null = $cNvPr.SetAttribute('id', $Image.ImageNumber) [ref] $null = $cNvPr.SetAttribute('name', $Image.Name) [ref] $null = $cNvPr.SetAttribute('descr', $Image.Name) $cNvPicPr = $nvPicPr.AppendChild($XmlDocument.CreateElement('pic', 'cNvPicPr', $xmlnsDrawingPicture)) $picLocks = $cNvPicPr.AppendChild($XmlDocument.CreateElement('a', 'picLocks', $xmlnsDrawingMain)) [ref] $null = $picLocks.SetAttribute('noChangeAspect', '1') [ref] $null = $picLocks.SetAttribute('noChangeArrowheads', '1') $blipFill = $pic.AppendChild($XmlDocument.CreateElement('pic', 'blipFill', $xmlnsDrawingPicture)) $blip = $blipFill.AppendChild($XmlDocument.CreateElement('a', 'blip', $xmlnsDrawingMain)) [ref] $null = $blip.SetAttribute('embed', $xmlnsRelationships, $Image.Name) [ref] $null = $blip.SetAttribute('cstate', 'print') $extlst = $blip.AppendChild($XmlDocument.CreateElement('a', 'extlst', $xmlnsDrawingMain)) $ext = $extlst.AppendChild($XmlDocument.CreateElement('a', 'ext', $xmlnsDrawingMain)) [ref] $null = $ext.SetAttribute('uri', '') [ref] $null = $blipFill.AppendChild($XmlDocument.CreateElement('a', 'srcRect', $xmlnsDrawingMain)) $stretch = $blipFill.AppendChild($XmlDocument.CreateElement('a', 'stretch', $xmlnsDrawingMain)) [ref] $null = $stretch.AppendChild($XmlDocument.CreateElement('a', 'fillRect', $xmlnsDrawingMain)) $spPr = $pic.AppendChild($XmlDocument.CreateElement('pic', 'spPr', $xmlnsDrawingPicture)) [ref] $null = $spPr.SetAttribute('bwMode', 'auto') $xfrm = $spPr.AppendChild($XmlDocument.CreateElement('a', 'xfrm', $xmlnsDrawingMain)) $off = $xfrm.AppendChild($XmlDocument.CreateElement('a', 'off', $xmlnsDrawingMain)) [ref] $null = $off.SetAttribute('x', '0') [ref] $null = $off.SetAttribute('y', '0') $ext = $xfrm.AppendChild($XmlDocument.CreateElement('a', 'ext', $xmlnsDrawingMain)) [ref] $null = $ext.SetAttribute('cx', $Image.WidthEm) [ref] $null = $ext.SetAttribute('cy', $Image.HeightEm) $prstGeom = $spPr.AppendChild($XmlDocument.CreateElement('a', 'prstGeom', $xmlnsDrawingMain)) [ref] $null = $prstGeom.SetAttribute('prst', 'rect') [ref] $null = $prstGeom.AppendChild($XmlDocument.CreateElement('a', 'avLst', $xmlnsDrawingMain)) [ref] $null = $spPr.AppendChild($XmlDocument.CreateElement('a', 'noFill', $xmlnsDrawingMain)) $ln = $spPr.AppendChild($XmlDocument.CreateElement('a', 'ln', $xmlnsDrawingMain)) [ref] $null = $ln.AppendChild($XmlDocument.CreateElement('a', 'noFill', $xmlnsDrawingMain)) return $p } } function Out-WordLineBreak { <# .SYNOPSIS Output formatted Word line break. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','LineBreak')] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $LineBreak, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $p = $XmlDocument.CreateElement('w', 'p', $xmlns) $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) $pBdr = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pBdr', $xmlns)) $bottom = $pBdr.AppendChild($XmlDocument.CreateElement('w', 'bottom', $xmlns)) [ref] $null = $bottom.SetAttribute('val', $xmlns, 'single') [ref] $null = $bottom.SetAttribute('sz', $xmlns, 6) [ref] $null = $bottom.SetAttribute('space', $xmlns, 1) [ref] $null = $bottom.SetAttribute('color', $xmlns, 'auto') return $p } } function Out-WordList { <# .SYNOPSIS Output formatted Word list. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $List, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Xml.XmlElement] $Element, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Xml.XmlDocument] $XmlDocument, [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $NumberId, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NotRoot ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' foreach ($item in $List.Items) { if ($item.Type -eq 'PScribo.Item') { $p = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) if ($List.HasStyle) { $pStyle = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pStyle', $xmlns)) [ref] $null = $pStyle.SetAttribute('val', $xmlns, $List.Style) } $numPr = $pPr.AppendChild($XmlDocument.CreateElement('w', 'numPr', $xmlns)) $ilvl = $numPr.AppendChild($XmlDocument.CreateElement('w', 'ilvl', $xmlns)) [ref] $null = $ilvl.SetAttribute('val', $xmlns, $item.Level -1) $numId = $numPr.AppendChild($XmlDocument.CreateElement('w', 'numId', $xmlns)) [ref] $null = $numId.SetAttribute('val', $xmlns, $NumberId) $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $rPr = Get-WordParagraphRunPr -ParagraphRun $item -XmlDocument $XmlDocument [ref] $null = $r.AppendChild($rPr) $t = $r.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t.AppendChild($XmlDocument.CreateTextNode($item.Text)) } elseif ($item.Type -eq 'PScribo.List') { Out-WordList -List $item -Element $Element -XmlDocument $XmlDocument -NumberId $NumberId -NotRoot } } ## Append a blank line after each list if (-not $NotRoot) { $p = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) } } } function Out-WordPageBreak { <# .SYNOPSIS Output formatted Word page break. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','PageBreak')] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $PageBreak, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $p = $XmlDocument.CreateElement('w', 'p', $xmlns) $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $br = $r.AppendChild($XmlDocument.CreateElement('w', 'br', $xmlns)) [ref] $null = $br.SetAttribute('type', $xmlns, 'page') return $p } } function Out-WordParagraph { <# .SYNOPSIS Output formatted Word paragraph and run(s). #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Paragraph, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $p = $XmlDocument.CreateElement('w', 'p', $xmlns); $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)); if ($Paragraph.Tabs -gt 0) { $ind = $pPr.AppendChild($XmlDocument.CreateElement('w', 'ind', $xmlns)) [ref] $null = $ind.SetAttribute('left', $xmlns, (720 * $Paragraph.Tabs)) } if (-not [System.String]::IsNullOrEmpty($Paragraph.Style)) { $pStyle = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pStyle', $xmlns)) [ref] $null = $pStyle.SetAttribute('val', $xmlns, $Paragraph.Style) } $spacing = $pPr.AppendChild($XmlDocument.CreateElement('w', 'spacing', $xmlns)) [ref] $null = $spacing.SetAttribute('before', $xmlns, 0) [ref] $null = $spacing.SetAttribute('after', $xmlns, 0) if ($Paragraph.IsSectionBreakEnd) { $paragraphPrParams = @{ PageHeight = $Document.Options['PageHeight'] PageWidth = $Document.Options['PageWidth'] PageMarginTop = $Document.Options['MarginTop']; PageMarginBottom = $Document.Options['MarginBottom']; PageMarginLeft = $Document.Options['MarginLeft']; PageMarginRight = $Document.Options['MarginRight']; Orientation = $Paragraph.Orientation; } [ref] $null = $pPr.AppendChild((Get-WordSectionPr @paragraphPrParams -XmlDocument $xmlDocument)) } foreach ($paragraphRun in $Paragraph.Sections) { $rPr = Get-WordParagraphRunPr -ParagraphRun $paragraphRun -XmlDocument $XmlDocument $noSpace = ($paragraphRun.IsParagraphRunEnd -eq $true) -or ($paragraphRun.NoSpace -eq $true) $runs = Get-WordParagraphRun -Text $paragraphRun.Text -NoSpace:$noSpace foreach ($run in $runs) { if (-not [System.String]::IsNullOrEmpty($run)) { if ($run -imatch '<!#(TOTALPAGES|PAGENUMBER)#!>') { $r1 = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar1 = $r1.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar1.SetAttribute('fldCharType', $xmlns, 'begin') $r2 = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) [ref] $null = $r2.AppendChild($rPr) $instrText = $r2.AppendChild($XmlDocument.CreateElement('w', 'instrText', $xmlns)) [ref] $null = $instrText.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') if ($run -match '<!#PAGENUMBER#!>') { [ref] $null = $instrText.AppendChild($XmlDocument.CreateTextNode(' PAGE \* MERGEFORMAT ')) } elseif ($run -match '<!#TOTALPAGES#!>') { [ref] $null = $instrText.AppendChild($XmlDocument.CreateTextNode(' NUMPAGES \* MERGEFORMAT ')) } $r3 = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar2 = $r3.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar2.SetAttribute('fldCharType', $xmlns, 'separate') $r4 = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) [ref] $null = $r4.AppendChild($rPr) $t2 = $r4.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t2.AppendChild($XmlDocument.CreateTextNode('1')) $r5 = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar3 = $r5.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar3.SetAttribute('fldCharType', $xmlns, 'end') } else { $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) [ref] $null = $r.AppendChild($rPr) ## Create a separate text block for each line/break $lines = $run -split '\r\n?|\n' for ($l = 0; $l -lt $lines.Count; $l++) { $line = $lines[$l] $t = $r.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) if ($line -ne $line.Trim()) { ## Only preserve space if there is a preceeding or trailing space [ref] $null = $t.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') } [ref] $null = $t.AppendChild($XmlDocument.CreateTextNode($line)) if ($l -lt ($lines.Count - 1)) { ## Don't add a line break to the last line/break [ref] $null = $r.AppendChild($XmlDocument.CreateElement('w', 'br', $xmlns)) } } } } } } return $p } } function Out-WordSection { <# .SYNOPSIS Output formatted Word section (paragraph). #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Section, [Parameter(Mandatory)] [System.Xml.XmlElement] $Element, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $p = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)); $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)); if (-not [System.String]::IsNullOrEmpty($Section.Style)) { $pStyle = $pPr.AppendChild($XmlDocument.CreateElement('w', 'pStyle', $xmlns)) [ref] $null = $pStyle.SetAttribute('val', $xmlns, $Section.Style) } if ($Section.Tabs -gt 0) { $ind = $pPr.AppendChild($XmlDocument.CreateElement('w', 'ind', $xmlns)); [ref] $null = $ind.SetAttribute('left', $xmlns, (720 * $Section.Tabs)); } $spacing = $pPr.AppendChild($XmlDocument.CreateElement('w', 'spacing', $xmlns)); ## Increment heading spacing by 2pt for each section level, starting at 8pt for level 0, 10pt for level 1 etc $spacingPt = (($Section.Level * 2) + 8) * 20 [ref] $null = $spacing.SetAttribute('before', $xmlns, $spacingPt) [ref] $null = $spacing.SetAttribute('after', $xmlns, $spacingPt) if ($Section.IsSectionBreakEnd) { $sectionPrParams = @{ PageHeight = $Document.Options['PageHeight'] PageWidth = $Document.Options['PageWidth'] PageMarginTop = $Document.Options['MarginTop'] PageMarginBottom = $Document.Options['MarginBottom'] PageMarginLeft = $Document.Options['MarginLeft'] PageMarginRight = $Document.Options['MarginRight'] Orientation = $Section.Orientation; } [ref] $null = $pPr.AppendChild((Get-WordSectionPr @sectionPrParams -XmlDocument $xmlDocument)) } $r = $p.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $t = $r.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) if ($Document.Options['EnableSectionNumbering']) { [System.String] $sectionName = '{0} {1}' -f $Section.Number, $Section.Name } else { [System.String] $sectionName = '{0}' -f $Section.Name } [ref] $null = $t.AppendChild($XmlDocument.CreateTextNode($sectionName)) foreach ($subSection in $Section.Sections.GetEnumerator()) { $currentIndentationLevel = 1 if ($null -ne $subSection.PSObject.Properties['Level']) { $currentIndentationLevel = $subSection.Level + 1 } Write-PScriboProcessSectionId -SectionId $subSection.Id -SectionType $subSection.Type -Indent $currentIndentationLevel switch ($subSection.Type) { 'PScribo.Section' { Out-WordSection -Section $subSection -Element $Element -XmlDocument $XmlDocument } 'PScribo.Paragraph' { [ref] $null = $Element.AppendChild((Out-WordParagraph -Paragraph $subSection -XmlDocument $XmlDocument)) } 'PScribo.PageBreak' { [ref] $null = $Element.AppendChild((Out-WordPageBreak -PageBreak $subSection -XmlDocument $XmlDocument)) } 'PScribo.LineBreak' { [ref] $null = $Element.AppendChild((Out-WordLineBreak -LineBreak $subSection -XmlDocument $XmlDocument)) } 'PScribo.Table' { Out-WordTable -Table $subSection -XmlDocument $XmlDocument -Element $Element } 'PScribo.BlankLine' { Out-WordBlankLine -BlankLine $subSection -XmlDocument $XmlDocument -Element $Element } 'PScribo.Image' { [ref] $null = $Element.AppendChild((Out-WordImage -Image $subSection -XmlDocument $XmlDocument)) } 'PScribo.ListReference' { Out-WordList -List $Document.Lists[$subSection.Number -1] -Element $Element -XmlDocument $xmlDocument -NumberId $subSection.Number } Default { Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning } } } } } function Out-WordTable { <# .SYNOPSIS Output one (or more listed) formatted Word tables. .NOTES Specifies that the current row should be repeated at the top each new page on which the table is displayed. E.g, <w:tblHeader />. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Table, ## Root element to append the table(s) to. List view will create multiple tables [Parameter(Mandatory)] [ValidateNotNull()] [System.Xml.XmlElement] $Element, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) begin { $formattedTables = @(ConvertTo-PScriboPreformattedTable -Table $Table) } process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style for ($tableNumber = 0; $tableNumber -lt $formattedTables.Count; $tableNumber++) { $formattedTable = $formattedTables[$tableNumber] if ($Table.HasCaption -and ($tableStyle.CaptionLocation -eq 'Above')) { $tableCaption = (Get-WordTableCaption -Table $Table -XmlDocument $XmlDocument) [ref] $null = $Element.AppendChild($tableCaption) } $tbl = $Element.AppendChild($XmlDocument.CreateElement('w', 'tbl', $xmlns)) [ref] $null = $tbl.AppendChild((Get-WordTablePr -Table $Table -XmlDocument $XmlDocument)) ## LibreOffice requires column widths to be specified so we must calculate rough approximations (#99). $getWordTableRenderWidthMmParams = @{ TableWidth = $Table.Width Tabs = $Table.Tabs Orientation = $Table.Orientation } $tableRenderWidthMm = Get-WordTableRenderWidthMm @getWordTableRenderWidthMmParams $tableRenderWidthTwips = ConvertTo-Twips -Millimeter $tableRenderWidthMm $tblGrid = $tbl.AppendChild($XmlDocument.CreateElement('w', 'tblGrid', $xmlns)) $tableColumnCount = $Table.Columns.Count if ($Table.IsList -and (-not $Table.IsKeyedList)) { $tableColumnCount = 2 } for ($i = 0; $i -lt $tableColumnCount; $i++) { $gridCol = $tblGrid.AppendChild($XmlDocument.CreateElement('w', 'gridCol', $xmlns)) $gridColPct = (100 / $Table.Columns.Count) -as [System.Int32] if (($null -ne $Table.ColumnWidths) -and ($null -ne $Table.ColumnWidths[$i])) { $gridColPct = $Table.ColumnWidths[$i] } $gridColWidthTwips = (($tableRenderWidthTwips/100) * $gridColPct) -as [System.Int32] [ref] $null = $gridCol.SetAttribute('w', $xmlns, $gridColWidthTwips) } for ($r = 0; $r -lt $formattedTable.Rows.Count; $r++) { $row = $formattedTable.Rows[$r] $isRowStyleInherited = $row.IsStyleInherited $rowStyle = $null if (-not $isRowStyleInherited) { $rowStyle = Get-PScriboDocumentStyle -Style $row.Style } $tr = $tbl.AppendChild($XmlDocument.CreateElement('w', 'tr', $xmlns)) if (($r -eq 0) -and ($formattedTable.HasHeaderRow)) { $trPr = $tr.AppendChild($XmlDocument.CreateElement('w', 'trPr', $xmlns)) [ref] $null = $trPr.AppendChild($XmlDocument.CreateElement('w', 'tblHeader', $xmlns)) $cnfStyle = $trPr.AppendChild($XmlDocument.CreateElement('w', 'cnfStyle', $xmlns)) # [ref] $null = $cnfStyle.SetAttribute('val', $xmlns, '100000000000') [ref] $null = $cnfStyle.SetAttribute('firstRow', $xmlns, '1') } for ($c = 0; $c -lt $row.Cells.Count; $c++) { $cell = $row.Cells[$c] $isCellStyleInherited = $cell.IsStyleInherited $cellStyle = $null if (-not $isCellStyleInherited) { $cellStyle = Get-PScriboDocumentStyle -Style $cell.Style } $tc = $tr.AppendChild($XmlDocument.CreateElement('w', 'tc', $xmlns)) $tcPr = $tc.AppendChild($XmlDocument.CreateElement('w', 'tcPr', $xmlns)) if ((-not $isCellStyleInherited) -and (-not [System.String]::IsNullOrEmpty($cellStyle.BackgroundColor))) { $shd = $tcPr.AppendChild($XmlDocument.CreateElement('w', 'shd', $xmlns)) [ref] $null = $shd.SetAttribute('val', $xmlns, 'clear') [ref] $null = $shd.SetAttribute('color', $xmlns, 'auto') $backgroundColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $cellStyle.BackgroundColor) [ref] $null = $shd.SetAttribute('fill', $xmlns, $backgroundColor) } elseif ((-not $isRowStyleInherited) -and (-not [System.String]::IsNullOrEmpty($rowStyle.BackgroundColor))) { $shd = $tcPr.AppendChild($XmlDocument.CreateElement('w', 'shd', $xmlns)) [ref] $null = $shd.SetAttribute('val', $xmlns, 'clear') [ref] $null = $shd.SetAttribute('color', $xmlns, 'auto') $backgroundColor = ConvertTo-WordColor -Color (Resolve-PScriboStyleColor -Color $rowStyle.BackgroundColor) [ref] $null = $shd.SetAttribute('fill', $xmlns, $backgroundColor) } if (($Table.IsList) -and ($c -eq 0) -and ($r -ne 0)) { $cnfStyle = $tcPr.AppendChild($XmlDocument.CreateElement('w', 'cnfStyle', $xmlns)) [ref] $null = $cnfStyle.SetAttribute('firstColumn', $xmlns, '1') } $tcW = $tcPr.AppendChild($XmlDocument.CreateElement('w', 'tcW', $xmlns)) if (($null -ne $Table.ColumnWidths) -and ($null -ne $Table.ColumnWidths[$c])) { [ref] $null = $tcW.SetAttribute('w', $xmlns, ($Table.ColumnWidths[$c] * 50)) [ref] $null = $tcW.SetAttribute('type', $xmlns, 'pct') } else { [ref] $null = $tcW.SetAttribute('w', $xmlns, 0) [ref] $null = $tcW.SetAttribute('type', $xmlns, 'auto') } ## Scaffold paragraph and paragraph run for cell content $newPScriboParagraphParams = @{ NoIncrementCounter = $true } if (-not $isCellStyleInherited) { $newPScriboParagraphParams['Style'] = $cellStyle.Id } elseif (-not $isRowStyleInherited) { $newPScriboParagraphParams['Style'] = $rowStyle.Id } $paragraph = New-PScriboParagraph @newPScriboParagraphParams if (-not [System.String]::IsNullOrEmpty($cell.Content)) { $paragraphRun = New-PScriboParagraphRun -Text $cell.Content } else { $paragraphRun = New-PScriboParagraphRun -Text '' } $paragraphRun.IsParagraphRunEnd = $true [ref] $null = $paragraph.Sections.Add($paragraphRun) $p = Out-WordParagraph -Paragraph $paragraph -XmlDocument $XmlDocument [ref] $null = $tc.AppendChild($p) } } if ($Table.HasCaption -and ($tableStyle.CaptionLocation -eq 'Below')) { $tableCaption = Get-WordTableCaption -Table $Table -XmlDocument $XmlDocument [ref] $null = $Element.AppendChild($tableCaption) } ## Output empty line after (each) table $p = $Element.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) ## Only apply section break to the last table if (($tableNumber -eq ($formattedTables.Count -1)) -and ($Table.IsSectionBreakEnd)) #if ($Table.IsSectionBreakEnd) { $pPr = $p.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)); $spacing = $pPr.AppendChild($XmlDocument.CreateElement('w', 'spacing', $xmlns)) [ref] $null = $spacing.SetAttribute('before', $xmlns, 0) [ref] $null = $spacing.SetAttribute('after', $xmlns, 0) $paragraphPrParams = @{ PageHeight = $Document.Options['PageHeight'] PageWidth = $Document.Options['PageWidth'] PageMarginTop = $Document.Options['MarginTop'] PageMarginBottom = $Document.Options['MarginBottom'] PageMarginLeft = $Document.Options['MarginLeft'] PageMarginRight = $Document.Options['MarginRight'] Orientation = $Table.Orientation } $sectPr = Get-WordSectionPr @paragraphPrParams -XmlDocument $xmlDocument [ref] $null = $pPr.AppendChild($sectPr) } } } } function Out-WordTOC { <# .SYNOPSIS Output formatted Word table of contents. #> [CmdletBinding()] [OutputType([System.Xml.XmlElement])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $TOC, [Parameter(Mandatory)] [System.Xml.XmlDocument] $XmlDocument ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $sdt = $XmlDocument.CreateElement('w', 'sdt', $xmlns) $sdtPr = $sdt.AppendChild($XmlDocument.CreateElement('w', 'sdtPr', $xmlns)) $docPartObj = $sdtPr.AppendChild($XmlDocument.CreateElement('w', 'docPartObj', $xmlns)) $docObjectGallery = $docPartObj.AppendChild($XmlDocument.CreateElement('w', 'docPartGallery', $xmlns)) [ref] $null = $docObjectGallery.SetAttribute('val', $xmlns, 'Table of Contents') [ref] $null = $docPartObj.AppendChild($XmlDocument.CreateElement('w', 'docPartUnique', $xmlns)) [ref] $null = $sdt.AppendChild($XmlDocument.CreateElement('w', 'stdEndPr', $xmlns)) $sdtContent = $sdt.AppendChild($XmlDocument.CreateElement('w', 'stdContent', $xmlns)) $p1 = $sdtContent.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) $pPr1 = $p1.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) $pStyle1 = $pPr1.AppendChild($XmlDocument.CreateElement('w', 'pStyle', $xmlns)) [ref] $null = $pStyle1.SetAttribute('val', $xmlns, 'TOC') $r1 = $p1.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $t1 = $r1.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t1.AppendChild($XmlDocument.CreateTextNode($TOC.Name)) $p2 = $sdtContent.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) $pPr2 = $p2.AppendChild($XmlDocument.CreateElement('w', 'pPr', $xmlns)) $tabs2 = $pPr2.AppendChild($XmlDocument.CreateElement('w', 'tabs', $xmlns)) $tab2 = $tabs2.AppendChild($XmlDocument.CreateElement('w', 'tab', $xmlns)) [ref] $null = $tab2.SetAttribute('val', $xmlns, 'right') [ref] $null = $tab2.SetAttribute('leader', $xmlns, 'dot') [ref] $null = $tab2.SetAttribute('pos', $xmlns, '9016') $r2 = $p2.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar1 = $r2.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar1.SetAttribute('fldCharType', $xmlns, 'begin') $r3 = $p2.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $instrText = $r3.AppendChild($XmlDocument.CreateElement('w', 'instrText', $xmlns)) [ref] $null = $instrText.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') [ref] $null = $instrText.AppendChild($XmlDocument.CreateTextNode(' TOC \h \z \u ')) $r4 = $p2.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar2 = $r4.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar2.SetAttribute('fldCharType', $xmlns, 'separate') $p3 = $sdtContent.AppendChild($XmlDocument.CreateElement('w', 'p', $xmlns)) $r5 = $p3.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar3 = $r5.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar3.SetAttribute('fldCharType', $xmlns, 'end') return $sdt } } # SIG # Begin signature block # MIIuugYJKoZIhvcNAQcCoIIuqzCCLqcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCrjJx3nTlsYwQ/ # BQqqedLto4ONehocVm2MA0lrsJWsKKCCE6QwggWQMIIDeKADAgECAhAFmxtXno4h # MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV # BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z # ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB # AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z # G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ # anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s # Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL # 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb # BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 # JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c # AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx # YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 # viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL # T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud # EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf # Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk # aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS # PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK # 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB # cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp # 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg # dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri # RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 # 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 # nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 # i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H # EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G # CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C # 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce # 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da # E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T # SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA # FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh # D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM # 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z # 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 # huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY # mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP # /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T # AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD # VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG # A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV # HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN # BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry # sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL # IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf # Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh # OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh # dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV # 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j # wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH # Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC # XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l # /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW # eE4wggdYMIIFQKADAgECAhAIfHT3o/FeY5ksO94AUhTmMA0GCSqGSIb3DQEBCwUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwHhcNMjMxMDE4MDAwMDAwWhcNMjYxMjE2MjM1OTU5WjBgMQsw # CQYDVQQGEwJHQjEPMA0GA1UEBxMGTG9uZG9uMR8wHQYDVQQKExZWaXJ0dWFsIEVu # Z2luZSBMaW1pdGVkMR8wHQYDVQQDExZWaXJ0dWFsIEVuZ2luZSBMaW1pdGVkMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtyhrsCMi6pgLcX5sWY7I09dO # WKweRHfDwW5AN6ffgLCYO9dqWWxvqu95FqnNVRyt1VNzEl3TevKVhRE0GGdirei3 # VqnFFjLDwD2jHhGY8qoSYyfffj/WYq2DkvNI62C3gUwSeP3FeqKRalc2c3V2v4jh # yEYhrgG3nfnWQ/Oq2xzuiCqHy1E4U+IKKDtrXls4JX2Z4J/uAHZIAyKfrcTRQOhZ # R4ZS1cQkeSBU9Urx578rOmxL0si0GAoaYQC49W7OimRelbahxZw/R+f5ch+C1ycU # CpeXLg+bFhpa0+EXnkGidlILZbJiZJn7qvMQTZgipQKZ8nhX3rtJLqTeodPWzcCk # tXQUy0q5fxhR3e6Ls7XQesq/G2yMcCMTCd6eltze0OgabvL6Xkhir5lATHaJtnmw # FlcKzRr1YXK1k1D84hWfKSAdUg8T1O72ztimbqFLg6WoC8M2qqsHcm2DOc9hM3i2 # CWRpegikitRvZ9i1wkA9arGh7+a7UD+nLh2hnGmO06wONLNqABOEn4JOVnNrQ1gY # eDeH9FDx7IYuAvMsfXG9Bo+I97TR2VfwDAx+ccR+UQLON3aQyFZ3BefYnvUu0gUR # ikEAnAS4Jnc3BHizgb0voz0iWRDjFoTTmCmrInCVDGc+5KMy0xyoUwdQvYvRGAWB # 61OCWnXBXbAEPniTZ80CAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hf # EYb7/mF7CIhl9E5CMB0GA1UdDgQWBBRuAv58K4EDYLmb7WNcxt5+r4NfnzA+BgNV # HSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2lj # ZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD # MIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEu # Y3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYB # BQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv # bTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQw # CQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnXMg6efkBrwLIvd1Xmuh0dam # 9FhUtDEj+P5SIqdP/U4veOv66NEQhBHLbW2Dvrdm6ec0HMj9b4e8pt4ylKFzHIPj # fpuRffHVR9JQSx8qpryN6pP49DfCkAYeZGqjY3pGRzd/xQ0cfwcuYbUF+vwVk7tj # q8c93VHCM0rb5M4N2hD1Ze1pvZxtaf9QnFKFzgXZjr02K6bswQc2+n5jFCp7zV1f # KTstyb68rhSJBWKK1tMeFk6a6HXr5buTD3skluC0oyPmD7yAd97r2owjDMEveEso # kADP/z7XQk7wqbwbpi4W6Uju2qHK/9UUsVRF5KTVEAIzVw2V1Aq/Jh3JuSV7b7C1 # 4CghNekltBb+w7YVp8/IFcj7axqnpNQ/+f7RVc3A5hyjV+MkoSwn8Sg7a7hn6SzX # jec/TfRVvWCmG94MQHko+6206uIXrZnmQ6UQYFyOHRlyKDEozzkZhIcVlsZloUjL # 3FZ5V/l8TIIzbc3bkEnu4iByksNvRxI6c5264OLauYlWv50ZUPwXmZ9gX8bs3BqZ # avbGUrOW2PIjtWhtvs4zHhMBCoU1Z0OMvXcF9rUDqefmVCZK46xz3DGKVkDQnxY6 # UWQ3GL60/lEzju4YS99LJVQks2UGmP6LAzlEZ1dnGqi1aQ51OidCYEs39B75PsvO # By2iAR8pBVi/byWBypExghpsMIIaaAIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYD # VQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBH # NCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAh8dPej8V5j # mSw73gBSFOYwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA # oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgfJZdjqiM3sSBkB4rC8EsQ104 # Is2IRt2dBu+BcdXYwPkwDQYJKoZIhvcNAQEBBQAEggIAmls7DXwRtBZYvT2lEo37 # iVuMhbUo31l7VrSVXrupKUqfQPLgGlDq9XmOmZdl11+gn/F40r7ZiNPZ6g0g+oXk # 0gLApU/JwaTEtHfVk1MFDPyG1Wl8+aZD+RHpD1eP8nJrY/lX9OWJZO2E5vVar1nj # ThKqAT3qrNonUj5/mvEduIn1aO3zwfkS7r/AygqT3amSJNH82L6Qh7dcO38vITe6 # +k+kTijYEX1ow4MCyjI4icDbTZBwU+H1cRXUUv2YWjM0TQsV6deCou+xndBAxMVJ # 0iAxC7+GXXujzmKRpxCH1VBRpvCaSNEgF6ms5BjaCHrXs8ND4p8joVIIN4EpGfsQ # cBGtQ41nYcghxQNRHRH+ynb7g521tqgNEvT75ihpE9/ra+tM0ccQaQ6NzBEoeR7N # +RjTFvZP5OHyFpGauLmuh5QRp27R/hEaIt2XEgyW02lDyRS6guM9o5DjfKgBP3nR # bFYnoXugQVujCVcT2xbVoOuPMwnHZIPHmTMtU1ZK57i6QcrxX90+xzQFDxiBSDPp # JFSxN/U71DoQ9xdyBe6HoX5NPctfU6MP0cwk5ALkdxEhRcacabX6JWtVA0Hw36y4 # tgGE38Wg++o8k6UnPjAdgiddViAHw9NnMdKO7LhRzA7N2c9d5u3JLh1j8dP5DRUc # 8kJcXEjb3o4el+qDIu3dn8+hghc5MIIXNQYKKwYBBAGCNwMDATGCFyUwghchBgkq # hkiG9w0BBwKgghcSMIIXDgIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJ # EAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgJFMLk4BJ # /ywYt+/j7+qyze0RrDo0wQRwKsVJxby3YJACEELqsMtKmag/NbKRcyEHT64YDzIw # MjUwMzA3MDg1NTA4WqCCEwMwgga8MIIEpKADAgECAhALrma8Wrp/lYfG+ekE4zME # MA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy # dCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNI # QTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjQwOTI2MDAwMDAwWhcNMzUxMTI1MjM1 # OTU5WjBCMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxIDAeBgNVBAMT # F0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDI0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A # MIICCgKCAgEAvmpzn/aVIauWMLpbbeZZo7Xo/ZEfGMSIO2qZ46XB/QowIEMSvgjE # dEZ3v4vrrTHleW1JWGErrjOL0J4L0HqVR1czSzvUQ5xF7z4IQmn7dHY7yijvoQ7u # jm0u6yXF2v1CrzZopykD07/9fpAT4BxpT9vJoJqAsP8YuhRvflJ9YeHjes4fduks # THulntq9WelRWY++TFPxzZrbILRYynyEy7rS1lHQKFpXvo2GePfsMRhNf1F41nyE # g5h7iOXv+vjX0K8RhUisfqw3TTLHj1uhS66YX2LZPxS4oaf33rp9HlfqSBePejlY # eEdU740GKQM7SaVSH3TbBL8R6HwX9QVpGnXPlKdE4fBIn5BBFnV+KwPxRNUNK6lY # k2y1WSKour4hJN0SMkoaNV8hyyADiX1xuTxKaXN12HgR+8WulU2d6zhzXomJ2Ple # I9V2yfmfXSPGYanGgxzqI+ShoOGLomMd3mJt92nm7Mheng/TBeSA2z4I78JpwGpT # RHiT7yHqBiV2ngUIyCtd0pZ8zg3S7bk4QC4RrcnKJ3FbjyPAGogmoiZ33c1HG93V # p6lJ415ERcC7bFQMRbxqrMVANiav1k425zYyFMyLNyE1QulQSgDpW9rtvVcIH7Wv # G9sqYup9j8z9J1XqbBZPJ5XLln8mS8wWmdDLnBHXgYly/p1DhoQo5fkCAwEAAaOC # AYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM # MAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAf # BgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4EFgQUn1csA3cO # KBWQZqVjXu5Pkh92oFswWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybDMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFt # cGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUHMAGGGGh0dHA6 # Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDovL2NhY2VydHMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVT # dGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAPa0eH3aZW+M4hBJH2UOR # 9hHbm04IHdEoT8/T3HuBSyZeq3jSi5GXeWP7xCKhVireKCnCs+8GZl2uVYFvQe+p # PTScVJeCZSsMo1JCoZN2mMew/L4tpqVNbSpWO9QGFwfMEy60HofN6V51sMLMXNTL # fhVqs+e8haupWiArSozyAmGH/6oMQAh078qRh6wvJNU6gnh5OruCP1QUAvVSu4kq # VOcJVozZR5RRb/zPd++PGE3qF1P3xWvYViUJLsxtvge/mzA75oBfFZSbdakHJe2B # VDGIGVNVjOp8sNt70+kEoMF+T6tptMUNlehSR7vM+C13v9+9ZOUKzfRUAYSyyEmY # tsnpltD/GWX8eM70ls1V6QG/ZOB6b6Yum1HvIiulqJ1Elesj5TMHq8CWT/xrW7tw # ipXTJ5/i5pkU5E16RSBAdOp12aw8IQhhA/vEbFkEiF2abhuFixUDobZaA0VhqAsM # HOmaT3XThZDNi5U2zHKhUs5uHHdG6BoQau75KiNbh0c+hatSF+02kULkftARjsyE # pHKsF7u5zKRbt5oK5YGwFvgc4pEVUNytmB3BpIiowOIIuDgP5M9WArHYSAR16gc0 # dP2XdkMEP5eBsX7bf/MGN4K3HP50v/01ZHo/Z5lGLvNwQ7XHBx1yomzLP8lx4Q1z # ZKDyHcp4VQJLu2kWTsKsOqQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5b # MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5 # NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG # A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh # bXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPB # PXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/ # nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLc # Z47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mf # XazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3N # Ng1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yem # j052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g # 3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD # 4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDS # LFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwM # O1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU # 7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw # DQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPO # vxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQ # TGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWae # LJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPBy # oyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfB # wWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8l # Y5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/ # O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbb # bxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3 # OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBl # dkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt # 1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwF # ADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL # ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElE # IFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKn # JS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/W # BTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHi # LQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhm # V1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHE # tWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 # MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mX # aXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZ # xd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfh # vbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvl # EFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn1 # 5GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV # HQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4Ix # LVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggr # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdo # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290 # Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAA # MA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzs # hV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre # +i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8v # C6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38 # dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNr # Iv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYIDdjCCA3ICAQEw # dzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNV # BAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1w # aW5nIENBAhALrma8Wrp/lYfG+ekE4zMEMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqG # SIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjUwMzA3MDg1 # NTA4WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTb04XuYtvSPnvk9nFIUIck1YZb # RTAvBgkqhkiG9w0BCQQxIgQgTaS9XXKJ9oC1LPtbrTQIik9IEjWtIfW8mjQXH0y6 # NGYwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgdnafqPJjLx9DCzojMK7WVnX+13Pb # BdZluQWTmEOPmtswDQYJKoZIhvcNAQEBBQAEggIAdYzF4SMBuNj+Ha1uAl/xiyqj # tynNRAMfdM/s57YJYZMlvqM9mqgsj9p7M5klj0N3/F5PigHIfvdE5yU/kjAvgBgt # gOglIcf3P+L10Wq8MJIaexjmnB+B8mKFztySyO/AfkOsUgVv7Bc9esa90hWSRA5Z # XEjUViWZTf9VLMGW5kKXeCwlkUYaOtM7Uf0Z1dcMdje6UPugpLysVY7s3O5WrCym # IrjXkr+570Z54rkt5kH4RCEGlCRIjuwq4sUKQMPMai+37ZZ6jA9gCCE6ZxTCbCPW # CyctncqhaFktkERIm0+4yrEdRJh4ty3AunHGm8kUdzbnwvK8eokswstaqm8ZR9vK # lDauIeDOvti7HkVkJ0Pz1RZvq9CWhrGhuMJATX6BzUt8+7TDdy/vhd7TKidkPB+w # YAlIse2PMsLOmsNkx64PSbVFfJxcU7PEa5uYHc2GMC1IAUKCGEicQYnG6by/eQ3p # Vt3KNzTeX0U3kBSw+yNZTXbR6Jp6h7cyC4Cf6gM/Y/U2PLJzUf58NzvUYW0kTf6S # Cc6v5+Ce6tNaHbnnig65CR+B9qC1zTCu4L7ZA808nHIJNI8B95Y6qOVaTQzriQAr # s4eJ8Kg+60f6vi57jfem1oV/EkFal8iklCQnFFSIkfOFHhGgm1AQiVATwsj9Ehc/ # 1RTcoxohE/MIAFKmGSs= # SIG # End signature block |