Skip to content

Commit c952f4a

Browse files
committed
feat: add Blazor WASM online converter with GitHub Pages deployment
1 parent cbe8b70 commit c952f4a

32 files changed

+1393
-0
lines changed

.github/workflows/pages.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- 'MiniPdf.Web/**'
8+
- 'src/MiniPdf/**'
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
pages: write
14+
id-token: write
15+
16+
concurrency:
17+
group: pages
18+
cancel-in-progress: true
19+
20+
jobs:
21+
deploy:
22+
runs-on: ubuntu-latest
23+
environment:
24+
name: github-pages
25+
url: ${{ steps.deployment.outputs.page_url }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Setup .NET
30+
uses: actions/setup-dotnet@v4
31+
with:
32+
dotnet-version: '9.0.x'
33+
34+
- name: Publish Blazor WASM
35+
run: dotnet publish MiniPdf.Web/MiniPdf.Web.Client/MiniPdf.Web.Client.csproj -c Release -o publish
36+
37+
- name: Set base href for GitHub Pages
38+
run: sed -i 's|<base href="/" />|<base href="/MiniPdf/" />|g' publish/wwwroot/index.html
39+
40+
- name: Copy index.html to 404.html
41+
run: cp publish/wwwroot/index.html publish/wwwroot/404.html
42+
43+
- name: Add .nojekyll
44+
run: touch publish/wwwroot/.nojekyll
45+
46+
- name: Upload Pages artifact
47+
uses: actions/upload-pages-artifact@v3
48+
with:
49+
path: publish/wwwroot
50+
51+
- name: Deploy to GitHub Pages
52+
id: deployment
53+
uses: actions/deploy-pages@v4
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Deploy to Azure App Service
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
8+
env:
9+
AZURE_WEBAPP_NAME: minipdf-web # Change to your App Service name
10+
DOTNET_VERSION: '9.0.x'
11+
12+
jobs:
13+
build-and-deploy:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Setup .NET
20+
uses: actions/setup-dotnet@v4
21+
with:
22+
dotnet-version: ${{ env.DOTNET_VERSION }}
23+
24+
- name: Restore
25+
run: dotnet restore
26+
27+
- name: Publish
28+
run: dotnet publish MiniPdf.Web/MiniPdf.Web.csproj -c Release -o ${{ github.workspace }}/publish
29+
30+
- name: Deploy to Azure App Service
31+
uses: azure/webapps-deploy@v3
32+
with:
33+
app-name: ${{ env.AZURE_WEBAPP_NAME }}
34+
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
35+
package: ${{ github.workspace }}/publish
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- 'MiniPdf.Web/**'
8+
- 'src/MiniPdf/**'
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
pages: write
14+
id-token: write
15+
16+
concurrency:
17+
group: pages
18+
cancel-in-progress: true
19+
20+
jobs:
21+
deploy:
22+
runs-on: ubuntu-latest
23+
environment:
24+
name: github-pages
25+
url: ${{ steps.deployment.outputs.page_url }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Setup .NET
30+
uses: actions/setup-dotnet@v4
31+
with:
32+
dotnet-version: '9.0.x'
33+
34+
- name: Publish Blazor WASM
35+
run: dotnet publish MiniPdf.Web/MiniPdf.Web.Client/MiniPdf.Web.Client.csproj -c Release -o publish
36+
37+
- name: Set base href for GitHub Pages
38+
run: sed -i 's|<base href="/" />|<base href="/MiniPdf/" />|g' publish/wwwroot/index.html
39+
40+
- name: Copy index.html to 404.html
41+
run: cp publish/wwwroot/index.html publish/wwwroot/404.html
42+
43+
- name: Add .nojekyll
44+
run: touch publish/wwwroot/.nojekyll
45+
46+
- name: Upload Pages artifact
47+
uses: actions/upload-pages-artifact@v3
48+
with:
49+
path: publish/wwwroot
50+
51+
- name: Deploy to GitHub Pages
52+
id: deployment
53+
uses: actions/deploy-pages@v4

MiniPdf.Web/.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## .NET
2+
bin/
3+
obj/
4+
publish/
5+
*.user
6+
*.suo
7+
.vs/
8+
9+
## IDE
10+
.idea/
11+
*.swp
12+
*~
13+
14+
## OS
15+
Thumbs.db
16+
.DS_Store
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Router AppAssembly="typeof(App).Assembly">
2+
<Found Context="routeData">
3+
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
4+
<FocusOnNavigate RouteData="routeData" Selector="h1" />
5+
</Found>
6+
</Router>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@inherits LayoutComponentBase
2+
3+
<div class="page">
4+
@Body
5+
</div>
6+
7+
<div id="blazor-error-ui" data-nosnippet>
8+
An unhandled error has occurred.
9+
<a href="." class="reload">Reload</a>
10+
<span class="dismiss">🗙</span>
11+
</div>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.8" />
11+
<ProjectReference Include="..\..\src\MiniPdf\MiniPdf.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
@page "/"
2+
@inject IJSRuntime JS
3+
4+
<PageTitle>MiniPdf Converter</PageTitle>
5+
6+
<div class="card">
7+
<div class="logo">
8+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#4f46e5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
9+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
10+
<polyline points="14 2 14 8 20 8"/>
11+
<line x1="12" y1="18" x2="12" y2="12"/>
12+
<line x1="9" y1="15" x2="15" y2="15"/>
13+
</svg>
14+
<h1>MiniPdf</h1>
15+
</div>
16+
<p class="subtitle">Convert .docx / .xlsx to PDF in your browser &mdash; powered by <a href="https://github.com/mini-software/MiniPdf" target="_blank">MiniPdf</a></p>
17+
18+
<div class="drop-zone @(isDragOver ? "drag-over" : "")"
19+
@ondragenter="HandleDragEnter"
20+
@ondragleave="HandleDragLeave"
21+
@ondragover:preventDefault>
22+
23+
<InputFile OnChange="HandleFileSelected" accept=".docx,.xlsx" />
24+
25+
@if (selectedFile is null)
26+
{
27+
<div class="drop-zone-content">
28+
<svg class="upload-icon" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
29+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
30+
<polyline points="17 8 12 3 7 8"/>
31+
<line x1="12" y1="3" x2="12" y2="15"/>
32+
</svg>
33+
<p><strong>Drop file here</strong> or click to browse</p>
34+
<div class="badges">
35+
<span class="badge badge-docx">.docx</span>
36+
<span class="badge badge-xlsx">.xlsx</span>
37+
</div>
38+
</div>
39+
}
40+
else
41+
{
42+
<div class="drop-zone-content">
43+
<div class="file-info">
44+
<div class="file-icon-wrap @FileExt">
45+
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
46+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
47+
<polyline points="14 2 14 8 20 8"/>
48+
</svg>
49+
</div>
50+
<div class="file-meta">
51+
<div class="file-name">@selectedFile.Name</div>
52+
<div class="file-size">@FormatSize(selectedFile.Size)</div>
53+
</div>
54+
</div>
55+
</div>
56+
}
57+
</div>
58+
59+
<button class="convert-btn" @onclick="ConvertFile" disabled="@(!CanConvert)">
60+
@if (isConverting)
61+
{
62+
<span class="spinner"></span>
63+
<span>Converting...</span>
64+
}
65+
else
66+
{
67+
<span>Convert to PDF</span>
68+
}
69+
</button>
70+
71+
@if (errorMessage is not null)
72+
{
73+
<div class="alert alert-error">@errorMessage</div>
74+
}
75+
76+
@if (successMessage is not null)
77+
{
78+
<div class="alert alert-success">@successMessage</div>
79+
}
80+
</div>
81+
82+
<div class="intro">
83+
<h2>About MiniPdf</h2>
84+
<p>A lightweight, zero-dependency .NET library for converting <strong>.docx</strong> and <strong>.xlsx</strong> files to PDF. No Office installation required.</p>
85+
<div class="intro-links">
86+
<a href="https://github.com/mini-software/MiniPdf" target="_blank">
87+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.83-.26.83-.57L9 20.86c-3.37.74-4.08-1.63-4.08-1.63-.55-1.4-1.34-1.77-1.34-1.77-1.1-.75.08-.73.08-.73 1.21.08 1.85 1.24 1.85 1.24 1.08 1.84 2.83 1.31 3.52 1 .1-.78.42-1.31.76-1.61-2.69-.31-5.52-1.35-5.52-6a4.68 4.68 0 0 1 1.25-3.25 4.35 4.35 0 0 1 .12-3.21s1.02-.33 3.34 1.24a11.5 11.5 0 0 1 6.08 0c2.32-1.57 3.33-1.24 3.33-1.24a4.35 4.35 0 0 1 .12 3.21 4.68 4.68 0 0 1 1.25 3.25c0 4.66-2.84 5.69-5.54 5.99.43.37.82 1.1.82 2.22l-.01 3.29c0 .31.22.7.84.57A12 12 0 0 0 12 .3"/></svg>
88+
GitHub
89+
</a>
90+
<a href="https://www.nuget.org/packages/MiniPdf" target="_blank">
91+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/></svg>
92+
NuGet
93+
</a>
94+
</div>
95+
<div class="feedback">
96+
<p>Encountered a bug or have a suggestion? <a href="https://github.com/mini-software/MiniPdf/issues" target="_blank">Open an issue on GitHub</a></p>
97+
</div>
98+
</div>
99+
100+
@code {
101+
private IBrowserFile? selectedFile;
102+
private bool isConverting;
103+
private bool isDragOver;
104+
private string? errorMessage;
105+
private string? successMessage;
106+
107+
private bool CanConvert => selectedFile is not null && !isConverting;
108+
private string FileExt => selectedFile is not null
109+
? Path.GetExtension(selectedFile.Name).ToLowerInvariant().TrimStart('.')
110+
: "";
111+
112+
private void HandleFileSelected(InputFileChangeEventArgs e)
113+
{
114+
errorMessage = null;
115+
successMessage = null;
116+
selectedFile = e.File;
117+
118+
var ext = Path.GetExtension(selectedFile.Name).ToLowerInvariant();
119+
if (ext is not ".docx" and not ".xlsx")
120+
{
121+
errorMessage = "Unsupported file type. Please select a .docx or .xlsx file.";
122+
selectedFile = null;
123+
}
124+
}
125+
126+
private void HandleDragEnter() => isDragOver = true;
127+
private void HandleDragLeave() => isDragOver = false;
128+
129+
private async Task ConvertFile()
130+
{
131+
if (selectedFile is null) return;
132+
133+
isConverting = true;
134+
errorMessage = null;
135+
successMessage = null;
136+
StateHasChanged();
137+
await Task.Yield();
138+
139+
try
140+
{
141+
using var stream = selectedFile.OpenReadStream(maxAllowedSize: 50 * 1024 * 1024);
142+
using var ms = new MemoryStream();
143+
await stream.CopyToAsync(ms);
144+
ms.Position = 0;
145+
146+
var ext = Path.GetExtension(selectedFile.Name).ToLowerInvariant();
147+
byte[] pdfBytes = ext == ".docx"
148+
? MiniSoftware.MiniPdf.ConvertDocxToPdf(ms)
149+
: MiniSoftware.MiniPdf.ConvertToPdf(ms);
150+
151+
var outputName = Path.GetFileNameWithoutExtension(selectedFile.Name) + ".pdf";
152+
await JS.InvokeVoidAsync("downloadFile", outputName, "application/pdf", pdfBytes);
153+
successMessage = $"{outputName} downloaded successfully!";
154+
}
155+
catch (Exception ex)
156+
{
157+
errorMessage = $"Conversion error: {ex.Message}";
158+
}
159+
finally
160+
{
161+
isConverting = false;
162+
}
163+
}
164+
165+
private static string FormatSize(long bytes)
166+
{
167+
if (bytes >= 1024 * 1024)
168+
return $"{bytes / (1024.0 * 1024.0):F1} MB";
169+
if (bytes >= 1024)
170+
return $"{bytes / 1024.0:F1} KB";
171+
return $"{bytes} B";
172+
}
173+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using Microsoft.AspNetCore.Components.Web;
2+
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3+
using MiniPdf.Web.Client;
4+
5+
var builder = WebAssemblyHostBuilder.CreateDefault(args);
6+
builder.RootComponents.Add<App>("#app");
7+
8+
await builder.Build().RunAsync();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@using System.Net.Http
2+
@using System.Net.Http.Json
3+
@using Microsoft.AspNetCore.Components.Forms
4+
@using Microsoft.AspNetCore.Components.Routing
5+
@using Microsoft.AspNetCore.Components.Web
6+
@using Microsoft.AspNetCore.Components.Web.Virtualization
7+
@using Microsoft.JSInterop
8+
@using MiniPdf.Web.Client
9+
@using MiniPdf.Web.Client.Layout

0 commit comments

Comments
 (0)