Public/Convert-PCMarkdownToWord.ps1
|
function Convert-PCMarkdownToWord { <# .SYNOPSIS Renders a markdown file into a themed Word document. .DESCRIPTION Walks the markdown through the PowerCraft.Word.Core kernel: headings, paragraphs, emphasis, lists, tables, code, quotes, links and images map onto the theme's named styles. Images on their own line become numbered Word captions. Table of contents and list of figures are real fields, placed at <!-- INSERT_TOC_HERE --> and <!-- INSERT_LOF_HERE --> markers when present, otherwise per the placement parameters. An optional title-page document is merged ahead of the body with [Token] substitution. Writes a build report beside the output when -ReportPath is given. Cross-platform. No Word, no COM, no Pandoc. .PARAMETER Path The markdown file. .PARAMETER OutputPath The .docx to write. Defaults to the markdown name with a .docx extension. .PARAMETER Theme A .docx whose styles the renderer uses. Omit for the neutral default theme. See New-PCWordTheme. .PARAMETER TitlePage A .docx merged ahead of the body, with [Token] placeholders substituted from -Tokens. [Title] defaults to the first level-one heading and [Date] to today. .PARAMETER Tokens Hashtable of token values for the title page, for example @{ SUBTITLE = 'Draft for review' }. .PARAMETER Toc Where the table of contents goes when no marker is present: AfterTitle (default), BeforeContent, Marker, None. .PARAMETER ListOfFigures Where the list of figures goes when no marker is present: AfterToc (default), AfterTitle, BeforeContent, Marker, None. .PARAMETER HeadingOffset Added to heading levels. The default -1 maps # to Title and ## to Heading 1, as the legacy pipeline did. .PARAMETER Deterministic Fix document timestamps so repeated builds of the same input are structurally identical. .PARAMETER Force Overwrite an existing output file. .PARAMETER ReportPath Write the build report as JSON to this path. .EXAMPLE Convert-PCMarkdownToWord .\update.md -Theme .\Context\style-reference.docx -TitlePage .\Context\title-page.docx -Tokens @{ SUBTITLE = 'Status and milestones' } -Force #> [CmdletBinding(SupportsShouldProcess)] [OutputType([pscustomobject])] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName', 'MarkdownFile')] [string]$Path, [Parameter(Position = 1)] [string]$OutputPath, [string]$Theme, [string]$TitlePage, [hashtable]$Tokens, [string]$Title, [ValidateSet('AfterTitle', 'BeforeContent', 'Marker', 'None')] [string]$Toc = 'AfterTitle', [ValidateSet('AfterToc', 'AfterTitle', 'BeforeContent', 'Marker', 'None')] [string]$ListOfFigures = 'AfterToc', [string]$TocHeading = 'Table of Contents', [string]$ListOfFiguresHeading = 'List of Figures', [int]$HeadingOffset = -1, [switch]$KeepFirstHeading, [switch]$NoUpdateFieldsOnOpen, [switch]$Deterministic, [switch]$Force, [string]$ReportPath ) process { $markdown = (Resolve-Path -LiteralPath $Path -ErrorAction Stop).Path $request = [PowerCraft.Word.Core.DocumentBuildRequest]::new() $request.MarkdownPath = $markdown if ($OutputPath) { $request.OutputPath = $OutputPath } if ($Theme) { $request.ThemePath = (Resolve-Path -LiteralPath $Theme -ErrorAction Stop).Path } if ($TitlePage) { $request.TitlePagePath = (Resolve-Path -LiteralPath $TitlePage -ErrorAction Stop).Path } if ($Title) { $request.Title = $Title } if ($Tokens) { foreach ($key in $Tokens.Keys) { $request.Tokens[[string]$key] = [string]$Tokens[$key] } } $request.Toc = [PowerCraft.Word.Core.FieldPlacement]$Toc $request.ListOfFigures = [PowerCraft.Word.Core.FieldPlacement]$ListOfFigures $request.TocHeadingText = $TocHeading $request.LofHeadingText = $ListOfFiguresHeading $request.HeadingOffset = $HeadingOffset if ($KeepFirstHeading) { $request.RemoveFirstHeading = $false } $request.UpdateFieldsOnOpen = -not $NoUpdateFieldsOnOpen $request.Deterministic = [bool]$Deterministic $request.Overwrite = [bool]$Force $target = if ($OutputPath) { $OutputPath } else { [IO.Path]::ChangeExtension($markdown, '.docx') } if (-not $PSCmdlet.ShouldProcess($target, 'Render markdown to Word')) { return } $result = [PowerCraft.Word.Core.DocumentBuilder]::Build($request) $report = $result.Report foreach ($warning in $report.Warnings) { Write-Warning $warning } if ($ReportPath) { [IO.File]::WriteAllText((Join-Path (Get-Location) $ReportPath | ForEach-Object { if ([IO.Path]::IsPathRooted($ReportPath)) { $ReportPath } else { $_ } }), $report.ToJson()) } [pscustomobject]@{ OutputPath = $result.OutputPath Title = $report.Title Theme = $report.Theme Headings = $report.Headings Images = $report.Images Captions = $report.Captions Tables = $report.Tables Fields = @($report.Fields) TokensUnresolved = @($report.TokensUnresolved) StylesMissing = @($report.StylesMissing) ValidationErrors = $report.ValidationErrors Warnings = @($report.Warnings) ReportPath = $ReportPath } } } |