Skip to content

Commit a6a37eb

Browse files
Merge pull request #10 from PowerShellWeb/Init-MathML
MathML 0.1
2 parents e62ae01 + 972350e commit a6a37eb

22 files changed

+4300
-1
lines changed

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: [StartAutomating]

.github/workflows/BuildMathML.yml

Lines changed: 501 additions & 0 deletions
Large diffs are not rendered by default.

Build/GitHub/Jobs/BuildMathML.psd1

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@{
2+
"runs-on" = "ubuntu-latest"
3+
if = '${{ success() }}'
4+
steps = @(
5+
@{
6+
name = 'Check out repository'
7+
uses = 'actions/checkout@main'
8+
},
9+
'RunEZOut' # ,
10+
<#@{
11+
name = 'Run MathML (on branch)'
12+
if = '${{github.ref_name != ''main''}}'
13+
uses = './'
14+
id = 'MathMLAction'
15+
}#>
16+
# 'BuildAndPublishContainer'
17+
)
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@{
2+
name = 'PublishTestResults'
3+
uses = 'actions/upload-artifact@main'
4+
with = @{
5+
name = 'PesterResults'
6+
path = '**.TestResults.xml'
7+
}
8+
if = '${{always()}}'
9+
}
10+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#requires -Module PSDevOps
2+
Import-BuildStep -SourcePath (
3+
Join-Path $PSScriptRoot 'GitHub'
4+
) -BuildSystem GitHubWorkflow
5+
6+
Push-Location ($PSScriptRoot | Split-Path)
7+
New-GitHubWorkflow -Name "Build MathML Module" -On Push,
8+
PullRequest,
9+
Demand -Job TestPowerShellOnLinux,
10+
TagReleaseAndPublish, BuildMathML -Environment ([Ordered]@{
11+
REGISTRY = 'ghcr.io'
12+
IMAGE_NAME = '${{ github.repository }}'
13+
}) -OutputPath .\.github\workflows\BuildMathML.yml
14+
15+
Pop-Location

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## MathML 0.1
2+
3+
* Initial release of MathML (#1)
4+
* Core Commands:
5+
* Get-MathML (#2)
6+
* Export-MathML (#3)
7+
* Import-MathML (#4)
8+
* Extended Types
9+
* `MathML.get_SVG` (#5)
10+
* `MathML.ToString()` (#8)
11+
* `MathML.get/set_ID` (#9)
12+
* MathML schemas included (#7)
13+
* Initial Workflow (#6)

Commands/Export-MathML.ps1

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
function Export-MathML {
2+
<#
3+
.SYNOPSIS
4+
Exports MathML
5+
.DESCRIPTION
6+
Exports MathML into a file
7+
.EXAMPLE
8+
MathML https://dlmf.nist.gov/2.1 |
9+
Export-MathML ./dlmf.2.1.html
10+
#>
11+
[Alias('Save-MathML')]
12+
param(
13+
# The export file path.
14+
[Parameter(Mandatory)]
15+
[Alias('Fullname')]
16+
[string]
17+
$FilePath,
18+
19+
# Any input objects.
20+
[Parameter(ValueFromPipeline)]
21+
[PSObject[]]
22+
$InputObject,
23+
24+
# If set, will force an export, even if a file already exists.
25+
[switch]
26+
$Force
27+
)
28+
29+
# Gather all the input
30+
$allInput = @($input)
31+
32+
# If nothing was passed
33+
if ($allInput.Length -eq 0) {
34+
# briefly check for non-piped -InputObject
35+
if ($PSBoundParameters.InputObject) {
36+
$allInput = @($PSBoundParameters.InputObject | . { process { $_ } })
37+
}
38+
# If we still have no input, return (there is nothing to export)
39+
if ($allInput.Length -eq 0) {return}
40+
}
41+
42+
# Find the full path, but do not resolve it
43+
$unresolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
44+
# If it already existed, and we are not using the `-Force`
45+
if ((Test-Path $unresolvedPath) -and -not $Force) {
46+
# write an error
47+
Write-Error "$unresolvedPath already exists, use -Force"
48+
# and return
49+
return
50+
}
51+
52+
53+
# IF we have one MathML
54+
if ($allInput.Length -eq 1 -and $allInput[0] -is [xml]) {
55+
# save that to a file
56+
$newFile = New-Item -Path $unresolvedPath -Force -ItemType File
57+
# If the extension was .svg or .html, and the input has an SVG
58+
if ($newFile.Extension -in '.svg', '.html' -and $allInput[0].SVG -is [xml]) {
59+
# save the SVG to the file
60+
$allInput[0].SVG.Save($newFile.FullName)
61+
} else {
62+
# otherwise, save the XML to the file
63+
$allInput[0].Save($newFile.FullName)
64+
}
65+
}
66+
# If we have multiple MathML
67+
else {
68+
# we can store them in an XHTML file
69+
$html = @(
70+
# construct a simple header
71+
"<html><title>MathML</title><body>"
72+
foreach ($in in $allInput) {
73+
# and put each MathML within a div
74+
"<div>"
75+
76+
# If it was XML
77+
if ($in -is [xml]) {
78+
$in.OuterXml # put it inline
79+
}
80+
# If there was a SVG property
81+
elseif ($in.SVG) {
82+
# put that inline
83+
$in.SVG.OuterXml
84+
}
85+
# If there was a HTML property
86+
elseif ($in.HTML) {
87+
# put that inline (if it was unbalanced, export will not work)
88+
$in.HTML
89+
}
90+
# last but not least, escape any text
91+
else {
92+
[Security.SecurityElement]::Escape("$in")
93+
}
94+
"</div>"
95+
}
96+
"</body></html>"
97+
) -join [Environment]::NewLine
98+
99+
# Create a new file containing the HTML
100+
New-Item -Path $unresolvedPath -Force -ItemType File -Value $html
101+
}
102+
}

Commands/Get-MathML.ps1

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
function Get-MathML
2+
{
3+
<#
4+
.SYNOPSIS
5+
Gets MathML
6+
.DESCRIPTION
7+
Gets MathML from a file or page
8+
.EXAMPLE
9+
MathML https://dlmf.nist.gov/2.1
10+
.EXAMPLE
11+
MathML 'https://en.wikipedia.org/wiki/Rose_(mathematics)'
12+
.EXAMPLE
13+
MathML "<math xmlns='http://www.w3.org/1998/Math/MathML'>
14+
<semantics>
15+
<mrow>
16+
<mn>1</mn>
17+
<mo>+</mo>
18+
<mn>1</mn>
19+
<mo>=</mo>
20+
<mn>2</mn>
21+
</mrow>
22+
</semantics>
23+
</math>"
24+
#>
25+
[Alias('MathML')]
26+
param(
27+
# A url or file path that hopefully contains MathML
28+
# The response from this URL will be cached.
29+
[Parameter(ValueFromPipelineByPropertyName)]
30+
[Alias('Uri','FilePath','Fullname')]
31+
[string]
32+
$Url,
33+
34+
# If set, will request the URL, even if it has been cached.
35+
[Parameter(ValueFromPipelineByPropertyName)]
36+
[switch]
37+
$Force,
38+
39+
# If set, will use chromium to request the page, and will
40+
[Parameter(ValueFromPipelineByPropertyName)]
41+
[switch]
42+
$UseChromium,
43+
44+
# The path to a chromium browser.
45+
[Parameter(ValueFromPipelineByPropertyName)]
46+
[string]
47+
$ChromiumPath = 'chromium'
48+
)
49+
50+
begin {
51+
if (-not $script:MathMLCache) {
52+
$script:MathMLCache = [Ordered]@{}
53+
}
54+
55+
$mathMlPattern = [Regex]::new('<math[\s\S]+?</math>','IgnoreCase')
56+
}
57+
58+
process {
59+
# If we have no URL
60+
if (-not $PSBoundParameters.Url) {
61+
# get any loaded MathML
62+
$mathMLValues = @($script:MathMLCache.Values.MathML)
63+
if ($mathMLValues) {
64+
# unroll each result
65+
foreach ($value in $mathMLValues) {
66+
if (-not $value) { continue }
67+
# and return non-null values
68+
$value
69+
}
70+
}
71+
return
72+
}
73+
74+
# If we have not yet cached this URL, or we are using the `-Force`
75+
if (-not $script:MathMLCache["$url"] -or $Force) {
76+
# Create a cache object
77+
$script:MathMLCache["$url"] = [Ordered]@{
78+
Response =
79+
# If the URL could be XML
80+
if ($url -as [xml]) {
81+
# use that as the source.
82+
($url -as [xml]).OuterXml
83+
}
84+
# If the URL was actually a file path
85+
elseif (Test-Path $url)
86+
{
87+
# get it's content.
88+
Get-Content -Raw $Url
89+
}
90+
# If we are not using chromium,
91+
elseif (-not $UseChromium)
92+
{
93+
# use Invoke-RestMethod to get the URL
94+
Invoke-RestMethod $url
95+
}
96+
# If we are using chromium
97+
else
98+
{
99+
# Call chromium in headless mode and dump DOM
100+
& $ChromiumPath --headless --disable-gpu --no-sandbox --dump-dom "$url" *>&1 |
101+
# strip out any chromium trace messages
102+
Where-Object { $_ -notmatch '^\[\d+:\d+' } |
103+
# and stringify the whole response.
104+
Out-String -Width 1mb
105+
}
106+
}
107+
}
108+
109+
# If we have a response for this URL, but no MathML yet
110+
if (
111+
$script:MathMLCache["$url"].Response -and -not
112+
$script:MathMLCache["$url"].MathML
113+
) {
114+
$script:MathMLCache["$url"].MathML =
115+
# find any matches for our pattern
116+
@(foreach ($match in $mathMlPattern.Matches("$(
117+
$script:MathMLCache["$url"].Response
118+
)")) {
119+
# and cast them into XML.
120+
$matchXml = $match.Value -as [xml]
121+
122+
if (-not $matchXML) { continue }
123+
# If they do not have the xml namespace
124+
if (-not $matchXML.math.xmlns) {
125+
# add it
126+
$matchXML.math.setAttribute('xmlns', 'http://www.w3.org/1998/Math/MathML')
127+
}
128+
# decorate the return as MathML
129+
$matchXml.pstypenames.insert(0, 'MathML')
130+
# and output it to the cache
131+
$matchXml
132+
})
133+
134+
}
135+
136+
# Last but not least, output any MathML objects in the cache for this URL.
137+
$script:MathMLCache["$url"].MathML
138+
}
139+
}
140+

Commands/Import-MathML.ps1

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
function Import-MathML
2+
{
3+
<#
4+
.SYNOPSIS
5+
Imports MathML
6+
.DESCRIPTION
7+
Imports MathML from a file or URL
8+
.LINK
9+
Get-MathML
10+
#>
11+
[Alias('Restore-MathML')]
12+
param(
13+
# The path to a file or URL that hopefully contains MathML
14+
[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
15+
[string]
16+
$FilePath
17+
)
18+
19+
process {
20+
# This is an extremely light wrapper of Get-MathML.
21+
Get-MathML @PSBoundParameters
22+
}
23+
24+
}

0 commit comments

Comments
 (0)