Run PDF creation async and show log/progress

This commit is contained in:
2026-01-03 22:09:11 +09:00
parent 280ad26f58
commit c298c143f5
3 changed files with 86 additions and 22 deletions
+44 -17
View File
@@ -1,13 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using HarfBuzzSharp;
using ImageMagick; using ImageMagick;
using MigraDoc.DocumentObjectModel; using MigraDoc.DocumentObjectModel;
using MigraDoc.Rendering; using MigraDoc.Rendering;
using PdfSharp.Drawing;
using PdfSharp.Fonts; using PdfSharp.Fonts;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO; using PdfSharp.Pdf.IO;
using PdfSharp.Snippets.Font; using PdfSharp.Snippets.Font;
using ReceiptPDFBuilder.Interfaces; using ReceiptPDFBuilder.Interfaces;
@@ -16,12 +14,33 @@ namespace ReceiptPDFBuilder.ViewModels;
class MainViewModel : BaseViewModel, IFontResolver class MainViewModel : BaseViewModel, IFontResolver
{ {
private string _baseDir; private string _baseDir;
private bool _isCreatingPDF;
private string _createPDFLog;
public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger)
{ {
_baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? ""; _baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? "";
_isCreatingPDF = false;
_createPDFLog = "Ready to create PDF!";
}
public bool IsCreatingPDF
{
get => _isCreatingPDF;
set { _isCreatingPDF = value; NotifyPropertyChanged(); }
}
public string CreatePDFLog
{
get => _createPDFLog;
set { _createPDFLog = value; NotifyPropertyChanged(); }
}
private void LogInfo(string log)
{
Console.WriteLine(log);
CreatePDFLog += Environment.NewLine + log;
} }
public async void ChooseFolder() public async void ChooseFolder()
@@ -37,10 +56,10 @@ class MainViewModel : BaseViewModel, IFontResolver
if (folders.Count == 1) if (folders.Count == 1)
{ {
var folder = folders[0]; var folder = folders[0];
Console.WriteLine("Chosen folder: " + folder.Path.LocalPath); LogInfo("Chosen folder: " + folder.Path.LocalPath);
if (Directory.Exists(folder.Path.LocalPath)) if (Directory.Exists(folder.Path.LocalPath))
{ {
CreatePDF(folder.Path.LocalPath); await Task.Run(() => CreatePDF(folder.Path.LocalPath));
} }
} }
} }
@@ -48,7 +67,7 @@ class MainViewModel : BaseViewModel, IFontResolver
public byte[]? GetFont(string faceName) public byte[]? GetFont(string faceName)
{ {
Console.WriteLine("Getting font {0}", faceName); LogInfo(string.Format("Getting font {0}", faceName));
if (faceName == "Noto Sans JP") if (faceName == "Noto Sans JP")
{ {
return File.ReadAllBytes(Path.Combine(_baseDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf")); return File.ReadAllBytes(Path.Combine(_baseDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf"));
@@ -62,7 +81,7 @@ class MainViewModel : BaseViewModel, IFontResolver
public FontResolverInfo? ResolveTypeface(string familyName, bool bold, bool italic) public FontResolverInfo? ResolveTypeface(string familyName, bool bold, bool italic)
{ {
Console.WriteLine("Resolving familyname {0}", familyName); LogInfo(string.Format("Resolving familyname {0}", familyName));
if (familyName == "Noto Sans JP") if (familyName == "Noto Sans JP")
{ {
if (bold) if (bold)
@@ -75,8 +94,11 @@ class MainViewModel : BaseViewModel, IFontResolver
} }
// https://forum.pdfsharp.net/viewtopic.php?f=2&t=1025 // https://forum.pdfsharp.net/viewtopic.php?f=2&t=1025
private void CreatePDF(string folderName) private async Task CreatePDF(string folderName)
{ {
// TODO: calculate needed width for images based on page width and margins and all that?
// TODO: resize (non-HEIC) images for smaller size...?
IsCreatingPDF = true;
var pdfDoc = new Document(); var pdfDoc = new Document();
var outputFileName = "MyReceipts.pdf"; var outputFileName = "MyReceipts.pdf";
var section = pdfDoc.AddSection(); var section = pdfDoc.AddSection();
@@ -109,7 +131,6 @@ class MainViewModel : BaseViewModel, IFontResolver
{ {
continue; continue;
} }
// TODO: calculate needed width for images based on page width and margins and all that?
var imageTitlePar = section.AddParagraph(); var imageTitlePar = section.AddParagraph();
imageTitlePar.Format.Alignment = ParagraphAlignment.Center; imageTitlePar.Format.Alignment = ParagraphAlignment.Center;
imageTitlePar.Format.Font.Size = 12; imageTitlePar.Format.Font.Size = 12;
@@ -133,16 +154,16 @@ class MainViewModel : BaseViewModel, IFontResolver
var outputPath = Path.Combine(convertedDir, info.Name + ".jpg"); var outputPath = Path.Combine(convertedDir, info.Name + ".jpg");
mImage.Quality = 80; mImage.Quality = 80;
mImage.Scale((uint)Math.Floor(mImage.Width * 0.5), (uint)Math.Floor(mImage.Height * 0.5)); mImage.Scale((uint)Math.Floor(mImage.Width * 0.5), (uint)Math.Floor(mImage.Height * 0.5));
mImage.Write(outputPath); await mImage.WriteAsync(outputPath);
fileName = Path.Combine("Converted", info.Name + ".jpg"); fileName = Path.Combine("Converted", info.Name + ".jpg");
Console.WriteLine("HEIC image fileName is now {0}", fileName); LogInfo(string.Format("Converted HEIC image to JPEG; fileName is now {0}", fileName));
} }
var paragraph = section.AddParagraph(); var paragraph = section.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center; paragraph.Format.Alignment = ParagraphAlignment.Center;
var image = paragraph.AddImage(fileName); var image = paragraph.AddImage(fileName);
image.LockAspectRatio = true; image.LockAspectRatio = true;
image.Width = 400; // can't be too wide now...not sure why...maybe due to margins... image.Width = 400; // can't be too wide now...not sure why...maybe due to margins...
Console.WriteLine("Added image: {0}", fileName); LogInfo(string.Format("Added image: {0}", fileName));
if (isPDF) if (isPDF)
{ {
// add other PDF pages // add other PDF pages
@@ -166,12 +187,18 @@ class MainViewModel : BaseViewModel, IFontResolver
section.AddPageBreak(); section.AddPageBreak();
} }
} }
var pdfRenderer = new PdfDocumentRenderer(); var pdfRenderer = new PdfDocumentRenderer
pdfRenderer.Document = pdfDoc; {
pdfRenderer.WorkingDirectory = folderName; Document = pdfDoc,
WorkingDirectory = folderName
};
LogInfo("Rendering document...");
pdfRenderer.RenderDocument(); pdfRenderer.RenderDocument();
string filename = Path.Join(folderName, outputFileName); string filename = Path.Join(folderName, outputFileName);
LogInfo("Saving document to disk...");
pdfRenderer.PdfDocument.Save(filename); pdfRenderer.PdfDocument.Save(filename);
Console.WriteLine(filename); LogInfo("PDF output to: " + filename);
IsCreatingPDF = false;
return;
} }
} }
+34 -3
View File
@@ -5,10 +5,41 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ReceiptPDFBuilder.Views.MainView" x:Class="ReceiptPDFBuilder.Views.MainView"
xmlns:vm="clr-namespace:ReceiptPDFBuilder.ViewModels" xmlns:vm="clr-namespace:ReceiptPDFBuilder.ViewModels"
xmlns:progRing="clr-namespace:AvaloniaProgressRing;assembly=AvaloniaProgressRing"
x:DataType="vm:MainViewModel"> x:DataType="vm:MainViewModel">
<Grid ColumnDefinitions="Auto, *"
RowDefinitions="Auto, *">
<Label Content="Easy Receipt Folder -> PDF Builder"
Grid.Row="0"
Grid.ColumnSpan="2"
FontWeight="Bold"
HorizontalAlignment="Center"/>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Spacing="2"> Spacing="2"
<Button Content="Choose Folder" Grid.Column="0"
Command="{Binding ChooseFolder}" /> Grid.Row="1"
Margin="2">
<Button Content="Choose Receipt Folder"
Command="{Binding ChooseFolder}"
IsEnabled="{Binding !IsCreatingPDF}" />
<Label Content="Creating PDF..."
IsVisible="{Binding IsCreatingPDF}"/>
<progRing:ProgressRing Width="80"
Height="80"
IsActive="{Binding IsCreatingPDF}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Foreground="LightBlue"
Margin="10,20,0,0"/>
</StackPanel> </StackPanel>
<ScrollViewer Grid.Column="1"
Margin="2"
Grid.Row="1"
x:Name="LogScrollView">
<SelectableTextBlock Text="{Binding CreatePDFLog}"
Margin="2"
TextWrapping="Wrap"
x:Name="LogBlock"/>
</ScrollViewer>
</Grid>
</UserControl> </UserControl>
+6
View File
@@ -9,6 +9,12 @@ namespace ReceiptPDFBuilder.Views
public MainView() public MainView()
{ {
this.InitializeComponent(); this.InitializeComponent();
LogBlock.PropertyChanged += LogBlock_PropertyChanged;
}
private void LogBlock_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
LogScrollView.ScrollToEnd();
} }
} }
} }