Skip to content

Instantly share code, notes, and snippets.

@PSingletary
Created December 17, 2017 18:29
Show Gist options
  • Save PSingletary/92c7876902bf702782739424a6f60a9c to your computer and use it in GitHub Desktop.
Save PSingletary/92c7876902bf702782739424a6f60a9c to your computer and use it in GitHub Desktop.
PowerPoint Powered by PowerShell (Materials for http://www.slideshare.net/miriyagi/powerpoint-powered-by-powershell)
[Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.PowerPoint")
[Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[string] $Source = "$(Split-Path $Script:MyInvocation.MyCommand.Path -Parent)\Slide.html"
[int] $DefaultTitleHeight = 150
function Add-Textbox {
param (
[object] $Slide,
[string] $Text,
[int[]] $Margin = @(20, 20, 20, 170),
[int[]] $Padding = @(20, 20, 20, 20),
[int] $ForeColor = 0x000000,
[int] $BackColor = 0xFFFFFF,
[string] $AsciiFont = "Segoe UI",
[string] $FarEastFont = "メイリオ",
[int] $FontSize = 36,
[Microsoft.Office.Core.MsoVerticalAnchor] $VerticalAlignment = [Microsoft.Office.Core.MsoVerticalAnchor]::msoAnchorTop,
[Microsoft.Office.Interop.PowerPoint.PpParagraphAlignment] $HorizontalAlignment = [Microsoft.Office.Interop.PowerPoint.PpParagraphAlignment]::ppAlignLeft,
[int] $ThemeColor = -1,
[bool] $Bullet = $false
)
if ($Slide -eq $null -or $Text -eq $null -or $Padding.Count -ne 4 -or $Margin.Count -ne 4) {
throw "Invalid Arguments $Slide, $Text, $Padding, $Margin"
}
[float] $pWidth = $Slide.Master.Width - $Margin[0] - $Margin[2]
[float] $pHeight = $Slide.Master.Height - $Margin[1] - $Margin[3]
[Microsoft.Office.Core.MsoTextOrientation] $orientation = [Microsoft.Office.Core.MsoTextOrientation]::msoTextOrientationHorizontal
[object] $textbox = $Slide.Shapes.AddTextbox($orientation, $Margin[0], $Margin[1], $pWidth, $pHeight)
if ($textbox -eq $null) {
throw "Could not create a text box"
}
[object] $frame = $textbox.TextFrame
$frame.AutoSize = [Microsoft.Office.Interop.PowerPoint.PpAutoSize]::ppAutoSizeNone
$frame.MarginTop = $Padding[1]
$frame.MarginLeft = $Padding[0]
$frame.MarginRight = $Padding[2]
$frame.MarginBottom = $Padding[3]
$frame.VerticalAnchor = $VerticalAlignment
[object] $range = $frame.TextRange
$range.Text = $Text
[object] $format = $range.ParagraphFormat
$format.Alignment = $HorizontalAlignment
if ($Bullet) {
$format.Bullet.Visible = 1
$format.Bullet.Type = 1
$format.SpaceWithin = 2
}
[object] $font = $range.Font
$font.NameFarEast = $FarEastFont
$font.NameAscii = $AsciiFont
$font.Size = $FontSize
$font.Color.RGB = $ForeColor
if ($ThemeColor -eq -1) {
$textbox.Fill.BackColor.RGB = $BackColor
} else {
$textbox.Fill.BackColor.ObjectThemeColor = $ThemeColor
}
$textbox.Height = $pHeight
return $textbox
}
function Add-TitleSlide {
param (
[object] $Presentation,
[string] $Title,
[string] $Author
)
[object] $slide = $Presentation.Slides.Add($Presentation.Slides.Count + 1, 12)
[hashtable] $titleArgs = @{
Slide = $slide;
Text = $Title;
Margin = @(0, 0, 0, 150);
Padding = @(50, 50, 50, 20);
BackColor = 0x2647D2;
ForeColor = 0xFFFFFF;
FontSize = 72;
VerticalAlignment = [Microsoft.Office.Core.MsoVerticalAnchor]::msoAnchorBottom;
}
[hashtable] $authorArgs = @{
Slide = $slide;
Text = $Author;
Margin = @(0, 390, 0, 0);
Padding = @(50, 20, 50, 50);
ForeColor = 0x2647D2;
FontSize = 36;
VerticalAlignment = [Microsoft.Office.Core.MsoVerticalAnchor]::msoAnchorTop;
}
[object] $titleTextbox = Add-Textbox @titleArgs
[object] $authorTextbox = Add-Textbox @authorArgs
return $slide
}
function Add-SlideTitle {
param (
[object] $Slide,
[string] $Title = [string]::Empty
)
if ($Slide -eq $null) {
return $null
}
[int[]] $colors = @(3, 5, 6, 7, 8, 9, 10)
[hashtable] $args = @{
Slide = $Slide;
Text = $Title;
Margin = @(0, 390, 0, 0);
HorizontalAlignment = [Microsoft.Office.Interop.PowerPoint.PpParagraphAlignment]::ppAlignCenter;
VerticalAlignment = [Microsoft.Office.Core.MsoVerticalAnchor]::msoAnchorMiddle;
FontSize = 48;
ForeColor = 0xFFFFFF;
ThemeColor = $colors[$Slide.Parent.Slides.Count % $colors.Count];
}
[object] $textbox = Add-Textbox @args
return $textbox
}
function Add-Image {
param (
[object] $Slide,
[string] $Image,
[int] $TitleHeight = $DefaultTitleHeight,
[string] $Align = "middle",
[string] $Cite = [string]::Empty,
[switch] $ScaleToFit
)
if ($Slide -eq $null -or [string]::IsNullOrEmpty($Image)) {
return $null
}
if ($Image -match "^https?://") {
# Download a remote file
[Uri] $uri = New-Object Uri $Image
[Net.WebClient] $client = New-Object Net.WebClient
$Image = Join-Path -Path $env:TEMP -ChildPath $uri.Segments[$uri.Segments.Length - 1]
$client.DownloadFile($uri, $Image)
} elseif (![IO.Path]::IsPathRooted($Image)) {
# Convert a relative path to an absolute one
[string] $dirName = [IO.Path]::GetDirectoryName($Source)
$Image = Join-Path -Path $dirName -ChildPath $Image
}
# Calculate the width & height
[float] $slideHeight = $Slide.Master.Height - $TitleHeight
[float] $slideWidth = $Slide.Master.Width
[object] $bitmap = New-Object Drawing.Bitmap $Image
[float] $magX = $slideWidth / $bitmap.Width
[float] $magY = $slideHeight / $bitmap.Height
[float] $mag = if ($Fit.IsPresent -or $Align -eq "fit") { [Math]::Min($magX, $magY) } else { [Math]::Max($magX, $magY) }
[float] $pictureWidth = $bitmap.Width * $mag
[float] $pictureHeight = $bitmap.Height * $mag
[object] $picture = $Slide.Shapes.AddPicture($Image, $false, $true, 0, 0, $pictureWidth, $pictureHeight)
# X-Crop
if ($pictureWidth -gt $slideWidth) {
[float] $cropWidth = $pictureWidth - $slideWidth
$picture.PictureFormat.Crop.ShapeLeft += $cropWidth / 2
$picture.PictureFormat.Crop.ShapeWidth -= $cropWidth
$picture.Left = 0
} elseif ($pictureWidth -lt $slideWidth) {
[float] $deltaWidth = ($slideWidth - $pictureWidth) / 2
$picture.Left = $deltaWidth
}
# Y-Crop
if ($pictureHeight -gt $slideHeight) {
[float] $cropHeight = $pictureHeight - $slideHeight
switch ($Align) {
"top" {
$picture.PictureFormat.Crop.ShapeHeight -= $cropHeight
$picture.Top = 0
}
"bottom" {
$picture.PictureFormat.Crop.ShapeTop += $cropHeight
$picture.Top = 0
}
default {
$picture.PictureFormat.Crop.ShapeTop += $cropHeight / 2
$picture.PictureFormat.Crop.ShapeHeight -= $cropHeight
$picture.Top = 0
}
}
} elseif ($pictureHeight -lt $slideHeight) {
[float] $deltaHeight = ($slideHeight - $pictureHeight) / 2
$picture.Top = $deltaHeight
}
if (![string]::IsNullOrEmpty($Cite)) {
Add-Textbox -Slide $Slide -Text $Cite -Margin @(700, 50, 50, 440) -FontSize 18
}
return $picture
}
function Add-Code {
param (
[object] $Slide,
[string] $Code
)
[hashtable] $args = @{
Slide = $Slide;
Text = $Code.Trim();
ForeColor = 0xFFFFFF;
BackColor = 0x562401;
AsciiFont = "Consolas";
FontSize = 24;
}
[object] $textbox = Add-Textbox @args
return $textbox
}
function Add-List {
param (
[object] $Slide,
[string[]] $Items
)
if ($Items -eq $null) {
return $null
}
[hashtable] $args = @{
Slide = $Slide;
Text = [string]::Join("`n", $Items);
Bullet = $true;
}
[object] $textbox = Add-Textbox @args
return $textbox
}
function New-Slide {
param (
[object] $Presentation,
[Xml.XmlElement] $Xml
)
if ($Presentation -eq $null -or $Xml -eq $null) {
return $null
}
[object] $slide = $Presentation.Slides.Add($Presentation.Slides.Count + 1, 12)
$slide.Select()
if ($Xml.h1 -ne $null) {
$DefaultTitleHeight = 150
Add-SlideTitle -Slide $slide -Title $Xml.h1
} else {
$DefaultTitleHeight = 0
}
[string] $cite = [string]::Empty
if ($Xml.q -ne $null) {
$cite = $Xml.q.cite
$Xml = $Xml.q
}
if ($Xml.img -ne $null) {
Add-Image -Slide $slide -Image $Xml.img.src -Align $Xml.img.align -Cite $cite
}
if ($Xml.pre.code -ne $null) {
Add-Code -Slide $slide -Code $Xml.pre.code
}
if ($Xml.ul -ne $null) {
Add-List -Slide $slide -Items $Xml.ul.li
}
return $slide
}
try {
[xml] $xml = [xml] (Get-Content $Source)
} catch {
throw
}
[object] $powerPoint = New-Object -ComObject PowerPoint.Application
# $powerPoint.Visible = [Microsoft.Office.Core.MsoTriState]::msoTrue
[object] $presentation = $powerPoint.Presentations.Add($true)
[string] $title = $xml.html[1].head.title
[string] $author = $xml.html[1].head.meta | ?{ $_.name -eq 'author' } | Select-Object -ExpandProperty content
[object] $titleSlide = Add-TitleSlide -Presentation $presentation -Title $title -Author $author
$xml.html[1].body.section | ?{ $_ -ne $null } | %{
[int] $i = $presentation.Slides.Count
[int] $n = $xml.html[1].body.section.Count
[float] $p = 100.0 * $i / $n
Write-Progress -Activity "Generate PowerPoint presentation" -Status "$i / $n Completed" -PercentComplete $p
[void] (New-Slide -Presentation $presentation -Xml $_)
}
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- "Power{Point,ed by,Shell}" by @miriyagi at 1st #lt_titech -->
<title>Power{Point,ed by,Shell}</title>
<meta name="Author" content="八木 悠(@miriyagi)" />
</head>
<body>
<section>
<h1>八木 悠(@miriyagi)</h1>
<img src="puffy.png" />
</section>
<section>
<img src="love.jpg" align="top" />
</section>
<section>
<h1>研究で使うのは当然 Linux</h1>
<img src="linux.jpg" align="fit" />
</section>
<section>
<h1>研究に明け暮れる幸せな日々</h1>
<img src="research.jpg" />
</section>
<section>
<h1>研究発表でも</h1>
<img src="presentation.jpg" align="top" />
</section>
<section>
<h1>使うのは当然 Beamer</h1>
<img src="beamer.png" />
</section>
<section>
<h1>そんな CUI を愛する学生生活を満喫</h1>
<img src="terminal.jpg" />
</section>
<section>
<h1>とある企業に入社</h1>
<img src="hataraitara.jpg" align="top" />
</section>
<section>
<h1>渡されるのは Windows, Office, ...</h1>
<img src="windows.png" />
</section>
<section>
<h1>みんな GUI しかないじゃない!</h1>
<img src="mami.jpg" />
</section>
<section>
<h1>そんなある日……</h1>
<img src="start.png" />
</section>
<section>
<h1>PowerShell ならできるじゃない!</h1>
<img src="startps.png" />
</section>
<section>
<img src="logo.png" />
</section>
<section>
<h1>やりたいこと</h1>
<ul>
<li>PowerPoint プレゼンテーションを CUI で作りたい</li>
<li>XHTML を PowerShell 経由で変換できたら素敵</li>
<li>文字だけではなく画像やリストにも対応したい</li>
</ul>
</section>
<section>
<h1>PowerShell とは</h1>
<ul>
<li>Microsoft 製のシェルスクリプト言語</li>
<li>Windows 7 から標準搭載</li>
<li>最新版は PowerShell 3.0</li>
</ul>
</section>
<section>
<h1>オブジェクト指向シェル</h1>
<pre><code>
PS C:\Users\Yagi&gt; cd Temp
PS C:\Users\Yagi\Temp&gt; $x = ls
PS C:\Users\Yagi\Temp&gt; $x
ディレクトリ: C:\Users\Yagi\Temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2012/11/24 15:15 114145 bing.png
-a--- 2012/11/24 16:17 400645 logo.png
-a--- 2012/11/24 14:48 54711 love.png
PS C:\Users\Yagi\Temp&gt; $x[1].Name.ToUpper()
LOGO.PNG
</code></pre>
</section>
<section>
<h1>PowerShell ISE</h1>
<img src="ise.png" />
</section>
<section>
<h1>XHTML (XML) の読み込み</h1>
<pre><code>
$xml = [xml] (Get-Content "source.xhtml")
$sections = $xml.html.body.section
$x = $sections[0].h1
</code></pre>
</section>
<section>
<h1>プレゼンテーションの新規作成</h1>
<pre><code>
$powerPoint = New-Object -ComObject PowerPoint.Application
$presentation = $powerPoint.Presentations.Add($true)
</code></pre>
</section>
<section>
<h1>白紙スライドの追加</h1>
<pre><code>
$slide = $presentation.Slides.Add($index, 12)
</code></pre>
</section>
<section>
<h1>テキストボックスの作成</h1>
<pre><code>
$ori = [Microsoft.Office.Core.MsoTextOrientation]::msoTextOrientationHorizontal
# $ori = 1
$textBox = $slide.Shapes.AddTextBox($ori, $x, $y, $width, $height)
$textBox.TextFrame.TextRange.Text = "YUKI.N&gt;みえてる?"
</code></pre>
</section>
<section>
<h1>テキストボックスの変更</h1>
<pre><code>
# フォント
$box.TextFrame.TextRange.Font.NameFarEast = "メイリオ"
$box.TextFrame.TextRange.Font.NameAscii = "Consolas"
$box.TextFrame.TextRange.Font.Size = 24
# 色
$box.Fill.BackColor.RGB = 0x562401 # -&gt; RGB(0x01, 0x24, 0x56)
$box.TextFrame.TextRange.Font.Color.RGB = 0xFFFFFF
</code></pre>
</section>
<section>
<h1>画像の埋め込み</h1>
<pre><code>
$picture = $Slide.Shapes.AddPicture("image.png", $false, $true, $posX, $poxY, $width, $height)
</code></pre>
</section>
<section>
<h1>画像のトリミング</h1>
<pre><code>
if ($pictureWidth -gt $slideWidth) {
$cropWidth = $pictureWidth - $slideWidth
$picture.PictureFormat.Crop.ShapeLeft += $cropWidth / 2
$picture.PictureFormat.Crop.ShapeWidth -= $cropWidth
$picture.Left = 0
}
</code></pre>
</section>
<section>
<h1>後はこれを組み合わせれば……</h1>
<img src="combination.png" />
</section>
<section>
<h1>完成!</h1>
<img src="code.png" align="top" />
</section>
<section>
<h1>デモ</h1>
</section>
<section>
<h1>まとめ</h1>
<ul>
<li>XHTML を PowerPoint プレゼンテーションにする PowerShell スクリプトを書いたよ!</li>
<li>Windows でも(意外と)CUI で遊べるよ!</li>
</ul>
</section>
<section>
<h1>今後の課題</h1>
<ul>
<li>スライドテンプレートの利用</li>
<li>複数コンテンツへの対応</li>
<li>他のタグへの対応</li>
</ul>
</section>
<section>
<img src="owari.png" align="fit" />
</section>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment