PSWriteOffice
June 24, 2026 ยท View on GitHub
PowerShell-first document automation for Word, Excel, PowerPoint, PDF, RTF, Markdown, CSV, Reader, and Visio, built on top of OfficeIMO.*.
๐ฆ PowerShell Gallery
๐ ๏ธ Project
Why PSWriteOffice โจ
PSWriteOffice is the PowerShell layer for OfficeIMO.*.
OfficeIMOowns the document engine and low-level file-format behavior.PSWriteOfficeowns the PowerShell cmdlets, DSL aliases, packaging, examples, and scripting ergonomics.- The goal is simple: make Office document automation feel native in PowerShell without requiring Microsoft Office on the machine.
Start here when you want to create polished documents from PowerShell: workbooks with tables, charts, links, images, pivots, and navigation; Word reports with sections, headers, footers, tables, charts, and conversions; PowerPoint decks with reusable slides, notes, sections, themes, and transitions; PDFs with composition, extraction, forms, stamps, and metadata; RTF documents and bridges; Markdown and CSV workflows; Visio diagrams; and unified readback for existing files.
Platform Areas ๐งญ
| Area | Status | What it covers now |
|---|---|---|
| Word | Mature | Document creation, readers, bookmarks, content controls, fields, HTML conversion, Markdown conversion |
| Excel | Advanced | Sheets, tables, named ranges, formulas, validation, charts, pivots, comments, TOC/navigation, explicit range readers, chart formatting, smarter links, URL images, summary-sheet linking |
| PowerPoint | Experimental but useful | Slides, titles, text boxes, bullets, notes, layouts, placeholders, sections, text replacement, slide import, slide copy, transitions, sizing |
| Advanced | New PDFs, text, headings, images, tables, panels, bookmarks, metadata, attachments, stamps, watermarks, forms, merge/split/copy/move pages, HTML conversion, text/image/attachment extraction, compliance inspection | |
| RTF | Solid | Create/read/update .rtf, replace text, append paragraphs, edit document properties, convert RTF to Word/HTML/PDF, convert Word/HTML to RTF, and read semantic chunks through Reader |
| Markdown | Solid | Read Markdown, inspect full object trees/headings/front matter/tables, build Markdown with a DSL, render HTML, convert HTML |
| CSV | Solid | Read CSV, emit CSV, object-focused data workflows |
| Reader | New | Unified chunk/document/table/visual/asset/ingest readback over Word, Excel, PowerPoint, Markdown, PDF, RTF, HTML, CSV, JSON, XML, YAML, ZIP, EPUB, Visio, and text-like files through OfficeIMO.Reader |
| Visio | New | DSL diagram creation, built-in and package-backed stencils, create/load/save .vsdx, inspect diagrams, generate galleries, export stencil preview galleries, and export SVG/PNG through OfficeIMO.Visio |
Quick Start ๐
Word
New-OfficeWord -Path .\Report.docx {
WordSection {
WordHeader { WordParagraph -Text 'Quarterly Report' -Style Heading2 }
WordFooter { WordPageNumber }
WordParagraph -Text 'Hello from PSWriteOffice.'
WordList -Style Bulleted {
WordListItem -Text 'Alpha'
WordListItem -Text 'Beta'
}
}
}
$chartData = @(
[PSCustomObject]@{ Region = 'NA'; Revenue = 100 }
[PSCustomObject]@{ Region = 'EMEA'; Revenue = 200 }
)
New-OfficeWord -Path .\Charts.docx {
Add-OfficeWordChart -Type Pie -Data $chartData -CategoryProperty Region -SeriesProperty Revenue -Title 'Revenue Mix'
}
Excel
$data = @(
[PSCustomObject]@{ Region = 'NA'; Revenue = 100 }
[PSCustomObject]@{ Region = 'EMEA'; Revenue = 200 }
)
New-OfficeExcel -Path .\Report.xlsx {
ExcelSheet 'Data' {
ExcelTable -Data $data -TableName 'Sales' -AutoFit
ExcelNamedRange -Name 'SalesData' -Range 'A1:B3'
}
ExcelSheet 'Notes' {
ExcelRow -Row 1 -Values 'Label', 'Value'
ExcelRow -Row 2 -Values 'Generated', (Get-Date -Format 'yyyy-MM-dd')
}
ExcelTableOfContents -IncludeNamedRanges
}
PowerPoint
New-OfficePowerPoint -Path .\Deck.pptx {
PptSlide {
PptTitle -Title 'Status Update'
PptTextBox -Text 'Generated with PSWriteOffice' -X 80 -Y 150 -Width 360 -Height 60
PptBullets -Bullets 'Wins','Risks','Next Steps' -X 430 -Y 150 -Width 260 -Height 200
PptNotes -Text 'Keep this under five minutes.'
}
}
$ppt = Get-OfficePowerPoint -FilePath .\Deck.pptx
Add-OfficePowerPointSection -Presentation $ppt -Name 'Intro' -StartSlideIndex 0
Rename-OfficePowerPointSection -Presentation $ppt -Name 'Intro' -NewName 'Opening'
Update-OfficePowerPointText -Presentation $ppt -OldValue 'FY24' -NewValue 'FY25' -IncludeNotes
Copy-OfficePowerPointSlide -Presentation $ppt -Index 0
Get-OfficePowerPointSlide -Presentation $ppt -Index 0 | Set-OfficePowerPointSlideTransition -Transition Fade
Set-OfficePowerPointSlideSize -Presentation $ppt -Preset Screen16x9
Import-OfficePowerPointSlide -Presentation $ppt -SourcePath .\SourceDeck.pptx -SourceIndex 0
PowerPoint theme and layout helpers
$ppt = Get-OfficePowerPoint -FilePath .\Deck.pptx
Set-OfficePowerPointThemeColor -Presentation $ppt -Colors @{ Accent1 = '#C00000'; Accent2 = '#00B0F0' } -AllMasters
Set-OfficePowerPointThemeFonts -Presentation $ppt -MajorLatin 'Aptos' -MinorLatin 'Calibri' -AllMasters
Set-OfficePowerPointThemeName -Presentation $ppt -Name 'Contoso Theme' -AllMasters
Get-OfficePowerPointSlide -Presentation $ppt -Index 0 | Set-OfficePowerPointSlideLayout -LayoutName 'Title and Content'
Get-OfficePowerPointTheme -Presentation $ppt
$rows = @(
[PSCustomObject]@{ Area = 'Word'; Status = 'Ready'; Owner = 'Docs' }
[PSCustomObject]@{ Area = 'PDF'; Status = 'Review'; Owner = 'Adapters' }
)
New-OfficePdf -Path .\Status.pdf {
Add-OfficePdfHeading -Text 'Documentation Status' -Level 1
Add-OfficePdfParagraph -Text 'Generated with PSWriteOffice through OfficeIMO.Pdf.'
Add-OfficePdfTable -InputObject $rows -Property Area,Status,Owner -Header 'Area','Status','Owner' -Align Center
Add-OfficePdfBookmark -Name 'Status table'
Set-OfficePdfMetadata -Title 'Documentation Status' -Author 'PSWriteOffice'
}
Join-OfficePdf -Path .\Cover.pdf, .\Status.pdf -OutputPath .\Report.pdf
Split-OfficePdf -Path .\Report.pdf -OutputDirectory .\Pages
Get-OfficePdfText -Path .\Report.pdf
Excel chart finishing
$chart = Add-OfficeExcelChart -TableName 'Sales' -Row 6 -Column 1 -Type Pie -Title 'Revenue Mix' -PassThru
$chart |
Set-OfficeExcelChartLegend -Position Right |
Set-OfficeExcelChartDataLabels -ShowValue $true -ShowPercent $true -Position OutsideEnd -NumberFormat '0.0%' -SourceLinked:$false |
Set-OfficeExcelChartStyle -StyleId 251 -ColorStyleId 10
Excel links and media
ExcelSheet 'Data' {
Set-OfficeExcelSmartHyperlink -Address 'A2' -Url 'https://datatracker.ietf.org/doc/html/rfc7208'
Set-OfficeExcelHostHyperlink -Address 'B2' -Url 'https://learn.microsoft.com/office/open-xml/'
Add-OfficeExcelImageFromUrl -Address 'D2' -Url 'https://example.org/logo.png' -WidthPixels 48 -HeightPixels 48
}
Excel internal navigation
ExcelSheet 'Summary' {
Set-OfficeExcelInternalLinks -Range 'D2:D10'
Set-OfficeExcelInternalLinksByHeader -Header 'Sheet' -TableName 'SummaryTable' -DisplayScript { param($text) "Open $text" }
}
Excel external link automation
ExcelSheet 'Summary' {
Set-OfficeExcelUrlLinksByHeader -Header 'RFC' -TableName 'LinksTable' -UrlScript { param($text) "https://datatracker.ietf.org/doc/html/$text" } -TitleScript { param($text) "Open $text" }
Set-OfficeExcelUrlLinks -Range 'D2:D10' -UrlScript { param($text) "https://datatracker.ietf.org/doc/html/$text" }
}
RTF
New-OfficeRtf -OutputPath .\Report.rtf -Text 'Summary', 'Ready for review'
Get-OfficeRtf -Path .\Report.rtf
Update-OfficeRtfText -Path .\Report.rtf -OutputPath .\Report-Updated.rtf -OldText 'review' -NewText 'release'
ConvertFrom-OfficeRtf -Path .\Report-Updated.rtf -As Pdf -OutputPath .\Report.pdf
Get-OfficeDocumentChunk -Path .\Report-Updated.rtf
Markdown and CSV
New-OfficeMarkdown -Path .\README.md {
MarkdownHeading -Level 1 -Text 'Report'
MarkdownParagraph -Text 'Generated by PSWriteOffice.'
}
$data | ConvertTo-OfficeCsv -OutputPath .\export.csv
Reader and Visio
Get-OfficeDocumentCapability
Get-OfficeDocumentChunk -Path .\Report.docx
Get-OfficeDocumentChunk -Path .\Report.rtf
Get-OfficeDocument -Path .\Report.pdf -AsJson -Indented
Get-OfficeDocumentAsset -Path .\Deck.pptx -Kind image -OutputDirectory .\reader-assets
New-OfficeVisio -Path .\Diagram.vsdx -Title 'Service map' -RequestRecalcOnOpen {
VisioRectangle -Key web -Text 'Web' -X 1.5 -Y 4 -Width 1.5 -Height 0.8 -FillColor LightBlue -LineColor SteelBlue
VisioDiamond -Key decision -Text 'Ready?' -X 4 -Y 4 -Width 1.2 -Height 1 -FillColor '#FFF2CC' -LineColor '#B45309'
VisioRectangle -Key api -Text 'API' -X 6.2 -Y 4 -Width 1.5 -Height 0.8 -FillColor LightGreen -LineColor SeaGreen
VisioConnector -From web -To decision -Kind RightAngle -EndArrow Triangle -Label 'check'
VisioConnector -From decision -To api -Kind RightAngle -EndArrow Triangle -Label 'ship'
}
Get-OfficeVisioInfo -Path .\Diagram.vsdx -AsText
ConvertTo-OfficeVisioSvg -Path .\Diagram.vsdx -OutputPath .\Diagram.svg
ConvertTo-OfficeVisioPng -Path .\Diagram.vsdx -OutputPath .\Diagram.png
$flow = Get-OfficeVisioStencilCatalog -BuiltIn Flowchart
Find-OfficeVisioStencil -Catalog $flow -Query process -First 5
New-OfficeVisio -Path .\Flow.vsdx -Title 'Stencil flow' -UseMastersByDefault -RequestRecalcOnOpen {
Import-OfficeVisioStencil -BuiltIn Flowchart -Name Flow -Default
VisioStencil -Catalog Flow -Stencil process -Key intake -Text 'Intake' -X 1.5 -Y 4 -FillColor '#E0F2FE' -LineColor '#0369A1'
VisioStencil -Catalog Flow -Stencil decision -Key review -Text 'Review?' -X 4 -Y 4 -FillColor '#FEF3C7' -LineColor '#B45309'
VisioStencil -Catalog Flow -Stencil data -Key archive -Text 'Archive' -X 6.5 -Y 4 -FillColor '#DCFCE7' -LineColor '#15803D'
VisioConnector -From intake -To review -Kind RightAngle -EndArrow Triangle -Label 'submit'
VisioConnector -From review -To archive -Kind RightAngle -EndArrow Triangle -Label 'store'
}
$catalog = Get-OfficeVisioStencilCatalog -Path .\MyShapes.vssx -CatalogName 'Custom Shapes' -IncludeUnsupportedMasters
Find-OfficeVisioStencil -Catalog $catalog -Query server -First 10
New-OfficeVisio -Path .\CustomStencil.vsdx -UseMastersByDefault {
Import-OfficeVisioStencil -Catalog $catalog -Name Custom -Default
VisioStencil -Stencil server -Key server -Text 'Server' -X 2 -Y 4
VisioStencil -Stencil database -Key database -Text 'Database' -X 5 -Y 4
VisioConnector -From server -To database -Kind RightAngle -EndArrow Triangle
}
More visual Visio examples live in Examples\Visio. Run .\Examples\Visio\Build-VisioShowcase.ps1 to generate editable .vsdx files plus SVG/PNG previews for stencil flowcharts, architecture maps, network topology, and package-backed stencil loading.
New-OfficeVisioGallery -OutputDirectory .\VisioGallery |
Select-Object Name, FilePath, IsClean
Export-OfficeVisioStencilPreviewGallery -Path .\MyShapes.vssx -OutputDirectory .\StencilGallery -Title 'Custom stencil previews'
Read, Inspect, and Convert ๐
PSWriteOffice is not only about writing files. The module now has stronger read-back and bridge workflows too.
Excel import/export fast path
$data | Export-OfficeExcel -Path .\Report.xlsx -WorksheetName 'Data' -TableName 'Data' -AutoFit -FreezeTopRow
Import-OfficeExcel -Path .\Report.xlsx -WorksheetName 'Data'
Excel benchmark snapshot
The benchmark harness in Benchmarks\Compare-ExcelPerformance.ps1 compares
Export-OfficeExcel / Import-OfficeExcel with ImportExcel and ExcelFast
on repeatable local workloads. The table below is a local snapshot, not a
universal performance promise: hardware, PowerShell version, data shape,
formatting, and workbook features all matter.
Run context: PowerShell 7.5.5, AMD Ryzen 9 9950X3D2 16-Core Processor (16 cores / 32 logical processors), 64 GB RAM, Standard suite, median of three runs, measured on 2026-06-22.
| Scenario | Rows | Winner | PSWriteOffice 1.0.3 / OfficeIMO.Excel 0.6.47 | ImportExcel 7.8.10 | ExcelFast 0.0.1-alpha16 | Relative time (winner = 1x) |
|---|---|---|---|---|---|---|
| Export objects default | 1k | PSWriteOffice | 16 ms | 183 ms | 126 ms | PSWriteOffice 1x; ImportExcel 11.44x; ExcelFast 7.88x |
| Export objects default | 10k | PSWriteOffice | 84 ms | 1,287 ms | 153 ms | PSWriteOffice 1x; ImportExcel 15.32x; ExcelFast 1.82x |
| Export objects default | 25k | PSWriteOffice | 199 ms | 3,827 ms | 233 ms | PSWriteOffice 1x; ImportExcel 19.23x; ExcelFast 1.17x |
| Export wide objects default | 1k | PSWriteOffice | 26 ms | 166 ms | 127 ms | PSWriteOffice 1x; ImportExcel 6.38x; ExcelFast 4.88x |
| Export wide objects default | 10k | PSWriteOffice | 256 ms | 1,231 ms | 296 ms | PSWriteOffice 1x; ImportExcel 4.81x; ExcelFast 1.16x |
| Export wide objects default | 25k | PSWriteOffice | 679 ms | 3,832 ms | 1,134 ms | PSWriteOffice 1x; ImportExcel 5.64x; ExcelFast 1.67x |
| Import mixed objects full sheet | 1k | PSWriteOffice | 9 ms | 52 ms | 32 ms | PSWriteOffice 1x; ImportExcel 5.78x; ExcelFast 3.56x |
| Import mixed objects full sheet | 10k | PSWriteOffice | 98 ms | 383 ms | 311 ms | PSWriteOffice 1x; ImportExcel 3.91x; ExcelFast 3.17x |
| Import mixed objects full sheet | 25k | PSWriteOffice | 333 ms | 3,083 ms | 881 ms | PSWriteOffice 1x; ImportExcel 9.26x; ExcelFast 2.65x |
| Report workbook with table, chart, pivot, formatting | 1k | PSWriteOffice | 98 ms | 214 ms | n/a | PSWriteOffice 1x; ImportExcel 2.18x; ExcelFast n/a |
| Report workbook with table, chart, pivot, formatting | 10k | PSWriteOffice | 1,134 ms | 1,560 ms | n/a | PSWriteOffice 1x; ImportExcel 1.38x; ExcelFast n/a |
| Report workbook with table, chart, pivot, formatting | 25k | PSWriteOffice | 3,677 ms | 3,929 ms | n/a | PSWriteOffice 1x; ImportExcel 1.07x; ExcelFast n/a |
| Export regional workbook, one table per sheet | 1k | PSWriteOffice | 105 ms | 248 ms | n/a | PSWriteOffice 1x; ImportExcel 2.36x; ExcelFast n/a |
| Export regional workbook, one table per sheet | 10k | PSWriteOffice | 735 ms | 1,509 ms | n/a | PSWriteOffice 1x; ImportExcel 2.05x; ExcelFast n/a |
| Export regional workbook, one table per sheet | 25k | PSWriteOffice | 2,003 ms | 3,611 ms | n/a | PSWriteOffice 1x; ImportExcel 1.8x; ExcelFast n/a |
| Export title, offset header, frozen top row | 1k | ImportExcel | 251 ms | 222 ms | n/a | PSWriteOffice 1.13x; ImportExcel 1x; ExcelFast n/a |
| Export title, offset header, frozen top row | 10k | PSWriteOffice | 735 ms | 1,370 ms | n/a | PSWriteOffice 1x; ImportExcel 1.86x; ExcelFast n/a |
| Export title, offset header, frozen top row | 25k | PSWriteOffice | 1,828 ms | 3,290 ms | n/a | PSWriteOffice 1x; ImportExcel 1.8x; ExcelFast n/a |
| Export data workbook with summary formulas | 1k | PSWriteOffice | 53 ms | 192 ms | n/a | PSWriteOffice 1x; ImportExcel 3.62x; ExcelFast n/a |
| Export data workbook with summary formulas | 10k | PSWriteOffice | 507 ms | 1,449 ms | n/a | PSWriteOffice 1x; ImportExcel 2.86x; ExcelFast n/a |
| Export data workbook with summary formulas | 25k | PSWriteOffice | 1,531 ms | 3,254 ms | n/a | PSWriteOffice 1x; ImportExcel 2.13x; ExcelFast n/a |
To compare on your machine, run:
pwsh -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\Benchmarks\Compare-ExcelPerformance.ps1 -Suite Standard
The benchmark output also records exact module prerelease versions, machine and
runtime metadata, per-engine support status, file sizes, and memory deltas in
the generated CSV/JSON files. Export scenarios are also opened after generation
and checked with Open XML validation when the validator is available. The full
scenario list includes append, existing-workbook update, many-sheet, named
range, chart-only, pivot-only, and read-focused workloads. See
Benchmarks\README.md for the artifact schema and scenario list.
Excel readers
Get-OfficeExcelData -Path .\Report.xlsx -Sheet 'Data'
Get-OfficeExcelRange -Path .\Report.xlsx -Sheet 'Data' -Range 'A1:B10'
Get-OfficeExcelUsedRange -Path .\Report.xlsx -Sheet 'Data' -AsDataTable
Get-OfficeExcelNamedRange -Path .\Report.xlsx
Get-OfficeExcelPivotTable -Path .\Report.xlsx
Word bridges
$markdown = ConvertTo-OfficeWordMarkdown -Path .\Report.docx
ConvertFrom-OfficeWordMarkdown -Markdown $markdown -OutputPath .\Report-Roundtrip.docx
$doc = Get-OfficeWord -Path .\Report.docx
Update-OfficeWordText -Document $doc -OldValue 'FY24' -NewValue 'FY25'
Close-OfficeWord -Document $doc -Save
RTF bridges
ConvertTo-OfficeRtf -WordPath .\Report.docx -OutputPath .\Report.rtf
ConvertTo-OfficeRtf -HtmlPath .\Report.html -OutputPath .\Report-from-html.rtf
ConvertFrom-OfficeRtf -Path .\Report.rtf -As Word -OutputPath .\Report-from-rtf.docx
ConvertFrom-OfficeRtf -Path .\Report.rtf -As Html -OutputPath .\Report.html -IncludeDefaultCss
Word charts with the current API
$doc = New-OfficeWord -Path .\Report.docx
$chart = $doc.AddChart('Revenue Mix')
$chart.AddPie('North America', 125000).
AddPie('EMEA', 98000).
AddPie('APAC', 143000) | Out-Null
Close-OfficeWord -Document $doc -Save
Word tables with extra columns
$tableData = $data | Select-Object Region, Revenue,
@{ Name = 'RevenueBand'; Expression = { if ($_.Revenue -gt 100000) { 'High' } else { 'Standard' } } }
New-OfficeWord -Path .\Report.docx {
Add-OfficeWordTable -InputObject $tableData -Style 'GridTable1LightAccent1'
}
PowerPoint inspection
$ppt = Get-OfficePowerPoint -FilePath .\Deck.pptx
Get-OfficePowerPointSlide -Presentation $ppt
Get-OfficePowerPointSlideSummary -Presentation $ppt
Get-OfficePowerPointNotes -Presentation $ppt
Get-OfficePowerPointShape -Presentation $ppt -Index 0
Recent Highlights ๐
- Word to Markdown and Markdown to Word are now surfaced directly through
OfficeIMO.Word.Markdown. - PDF now has first-class creation, composition, merge/split, page movement, metadata, forms, stamping, attachment, extraction, HTML conversion, and compliance inspection cmdlets. Deeper PDF engine features still belong in
OfficeIMO.Pdffirst. - RTF now has create/read/update/replace cmdlets, Word/HTML/PDF bridges, and semantic Reader chunks through the
OfficeIMO.Reader.Rtfadapter. OfficeIMO.Readeris surfaced through chunk, document-envelope, table, visual, asset, ingest, JSON, and capability-discovery cmdlets across the currently registered adapters.OfficeIMO.Visiois surfaced through a first diagram DSL plus built-in and external stencil catalogs, create/load/save, deterministic inspection, reference gallery generation, stencil preview gallery export, and SVG/PNG export cmdlets.- Excel now has
Add-OfficeExcelTableOfContents,Get-OfficeExcelRange, andGet-OfficeExcelUsedRange. - PowerPoint now has section cmdlets, deck-wide text replacement, slide import helpers, slide copy, transitions, slide sizing, theme inspection, theme updates, and layout switching.
- Excel charts can now be finished with
Set-OfficeExcelChartLegend,Set-OfficeExcelChartDataLabels, andSet-OfficeExcelChartStyle. - Excel now has discoverable URL-image insertion plus smarter external hyperlink helpers.
- Excel summary sheets can now auto-link ranges and header-based columns to workbook tabs or external URLs.
- Backlink placement in Excel TOC flows is safer by default, avoiding overwriting active worksheet data.
Documentation ๐
Use the examples in this README for the first pass, then move to the generated command reference when you need exact parameters and pipeline behavior.
- Docs/Readme.md is the generated cmdlet index.
- Docs contains one generated page per command.
- Examples contains runnable scripts grouped by Word, Excel, PowerPoint, PDF, RTF, Markdown, CSV, Visio, and shared samples.
- Roadmap/PSWriteOffice-CmdletExamplesAndPolishPlan.md tracks the documentation quality bar and the next wrapper-polish priorities.
The generated command pages are built from XML comments in the cmdlet source. Prefer contextual examples there too: small workflow snippets with real input data, generated artifacts, readback/validation where useful, and no fake PowerShell compatibility aliases.
Build and Test ๐งช
dotnet build .\Sources\PSWriteOffice.sln -c Debug
pwsh -NoLogo -NoProfile -File .\PSWriteOffice.Tests.ps1
pwsh -NoLogo -NoProfile -File .\Build\Validate-PackagedArtefact.ps1
Regenerate the module manifest, generated help, packed artifacts, and website API payloads through the build-module lane:
pwsh -NoLogo -NoProfile -File .\Build\Manage-PSWriteOffice.ps1
pwsh -NoLogo -NoProfile -File .\Build\Export-ApiDocsSnapshot.ps1
pwsh -NoLogo -NoProfile -File .\Build\Measure-CmdletXmlExamples.ps1 -RequireExamples
Development loading is handled through PSWriteOffice.psm1, which prefers the local debug build in Sources\PSWriteOffice\bin\Debug\.
License
MIT