Save and load report PDF data to json

This commit is contained in:
2026-02-16 18:39:19 +09:00
parent 833e97fbea
commit b4d09c0d8b
5 changed files with 131 additions and 30 deletions
+18
View File
@@ -0,0 +1,18 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ReceiptPDFBuilders.Helpers;
class Utilities
{
public static JsonSerializerOptions GetSerializerOptions()
{
var opts = new JsonSerializerOptions
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
return opts;
}
}
+21 -5
View File
@@ -12,14 +12,18 @@ namespace ReceiptPDFBuilder.Models;
class PDFReport : ChangeNotifier class PDFReport : ChangeNotifier
{ {
private string _baseFolder; private string _baseFolder;
private string _name; private string _title;
private List<ReportFile> _files; private List<ReportFile> _files;
private DateTime _lastSaved;
private DateTime? _lastGenerated;
public PDFReport() public PDFReport()
{ {
_baseFolder = ""; _baseFolder = "";
_name = ""; _title = "";
_files = []; _files = [];
_lastSaved = DateTime.Now;
_lastGenerated = null;
} }
public string BaseFolder public string BaseFolder
@@ -28,10 +32,10 @@ class PDFReport : ChangeNotifier
set { _baseFolder = value; NotifyPropertyChanged(); } set { _baseFolder = value; NotifyPropertyChanged(); }
} }
public string Name public string Title
{ {
get => _name; get => _title;
set { _name = value; NotifyPropertyChanged(); } set { _title = value; NotifyPropertyChanged(); }
} }
public List<ReportFile> Files public List<ReportFile> Files
@@ -39,4 +43,16 @@ class PDFReport : ChangeNotifier
get => _files; get => _files;
set { _files = value; NotifyPropertyChanged(); } set { _files = value; NotifyPropertyChanged(); }
} }
public DateTime LastSaved
{
get => _lastSaved;
set { _lastSaved = value; NotifyPropertyChanged(); }
}
public DateTime? LastGenerated
{
get => _lastGenerated;
set { _lastGenerated = value; NotifyPropertyChanged(); }
}
} }
+4 -13
View File
@@ -5,6 +5,7 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using ReceiptPDFBuilder.Helpers; using ReceiptPDFBuilder.Helpers;
using ReceiptPDFBuilders.Helpers;
namespace ReceiptPDFBuilder.Models; namespace ReceiptPDFBuilder.Models;
@@ -42,19 +43,9 @@ class Settings : ChangeNotifier
return Path.Combine(path, GetSettingsFileName()); return Path.Combine(path, GetSettingsFileName());
} }
private static JsonSerializerOptions GetSerializerOptions()
{
var opts = new JsonSerializerOptions
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
return opts;
}
public async Task<string> SaveSettingsAsync() public async Task<string> SaveSettingsAsync()
{ {
var jsonContext = new SourceGenerationContext(GetSerializerOptions()); var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
using MemoryStream memoryStream = new MemoryStream(); using MemoryStream memoryStream = new MemoryStream();
await JsonSerializer.SerializeAsync(memoryStream, this, jsonContext.Settings); await JsonSerializer.SerializeAsync(memoryStream, this, jsonContext.Settings);
memoryStream.Position = 0; memoryStream.Position = 0;
@@ -72,14 +63,14 @@ class Settings : ChangeNotifier
return new Settings(); return new Settings();
} }
var json = File.ReadAllText(GetSettingsPath()); var json = File.ReadAllText(GetSettingsPath());
var jsonContext = new SourceGenerationContext(GetSerializerOptions()); var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
return JsonSerializer.Deserialize<Settings>(json, jsonContext.Settings) ?? new Settings(); return JsonSerializer.Deserialize<Settings>(json, jsonContext.Settings) ?? new Settings();
} }
public static async Task<Settings> LoadSettingsAsync() public static async Task<Settings> LoadSettingsAsync()
{ {
using FileStream fileStream = File.OpenRead(GetSettingsPath()); using FileStream fileStream = File.OpenRead(GetSettingsPath());
var jsonContext = new SourceGenerationContext(GetSerializerOptions()); var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
var output = await JsonSerializer.DeserializeAsync<Settings>(fileStream, jsonContext.Settings) ?? new Settings(); var output = await JsonSerializer.DeserializeAsync<Settings>(fileStream, jsonContext.Settings) ?? new Settings();
return output; return output;
} }
+79 -7
View File
@@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Themes.Fluent; using Avalonia.Themes.Fluent;
@@ -15,32 +16,36 @@ using MigraDoc.Rendering;
using PdfSharp.Fonts; using PdfSharp.Fonts;
using PdfSharp.Pdf.IO; using PdfSharp.Pdf.IO;
using PdfSharp.Snippets.Font; using PdfSharp.Snippets.Font;
using ReceiptPDFBuilder.Helpers;
using ReceiptPDFBuilder.Interfaces; using ReceiptPDFBuilder.Interfaces;
using ReceiptPDFBuilder.Models; using ReceiptPDFBuilder.Models;
using ReceiptPDFBuilders.Helpers;
namespace ReceiptPDFBuilder.ViewModels; namespace ReceiptPDFBuilder.ViewModels;
class MainViewModel : BaseViewModel, IFontResolver class MainViewModel : BaseViewModel, IFontResolver
{ {
private string _baseDir; private string _processDir;
private bool _isCreatingPDF; private bool _isCreatingPDF;
private string _createPDFLog; private string _createPDFLog;
private string _workingFolder; private string _workingFolder;
private string _reportTitle; private string _reportTitle;
private ObservableCollection<ReportFile> _reportFiles; private ObservableCollection<ReportFile> _reportFiles;
private DateTime? _lastGeneratedTime;
private Settings _settings; private Settings _settings;
public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger)
{ {
_baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? ""; _processDir = Path.GetDirectoryName(Environment.ProcessPath) ?? "";
_isCreatingPDF = false; _isCreatingPDF = false;
_createPDFLog = "Ready to create PDF! Choose a folder to begin..."; _createPDFLog = "Ready to create PDF! Choose a folder to begin...";
_workingFolder = ""; _workingFolder = "";
_reportFiles = new ObservableCollection<ReportFile>(); _reportFiles = new ObservableCollection<ReportFile>();
_reportFiles.CollectionChanged += ( sender, e ) => { NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); }; _reportFiles.CollectionChanged += ( sender, e ) => { NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); };
_reportTitle = ""; _reportTitle = "";
_lastGeneratedTime = null;
_settings = Settings.LoadSettings(); _settings = Settings.LoadSettings();
if (!string.IsNullOrWhiteSpace(_settings.LastUsedPath)) if (!string.IsNullOrWhiteSpace(_settings.LastUsedPath))
{ {
@@ -118,6 +123,26 @@ class MainViewModel : BaseViewModel, IFontResolver
_workingFolder = path; _workingFolder = path;
NotifyPropertyChanged(nameof(IsTitleBoxVisible)); NotifyPropertyChanged(nameof(IsTitleBoxVisible));
// TODO: Scan folder for saved info from previous reports and reload that first // TODO: Scan folder for saved info from previous reports and reload that first
var reportFilePath = Path.Combine(path, GetReportSavedDataFileName());
var successfullyLoadedPriorReport = false;
if (File.Exists(reportFilePath))
{
// load prior report
var json = File.ReadAllText(reportFilePath);
var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
var report = JsonSerializer.Deserialize<PDFReport>(json, jsonContext.PDFReport);
if (report != null && report.Files.Count > 0)
{
ReportFiles = new ObservableCollection<ReportFile>(report.Files);
ReportTitle = report.Title;
_workingFolder = report.BaseFolder;
_lastGeneratedTime = report.LastGenerated ?? null;
LogInfo("Reloaded report last saved at {0}", report.LastSaved);
successfullyLoadedPriorReport = true;
}
}
if (!successfullyLoadedPriorReport)
{
// Scan folder for files and display in DataGrid // Scan folder for files and display in DataGrid
var filePaths = Directory.GetFiles(_workingFolder); var filePaths = Directory.GetFiles(_workingFolder);
filePaths.Sort(); filePaths.Sort();
@@ -127,6 +152,7 @@ class MainViewModel : BaseViewModel, IFontResolver
} }
} }
} }
}
public async void RemoveFile(ReportFile file) public async void RemoveFile(ReportFile file)
{ {
@@ -235,26 +261,71 @@ class MainViewModel : BaseViewModel, IFontResolver
} }
} }
public async void SaveInterimReportInfo()
{
var report = new PDFReport()
{
Title = ReportTitle,
Files = ReportFiles.ToList(),
BaseFolder = _workingFolder,
LastSaved = DateTime.Now,
LastGenerated = _lastGeneratedTime,
};
await SavePDFReportDataToDisk(report);
}
private async Task SavePDFReportDataToDisk(PDFReport report)
{
var jsonContext = new SourceGenerationContext(Utilities.GetSerializerOptions());
using var memoryStream = new MemoryStream();
await JsonSerializer.SerializeAsync(memoryStream, report, jsonContext.PDFReport);
memoryStream.Position = 0;
using var reader = new StreamReader(memoryStream);
var json = await reader.ReadToEndAsync();
var savePath = Path.Combine(_workingFolder, GetReportSavedDataFileName());
await File.WriteAllTextAsync(savePath, json);
LogInfo("Saved report information to {0} (baseDir is {1})", savePath, _workingFolder);
}
private async Task CreateAndSaveReportObjectAfterReportCreation()
{
var report = new PDFReport()
{
Title = ReportTitle,
Files = ReportFiles.ToList(),
BaseFolder = _workingFolder,
LastSaved = DateTime.Now,
LastGenerated = DateTime.Now,
};
_lastGeneratedTime = DateTime.Now;
await SavePDFReportDataToDisk(report);
}
private string GetReportSavedDataFileName()
{
return "report_data.json";
}
public byte[]? GetFont(string faceName) public byte[]? GetFont(string faceName)
{ {
LogInfo(string.Format("Loading font {0}", faceName)); LogInfo(string.Format("Loading font {0}", faceName));
if (faceName == "Noto Sans JP") if (faceName == "Noto Sans JP")
{ {
var path = Path.Combine(_baseDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf"); var path = Path.Combine(_processDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf");
if (!File.Exists(path)) if (!File.Exists(path))
{ {
path = Path.Combine(_baseDir, "../Resources/Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf"); path = Path.Combine(_processDir, "../Resources/Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf");
} }
return File.ReadAllBytes(path); return File.ReadAllBytes(path);
} }
if (faceName == "Noto Sans JP Bold") if (faceName == "Noto Sans JP Bold")
{ {
var path = Path.Combine(_baseDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-SemiBold.ttf"); var path = Path.Combine(_processDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-SemiBold.ttf");
if (!File.Exists(path)) if (!File.Exists(path))
{ {
path = Path.Combine(_baseDir, "../Resources/Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-SemiBold.ttf"); path = Path.Combine(_processDir, "../Resources/Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-SemiBold.ttf");
} }
return File.ReadAllBytes(Path.Combine(_baseDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-SemiBold.ttf")); return File.ReadAllBytes(Path.Combine(_processDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-SemiBold.ttf"));
} }
return null; return null;
} }
@@ -399,6 +470,7 @@ class MainViewModel : BaseViewModel, IFontResolver
LogInfo("Saving document to disk..."); LogInfo("Saving document to disk...");
pdfRenderer.PdfDocument.Save(filename); pdfRenderer.PdfDocument.Save(filename);
LogInfo("Saved PDF output to: " + filename); LogInfo("Saved PDF output to: " + filename);
await CreateAndSaveReportObjectAfterReportCreation();
IsCreatingPDF = false; IsCreatingPDF = false;
return; return;
} }
+4
View File
@@ -129,6 +129,10 @@
IsEnabled="{Binding IsTitleBoxVisible}"> IsEnabled="{Binding IsTitleBoxVisible}">
<TextBlock><Run Text="&#x002b;" FontFamily="{StaticResource FontAwesomeSolid}"/> Add Item</TextBlock> <TextBlock><Run Text="&#x002b;" FontFamily="{StaticResource FontAwesomeSolid}"/> Add Item</TextBlock>
</Button> </Button>
<Button Command="{Binding SaveInterimReportInfo}"
IsEnabled="{Binding IsTitleBoxVisible}">
<TextBlock><Run Text="&#xf0c7;" FontFamily="{StaticResource FontAwesomeSolid}"/> Save Report Info</TextBlock>
</Button>
<Button Content="Create Report PDF" <Button Content="Create Report PDF"
Command="{Binding BuildPDF}" Command="{Binding BuildPDF}"
Classes="accent" Classes="accent"