Compare commits

..

5 Commits

Author SHA1 Message Date
Elias Fierke
4cfbcd0ab4 [feat:] added Country implementation for country code editing (unused) 2026-01-01 14:45:57 +01:00
Elias Fierke
b82473ada2 [chore:] PdfBuilder.cs skips empty addresses 2025-12-21 11:43:41 +01:00
Elias Fierke
1cba67253a [chore:] selectable pdf file path for label creation 2025-12-21 11:32:38 +01:00
Elias Fierke
63c1559110 [chore:] added "Postfach" to address creation 2025-12-21 11:25:48 +01:00
Elias Fierke
b670ba11fa [chore:] sender address usage in PdfBuilder.cs 2025-12-15 10:17:10 +01:00
5 changed files with 347 additions and 31 deletions

View File

@@ -385,9 +385,79 @@
<Label FontSize="16" Content="Länder" VerticalContentAlignment="Center" /> <Label FontSize="16" Content="Länder" VerticalContentAlignment="Center" />
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
<StackPanel Orientation="Vertical"> <Grid ColumnDefinitions="*,*">
<Grid ColumnDefinitions="400,*" /> <Grid Grid.Column="0" Margin="5">
</StackPanel> <Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="CountryList" Grid.Row="0"
SelectionChanged="CountryList_OnSelectionChanged" />
<Grid Grid.Row="1" ColumnDefinitions="*,250" Margin="0,5,0,0">
<TextBox x:Name="TbSettingsNewCountry" />
<Button Grid.Column="1" x:Name="BtnSettingsNewCountry"
HorizontalAlignment="Stretch" Click="BtnSettingsNewCountry_OnClick"
HorizontalContentAlignment="Center" Content="+ Hinzufügen"
Margin="5,0,0,0" />
</Grid>
</Grid>
<Grid Grid.Column="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid ColumnDefinitions="300,*" Margin="0,0,0,5">
<TextBlock Text="Name:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" x:Name="TbSettingsCountryName"
TextChanged="TbSettingsCountryName_OnTextChanged" />
</Grid>
<Grid ColumnDefinitions="300,*" Margin="0,0,0,5" Grid.Row="1">
<TextBlock Text="Übersetzung:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" x:Name="TbSettingsCountryTranslation"
TextChanged="TbSettingsCountryTranslation_OnTextChanged" />
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="LbSettingsAlternatives" Grid.Row="0" SelectionMode="Multiple" />
<Grid Grid.Row="1" ColumnDefinitions="*,200" Margin="0,5,0,0">
<TextBox x:Name="TbSettingsNewCountryAlternative"
Watermark="Kürzel/Alternative" />
<Button Grid.Column="1" x:Name="BtnSettingsNewCountryAlternative"
Content="+ Hinzufügen" Margin="5 0 0 0"
HorizontalContentAlignment="Center"
Click="BtnSettingsNewCountryAlternative_OnClick"
HorizontalAlignment="Stretch" />
</Grid>
<Button x:Name="BtnSettingsRemoveSelectedAlternatives" Grid.Row="2"
Content="Ausgewählte Entfernen" Background="#99963434"
HorizontalContentAlignment="Center"
HorizontalAlignment="Stretch"
Click="BtnSettingsRemoveSelectedAlternatives_OnClick"
Margin="0,5,0,0" />
<Button Grid.Row="3"
Content="Land Entfernen" Background="#99963434"
HorizontalContentAlignment="Center" x:Name="BtnSettingsRemoveCountry"
HorizontalAlignment="Stretch" Click="BtnSettingsRemoveCountry_OnClick"
Margin="0,5,0,0" />
</Grid>
</Grid>
</Grid>
</TabItem> </TabItem>
</TabControl> </TabControl>
</TabItem> </TabItem>

View File

@@ -11,6 +11,9 @@ namespace Logof_Client;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
public static MainWindow _instance; public static MainWindow _instance;
private Country _selectedCountry;
private bool _suppressCountryRefresh = false;
public Uri filePath; public Uri filePath;
public MainWindow() public MainWindow()
@@ -26,6 +29,8 @@ public partial class MainWindow : Window
Global.Load(); Global.Load();
Settings.Load(); Settings.Load();
RefreshCountryView();
//Thread.Sleep(3000); //Thread.Sleep(3000);
//Show(); //Show();
} }
@@ -556,15 +561,26 @@ public partial class MainWindow : Window
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff"); StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff");
} }
private void BtnGenerateLabels_OnClick(object? sender, RoutedEventArgs e) private async void BtnGenerateLabels_OnClick(object? sender, RoutedEventArgs e)
{ {
var builder = new PdfBuilder(); var saveDialog = new SaveFileDialog
{
DefaultExtension = "pdf",
Filters = { new FileDialogFilter { Name = "PDF-Dateien", Extensions = { "pdf" } } }
};
var filePath = await saveDialog.ShowAsync(this);
builder.CreateAddressLabelPdfFromAddressSetWithPlaceholder( if (!string.IsNullOrEmpty(filePath))
Convert.ToInt32(LstCustomerAdressSets.SelectedItems[0].ToString().Split(" - ")[0]), {
"Company Logo/Info", var builder = new PdfBuilder();
"output.pdf"
); builder.CreateAddressLabelPdfFromAddressSetWithPlaceholder(
Convert.ToInt32(LstCustomerAdressSets.SelectedItems[0].ToString().Split(" - ")[0]),
"Company Logo/Info",
filePath
);
//return true;
}
} }
private void TbSettingsCustomerSenderAddress_OnTextChanged(object? sender, TextChangedEventArgs e) private void TbSettingsCustomerSenderAddress_OnTextChanged(object? sender, TextChangedEventArgs e)
@@ -574,4 +590,133 @@ public partial class MainWindow : Window
if (customer.ID == Settings._instance.customers.current) if (customer.ID == Settings._instance.customers.current)
customer.sender_address = TbSettingsCustomerSenderAddress.Text; customer.sender_address = TbSettingsCustomerSenderAddress.Text;
} }
// Country Section
private void BtnSettingsRemoveCountry_OnClick(object? sender, RoutedEventArgs e)
{
if (_selectedCountry != null)
{
Global._instance.countries.Remove(_selectedCountry);
_selectedCountry = null;
}
RefreshCountryView();
}
public void RefreshCountryView()
{
if (_suppressCountryRefresh) return;
try
{
_suppressCountryRefresh = true;
Global.Save();
var alt_index = LbSettingsAlternatives.SelectedIndex;
var country_index = CountryList.SelectedIndex;
LbSettingsAlternatives.Items.Clear();
try
{
CountryList.Items.Clear();
}
catch
{
}
foreach (var c in Global._instance.countries) CountryList.Items.Add(c.name);
try
{
CountryList.SelectedIndex = country_index;
}
catch
{
}
try
{
_selectedCountry = Country.GetByName(CountryList.SelectedItems[0].ToString());
}
catch
{
_selectedCountry = null;
}
if (_selectedCountry == null) return;
//Console.WriteLine("Refreshing alternatives...");
foreach (var a in _selectedCountry.alternatives)
{
LbSettingsAlternatives.Items.Add(a);
//Console.WriteLine(a);
}
TbSettingsCountryName.Text = _selectedCountry.name;
TbSettingsCountryTranslation.Text = _selectedCountry.translation;
try
{
LbSettingsAlternatives.SelectedIndex = alt_index;
}
catch
{
}
}
finally
{
_suppressCountryRefresh = false;
}
}
private void BtnSettingsRemoveSelectedAlternatives_OnClick(object? sender, RoutedEventArgs e)
{
foreach (var selected in LbSettingsAlternatives.SelectedItems)
try
{
_selectedCountry.alternatives.Remove(selected.ToString());
}
catch
{
Console.WriteLine("Error while removing country alternative");
}
RefreshCountryView();
}
private void BtnSettingsNewCountryAlternative_OnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(TbSettingsNewCountryAlternative.Text)) return;
_selectedCountry.alternatives.Add(TbSettingsNewCountryAlternative.Text.Trim());
RefreshCountryView();
}
private void TbSettingsCountryTranslation_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(TbSettingsCountryTranslation.Text))
{
_selectedCountry.translation = TbSettingsCountryTranslation.Text.Trim();
RefreshCountryView();
}
}
private void TbSettingsCountryName_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(TbSettingsCountryName.Text))
{
_selectedCountry.name = TbSettingsCountryName.Text.Trim();
RefreshCountryView();
}
}
private void BtnSettingsNewCountry_OnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(TbSettingsNewCountry.Text)) return;
Global._instance.countries.Add(new Country(TbSettingsNewCountry.Text));
RefreshCountryView();
}
private void CountryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
_selectedCountry = Country.GetByName(CountryList.SelectedItems[0].ToString());
RefreshCountryView();
}
} }

View File

@@ -62,6 +62,7 @@ public class Global
} }
public string config_path { get; set; } = ""; public string config_path { get; set; } = "";
public List<Country> countries { get; set; } = new();
public static void Save() public static void Save()
{ {
@@ -147,3 +148,37 @@ public class AddressSets
return null; return null;
} }
} }
public class Country
{
public Country(string name, string translation, List<string> alternatives)
{
this.name = name;
this.translation = translation;
this.alternatives = alternatives;
}
public Country(string name)
{
this.name = name;
translation = "";
alternatives = new List<string>();
}
public Country()
{
}
public string? name { get; set; }
public string translation { get; set; }
public List<string> alternatives { get; set; }
public static Country GetByName(string name)
{
foreach (var country in Global._instance.countries)
if (country.name == name)
return country;
return null;
}
}

View File

@@ -129,7 +129,7 @@ public static class AddressCreator
address_line_count++; address_line_count++;
if (!string.IsNullOrWhiteSpace(address.postfach)) if (!string.IsNullOrWhiteSpace(address.postfach))
{ {
string_address = address.postfach.Trim() + "\n" + string_address; string_address = "Postfach " + address.postfach.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }
else if (!string.IsNullOrWhiteSpace(address.strasse.Trim())) else if (!string.IsNullOrWhiteSpace(address.strasse.Trim()))
@@ -180,7 +180,7 @@ public static class AddressCreator
} }
else if (!string.IsNullOrWhiteSpace(address.postfach.Trim())) else if (!string.IsNullOrWhiteSpace(address.postfach.Trim()))
{ {
string_address = address.postfach.Trim() + "\n" + string_address; string_address = "Postfach " + address.postfach.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using PdfSharp; using PdfSharp;
using PdfSharp.Drawing; using PdfSharp.Drawing;
@@ -32,6 +33,7 @@ public class PdfBuilder
private readonly double _marginRight = 0; // mm private readonly double _marginRight = 0; // mm
private readonly double _marginTop = 0; // mm private readonly double _marginTop = 0; // mm
private readonly XFont _regularFont = new("Arial", 9, XFontStyleEx.Regular); private readonly XFont _regularFont = new("Arial", 9, XFontStyleEx.Regular);
private readonly XFont _smallFont = new("Arial", 6, XFontStyleEx.Regular);
/// <summary> /// <summary>
/// Creates a PDF document with address stickers from an AddressSet in a 3×7 grid layout on A4 pages. /// Creates a PDF document with address stickers from an AddressSet in a 3×7 grid layout on A4 pages.
@@ -49,9 +51,32 @@ public class PdfBuilder
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses"); throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set // Generate markdown addresses from all KasPersons in the set
var addresses = new string?[addressSet.KasPersons.Count]; //var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try
{
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id);
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address))
// ensure single line and wrap in a small-font tag
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n";
}
catch
{
senderLine = null;
}
for (var i = 0; i < addressSet.KasPersons.Count; i++) for (var i = 0; i < addressSet.KasPersons.Count; i++)
addresses[i] = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid); {
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid);
if (string.IsNullOrWhiteSpace(addr.Trim())) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + addr);
else
addresses.Add(addr);
}
CreateAddressLabelPdf(addresses, outputPath); CreateAddressLabelPdf(addresses, outputPath);
} }
@@ -74,9 +99,31 @@ public class PdfBuilder
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses"); throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set // Generate markdown addresses from all KasPersons in the set
var addresses = new string?[addressSet.KasPersons.Count]; //var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try
{
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id);
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address))
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n";
}
catch
{
senderLine = null;
}
for (var i = 0; i < addressSet.KasPersons.Count; i++) for (var i = 0; i < addressSet.KasPersons.Count; i++)
addresses[i] = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid); {
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid);
if (string.IsNullOrWhiteSpace(addr.Trim())) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + (addr ?? ""));
else
addresses.Add(addr);
}
CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath); CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath);
} }
@@ -86,15 +133,15 @@ public class PdfBuilder
/// </summary> /// </summary>
/// <param name="addresses">Array of addresses (from CreateFinalMarkdownString)</param> /// <param name="addresses">Array of addresses (from CreateFinalMarkdownString)</param>
/// <param name="outputPath">Path where the PDF should be saved</param> /// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdf(string?[] addresses, string outputPath) public void CreateAddressLabelPdf(List<string> addresses, string outputPath)
{ {
if (addresses == null || addresses.Length == 0) if (addresses == null || addresses.Count == 0)
throw new ArgumentException("Addresses array cannot be null or empty"); throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument(); var document = new PdfDocument();
var addressIndex = 0; var addressIndex = 0;
while (addressIndex < addresses.Length) while (addressIndex < addresses.Count)
{ {
var page = document.AddPage(); var page = document.AddPage();
page.Size = PageSize.A4; page.Size = PageSize.A4;
@@ -116,9 +163,9 @@ public class PdfBuilder
/// <param name="addresses">Array of addresses</param> /// <param name="addresses">Array of addresses</param>
/// <param name="placeholderText">Text for the first cell (top-left)</param> /// <param name="placeholderText">Text for the first cell (top-left)</param>
/// <param name="outputPath">Path where the PDF should be saved</param> /// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfWithPlaceholder(string?[] addresses, string placeholderText, string outputPath) public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath)
{ {
if (addresses == null || addresses.Length == 0) if (addresses == null || addresses.Count == 0)
throw new ArgumentException("Addresses array cannot be null or empty"); throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument(); var document = new PdfDocument();
@@ -126,7 +173,7 @@ public class PdfBuilder
var addressIndex = 0; var addressIndex = 0;
var isFirstCell = true; var isFirstCell = true;
while (addressIndex < addresses.Length || isFirstCell) while (addressIndex < addresses.Count || isFirstCell)
{ {
var page = document.AddPage(); var page = document.AddPage();
page.Size = PageSize.A4; page.Size = PageSize.A4;
@@ -140,7 +187,7 @@ public class PdfBuilder
document.Save(outputPath); document.Save(outputPath);
} }
private void DrawPage(XGraphics gfx, string?[] addresses, ref int addressIndex) private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex)
{ {
var cellCount = 0; var cellCount = 0;
@@ -148,7 +195,7 @@ public class PdfBuilder
{ {
for (var col = 0; col < 3; col++) for (var col = 0; col < 3; col++)
{ {
if (addressIndex >= addresses.Length) break; if (addressIndex >= addresses.Count) break;
var x = MmToPoints(_marginLeft + col * _cellWidth); var x = MmToPoints(_marginLeft + col * _cellWidth);
var y = MmToPoints(_marginTop + row * _cellHeight); var y = MmToPoints(_marginTop + row * _cellHeight);
@@ -158,11 +205,11 @@ public class PdfBuilder
cellCount++; cellCount++;
} }
if (addressIndex >= addresses.Length) break; if (addressIndex >= addresses.Count) break;
} }
} }
private void DrawPageWithPlaceholder(XGraphics gfx, string?[] addresses, ref int addressIndex, private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex,
ref bool isFirstCell, string placeholderText) ref bool isFirstCell, string placeholderText)
{ {
var cellCount = 0; var cellCount = 0;
@@ -179,7 +226,7 @@ public class PdfBuilder
DrawCell(gfx, x, y, placeholderText); DrawCell(gfx, x, y, placeholderText);
isFirstCell = false; isFirstCell = false;
} }
else if (addressIndex < addresses.Length) else if (addressIndex < addresses.Count)
{ {
DrawCell(gfx, x, y, addresses[addressIndex]); DrawCell(gfx, x, y, addresses[addressIndex]);
addressIndex++; addressIndex++;
@@ -233,10 +280,8 @@ public class PdfBuilder
// Calculate total height of the text block // Calculate total height of the text block
var totalHeight = lines.Length * lineHeight; var totalHeight = lines.Length * lineHeight;
// Start drawing from the lower-left corner of the cell // Start drawing from the top of the cell (align addresses to top)
var startY = y + cellHeight - paddingBottomPoints - totalHeight; var startY = y + paddingTopPoints;
if (startY < y + paddingTopPoints)
startY = y + paddingTopPoints; // don't overflow top padding
var currentY = startY; var currentY = startY;
@@ -259,6 +304,27 @@ public class PdfBuilder
while (i < line.Length) while (i < line.Length)
{ {
// Check for small-font tag <font6> ... </font6>
if (i <= line.Length - 7 && line.Substring(i, 7) == "<font6>")
{
var endTag = line.IndexOf("</font6>", i + 7, StringComparison.Ordinal);
if (endTag != -1)
{
var inner = line.Substring(i + 7, endTag - (i + 7));
if (!string.IsNullOrEmpty(inner))
{
gfx.DrawString(inner, _smallFont, XBrushes.Black,
new XRect(currentX, y, maxWidth - (currentX - x), _smallFont.Size * 1.2),
XStringFormats.TopLeft);
var measuredSmall = gfx.MeasureString(inner, _smallFont);
currentX += measuredSmall.Width;
}
i = endTag + 8; // move past </font6>
continue;
}
}
// Check for bold marker (**) // Check for bold marker (**)
if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*') if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*')
{ {