Compare commits
17 Commits
8e5709c215
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cfbcd0ab4 | ||
|
|
b82473ada2 | ||
|
|
1cba67253a | ||
|
|
63c1559110 | ||
|
|
b670ba11fa | ||
|
|
1ad57543d1 | ||
|
|
6cd4ea2df6 | ||
|
|
30e42afe35 | ||
|
|
70e127b2f0 | ||
|
|
7c73170b46 | ||
|
|
290f69e976 | ||
|
|
6ce08d7d4a | ||
|
|
86df6f6a63 | ||
|
|
eae0568ae0 | ||
|
|
4ebd6bc407 | ||
|
|
2c22306fef | ||
|
|
174223ba9e |
@@ -177,4 +177,14 @@ public class KasPersonError
|
||||
//public int refsid { get; set; }
|
||||
public List<AddressCheck.ErrorTypes> errors { get; set; } = new();
|
||||
public List<AddressCheck.WarningTypes> warnings { get; set; } = new();
|
||||
|
||||
public string GetString()
|
||||
{
|
||||
var output = "";
|
||||
foreach (var error in errors) output += error + ", ";
|
||||
|
||||
foreach (var warning in warnings) output += warning + ", ";
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Lucide.Avalonia" Version="0.1.35"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
|
||||
<PackageReference Include="PdfSharp" Version="6.1.1"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
114
MainWindow.axaml
114
MainWindow.axaml
@@ -135,6 +135,7 @@
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button HorizontalAlignment="Stretch" IsEnabled="False"
|
||||
Click="BtnGenerateLabels_OnClick"
|
||||
HorizontalContentAlignment="Center" x:Name="BtnGenerateLabels"
|
||||
Margin="0,0,0,10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@@ -160,10 +161,12 @@
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<LucideIcon Kind="SquaresUnite" Width="36" Height="36" />
|
||||
<Label Content="Vereinigung" VerticalContentAlignment="Center" FontSize="15"
|
||||
<Label Content="Vereinigung" VerticalContentAlignment="Center"
|
||||
FontSize="15"
|
||||
FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
<Label FontSize="9" Content="Fügt Elemente beider Mengen in eine Menge zusammen"></Label>
|
||||
<Label FontSize="9"
|
||||
Content="Fügt Elemente beider Mengen in eine Menge zusammen" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button HorizontalAlignment="Stretch" MinWidth="240"
|
||||
@@ -177,7 +180,8 @@
|
||||
FontSize="15"
|
||||
FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
<Label FontSize="9" Content="Überträgt nur doppelte Elemente in die neue Menge"></Label>
|
||||
<Label FontSize="9"
|
||||
Content="Überträgt nur doppelte Elemente in die neue Menge" />
|
||||
</StackPanel>
|
||||
|
||||
</Button>
|
||||
@@ -192,7 +196,8 @@
|
||||
FontSize="15"
|
||||
FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
<Label FontSize="9" Content="Elemente der ersten Menge ohne Elemente der zweiten Menge"></Label>
|
||||
<Label FontSize="9"
|
||||
Content="Elemente der ersten Menge ohne Elemente der zweiten Menge" />
|
||||
</StackPanel>
|
||||
|
||||
</Button>
|
||||
@@ -203,11 +208,12 @@
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<LucideIcon Kind="SquaresExclude" Width="36" Height="36" />
|
||||
<Label Content="Symmetrische Differenz" VerticalContentAlignment="Center"
|
||||
<Label Content="Symmetrische Differenz"
|
||||
VerticalContentAlignment="Center"
|
||||
FontSize="15"
|
||||
FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
<Label FontSize="9" Content="Nur Elemente, die NICHT doppelt sind"></Label>
|
||||
<Label FontSize="9" Content="Nur Elemente, die NICHT doppelt sind" />
|
||||
</StackPanel>
|
||||
|
||||
</Button>
|
||||
@@ -241,6 +247,14 @@
|
||||
</StackPanel>
|
||||
</TabItem.Header>
|
||||
</TabItem>
|
||||
<TabItem IsEnabled="False">
|
||||
<TabItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<LucideIcon Kind="LibraryBig" Width="32" Height="32" Size="32" />
|
||||
<Label FontSize="20" Content="Wiki" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TabItem.Header>
|
||||
</TabItem>
|
||||
<TabItem IsEnabled="True">
|
||||
<TabItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@@ -315,6 +329,13 @@
|
||||
TextChanged="TbSettingsCustomerDescription_OnTextChanged"
|
||||
x:Name="TbSettingsCustomerDescription" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="150,*">
|
||||
<Label Content="Absenderadresse" />
|
||||
<TextBox Grid.Column="1" Watermark="Absenderadresse"
|
||||
HorizontalAlignment="Stretch"
|
||||
TextChanged="TbSettingsCustomerSenderAddress_OnTextChanged"
|
||||
x:Name="TbSettingsCustomerSenderAddress" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="150,*">
|
||||
<Label Content="CSV-Trennzeichen" />
|
||||
<TextBox Grid.Column="1" Watermark=","
|
||||
@@ -357,6 +378,87 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<LucideIcon Kind="Globe" Width="16" Height="16" Size="16" />
|
||||
<Label FontSize="16" Content="Länder" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
</TabItem.Header>
|
||||
<Grid ColumnDefinitions="*,*">
|
||||
<Grid Grid.Column="0" Margin="5">
|
||||
<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>
|
||||
</TabControl>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace Logof_Client;
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public static MainWindow _instance;
|
||||
|
||||
private Country _selectedCountry;
|
||||
private bool _suppressCountryRefresh = false;
|
||||
public Uri filePath;
|
||||
|
||||
public MainWindow()
|
||||
@@ -26,6 +29,8 @@ public partial class MainWindow : Window
|
||||
Global.Load();
|
||||
Settings.Load();
|
||||
|
||||
RefreshCountryView();
|
||||
|
||||
//Thread.Sleep(3000);
|
||||
//Show();
|
||||
}
|
||||
@@ -249,13 +254,13 @@ public partial class MainWindow : Window
|
||||
var processor = new CombineAddresses(progressWindow);
|
||||
var result = await processor.Perform(address_lists, type, CbMergeExportUnmerged.IsChecked);
|
||||
|
||||
if(result.Item1 != null)
|
||||
if (result.Item1 != null)
|
||||
result.Item1.owner_id = owner_id;
|
||||
if(result.Item2 != null)
|
||||
if (result.Item2 != null)
|
||||
result.Item2.owner_id = owner_id;
|
||||
if(result.Item1 != null)
|
||||
if (result.Item1 != null)
|
||||
Settings._instance.addressSets.addresses.Add(result.Item1);
|
||||
if(result.Item2 != null)
|
||||
if (result.Item2 != null)
|
||||
Settings._instance.addressSets.addresses.Add(result.Item2);
|
||||
Settings.Save();
|
||||
progressWindow.Close();
|
||||
@@ -303,6 +308,7 @@ public partial class MainWindow : Window
|
||||
{
|
||||
TbSettingsCustomerDescription.Text = customer.description;
|
||||
TbSettingsCustomerName.Text = customer.name;
|
||||
TbSettingsCustomerSenderAddress.Text = customer.sender_address;
|
||||
TbSettingsCustomerCsvSeparator.Text = customer.separator.ToString();
|
||||
if (customer.patch != null)
|
||||
TbSettingsCustomerPatchInfo.Text = customer.patch.ToString();
|
||||
@@ -457,8 +463,8 @@ public partial class MainWindow : Window
|
||||
{
|
||||
BtnCheck.IsEnabled = true;
|
||||
BtnCombine.IsEnabled = true;
|
||||
BtnGenerateLabels.IsEnabled = true;
|
||||
|
||||
// BtnGenerateLabels.IsEnabled = true;
|
||||
// BtnRepair.IsEnabled = true;
|
||||
// BtnShorten.IsEnabled = true;
|
||||
}
|
||||
@@ -554,4 +560,163 @@ public partial class MainWindow : Window
|
||||
|
||||
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff");
|
||||
}
|
||||
|
||||
private async void BtnGenerateLabels_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var saveDialog = new SaveFileDialog
|
||||
{
|
||||
DefaultExtension = "pdf",
|
||||
Filters = { new FileDialogFilter { Name = "PDF-Dateien", Extensions = { "pdf" } } }
|
||||
};
|
||||
var filePath = await saveDialog.ShowAsync(this);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
var builder = new PdfBuilder();
|
||||
|
||||
builder.CreateAddressLabelPdfFromAddressSetWithPlaceholder(
|
||||
Convert.ToInt32(LstCustomerAdressSets.SelectedItems[0].ToString().Split(" - ")[0]),
|
||||
"Company Logo/Info",
|
||||
filePath
|
||||
);
|
||||
//return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void TbSettingsCustomerSenderAddress_OnTextChanged(object? sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (LstSettingsCustomers.SelectedIndex == null || LstSettingsCustomers.SelectedIndex == -1) return;
|
||||
foreach (var customer in Settings._instance.customers.customers)
|
||||
if (customer.ID == Settings._instance.customers.current)
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@
|
||||
x:Name="BtnShowSelected" Click="BtnShowSelected_OnClick"
|
||||
Margin="10,10,10,10" />
|
||||
</Grid>
|
||||
<DataGrid x:Name="DgResult" Grid.Column="1" AutoGenerateColumns="True" />
|
||||
<ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel x:Name="StkResults" Orientation="Vertical" Margin="10" />
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -5,6 +5,7 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Logof_Client;
|
||||
|
||||
@@ -30,7 +31,43 @@ public partial class ResultWindow : Window
|
||||
// Filter to only show persons with errors
|
||||
var result_with_errors = result.Where(p => p.PersonError != null).ToList();
|
||||
LblResultCount.Content = $"{result_with_errors.Count}/{ur_result.Count} Ergebnisse";
|
||||
DgResult.ItemsSource = result_with_errors;
|
||||
|
||||
StkResults.Children.Clear();
|
||||
foreach (var person in result_with_errors) StkResults.Children.Add(CreatePersonGrid(person));
|
||||
}
|
||||
|
||||
private Grid CreatePersonGrid(KasPerson person)
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnDefinitions = ColumnDefinitions.Parse("100,*,100,*,100,*"),
|
||||
RowDefinitions = RowDefinitions.Parse("Auto"),
|
||||
Margin = new Thickness(0, 5, 0, 5),
|
||||
Background = new SolidColorBrush(Color.Parse("#F0F0F0"))
|
||||
};
|
||||
|
||||
// Refsid
|
||||
grid.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "refsid: ",
|
||||
FontWeight = FontWeight.Bold, Margin = new Thickness(5)
|
||||
});
|
||||
grid.Children.Add(new TextBlock { Text = person.refsid.ToString(), Margin = new Thickness(5) });
|
||||
Grid.SetColumn(grid.Children[1], 1);
|
||||
|
||||
// PLZ
|
||||
grid.Children.Add(new TextBlock { Text = "plz:", FontWeight = FontWeight.Bold, Margin = new Thickness(5) });
|
||||
Grid.SetColumn(grid.Children[2], 2);
|
||||
grid.Children.Add(new TextBlock { Text = person.plz, Margin = new Thickness(5) });
|
||||
Grid.SetColumn(grid.Children[3], 3);
|
||||
|
||||
// PPLZ
|
||||
grid.Children.Add(new TextBlock { Text = "errors:", FontWeight = FontWeight.Bold, Margin = new Thickness(5) });
|
||||
Grid.SetColumn(grid.Children[4], 4);
|
||||
grid.Children.Add(new TextBlock { Text = person.PersonError.GetString(), Margin = new Thickness(5) });
|
||||
Grid.SetColumn(grid.Children[5], 5);
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
private void ViewSingle(int refsid)
|
||||
@@ -112,46 +149,74 @@ public partial class ResultWindow : Window
|
||||
private void UpdateFilter()
|
||||
{
|
||||
var temp_result = new List<KasPerson>();
|
||||
var checked_types = new List<AddressCheck.ErrorTypes>();
|
||||
var checked_types_war = new List<AddressCheck.WarningTypes>();
|
||||
|
||||
var checkedErrors = new HashSet<AddressCheck.ErrorTypes>();
|
||||
var checkedWarnings = new HashSet<AddressCheck.WarningTypes>();
|
||||
|
||||
// safer parsing: use TryParse and trim the Content string
|
||||
foreach (var cb in errortypecheckboxes)
|
||||
if (cb.IsChecked == true)
|
||||
checked_types.Add(
|
||||
(AddressCheck.ErrorTypes)Enum.Parse(typeof(AddressCheck.ErrorTypes), cb.Content.ToString()));
|
||||
{
|
||||
var s = cb.Content?.ToString()?.Trim();
|
||||
if (!string.IsNullOrEmpty(s) &&
|
||||
Enum.TryParse<AddressCheck.ErrorTypes>(s, true, out var et))
|
||||
checkedErrors.Add(et);
|
||||
}
|
||||
|
||||
foreach (var cb in warningtypecheckboxes)
|
||||
if (cb.IsChecked == true)
|
||||
checked_types_war.Add(
|
||||
(AddressCheck.WarningTypes)Enum.Parse(typeof(AddressCheck.WarningTypes), cb.Content.ToString()));
|
||||
{
|
||||
var s = cb.Content?.ToString()?.Trim();
|
||||
if (!string.IsNullOrEmpty(s) &&
|
||||
Enum.TryParse<AddressCheck.WarningTypes>(s, true, out var wt))
|
||||
checkedWarnings.Add(wt);
|
||||
}
|
||||
|
||||
// If no checkboxes are selected, show all persons with errors (default behavior)
|
||||
if (checkedErrors.Count == 0 && checkedWarnings.Count == 0)
|
||||
temp_result = ur_result.Where(p => p.PersonError != null).ToList();
|
||||
else
|
||||
foreach (var person in ur_result)
|
||||
{
|
||||
if (person.PersonError == null) continue;
|
||||
|
||||
foreach (var err in person.PersonError.errors)
|
||||
if (checked_types.Contains(err) && !temp_result.Contains(person))
|
||||
temp_result.Add(person);
|
||||
var personErrors = person.PersonError.errors ?? Enumerable.Empty<AddressCheck.ErrorTypes>();
|
||||
var personWarnings = person.PersonError.warnings ?? Enumerable.Empty<AddressCheck.WarningTypes>();
|
||||
|
||||
foreach (var war in person.PersonError.warnings)
|
||||
if (checked_types_war.Contains(war) && !temp_result.Contains(person))
|
||||
var matchesError = false;
|
||||
var matchesWarning = false;
|
||||
|
||||
// only test errors if the user selected error-types
|
||||
if (checkedErrors.Count > 0)
|
||||
matchesError = personErrors.Any(err => checkedErrors.Contains(err));
|
||||
|
||||
// only test warnings if the user selected warning-types
|
||||
if (checkedWarnings.Count > 0)
|
||||
matchesWarning = personWarnings.Any(war => checkedWarnings.Contains(war));
|
||||
|
||||
// If at least one category matches (OR across categories), include person
|
||||
if ((matchesError || matchesWarning) && !temp_result.Contains(person))
|
||||
temp_result.Add(person);
|
||||
}
|
||||
|
||||
LblResultCount.Content = $"{temp_result.Count}/{ur_result.Count} Ergebnisse";
|
||||
DgResult.ItemsSource = temp_result;
|
||||
|
||||
StkResults.Children.Clear();
|
||||
foreach (var person in temp_result) StkResults.Children.Add(CreatePersonGrid(person));
|
||||
}
|
||||
|
||||
|
||||
private void BtnShowSelected_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (var selected in DgResult.SelectedItems)
|
||||
try
|
||||
{
|
||||
var _asKas = (KasPerson)selected;
|
||||
ViewSingle(_asKas.refsid);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
// foreach (var selected in DgResult.SelectedItems)
|
||||
// try
|
||||
// {
|
||||
// var _asKas = (KasPerson)selected;
|
||||
// ViewSingle(_asKas.refsid);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Console.WriteLine(ex.Message);
|
||||
// }
|
||||
}
|
||||
}
|
||||
37
Settings.cs
37
Settings.cs
@@ -62,6 +62,7 @@ public class Global
|
||||
}
|
||||
|
||||
public string config_path { get; set; } = "";
|
||||
public List<Country> countries { get; set; } = new();
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
@@ -121,10 +122,12 @@ public class Customer
|
||||
|
||||
public string name { get; set; } = "";
|
||||
public string description { get; set; } = "";
|
||||
public string sender_address { get; set; } = "";
|
||||
public AddressPatch patch { get; set; }
|
||||
public char separator { get; set; } = ',';
|
||||
public int ID { get; }
|
||||
|
||||
|
||||
public static int GetIDByCustomerListItem(string item_content)
|
||||
{
|
||||
var id = item_content.Split(" - ")[0];
|
||||
@@ -145,3 +148,37 @@ public class AddressSets
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,17 @@ public static class AddressCreator
|
||||
// Maximum seven lines of information
|
||||
|
||||
// find the address
|
||||
var address = new KasPerson();
|
||||
KasPerson? address = null;
|
||||
var string_address = "";
|
||||
var address_line_count = 0;
|
||||
foreach (var set in Settings._instance.addressSets.addresses)
|
||||
{
|
||||
var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid);
|
||||
if (temp != null) break;
|
||||
if (temp != null)
|
||||
{
|
||||
address = temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no address found
|
||||
@@ -91,13 +95,17 @@ public static class AddressCreator
|
||||
// Maximum seven lines of information
|
||||
|
||||
// find the address
|
||||
var address = new KasPerson();
|
||||
KasPerson? address = null;
|
||||
var string_address = "";
|
||||
var address_line_count = 0;
|
||||
foreach (var set in Settings._instance.addressSets.addresses)
|
||||
{
|
||||
var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid);
|
||||
if (temp != null) break;
|
||||
if (temp != null)
|
||||
{
|
||||
address = temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no address found
|
||||
@@ -110,21 +118,21 @@ public static class AddressCreator
|
||||
address.land.ToLower().Trim() != "" && address.land.ToLower().Trim() != "de" &&
|
||||
address.land.ToLower().Trim() != "deutschland")
|
||||
{
|
||||
string_address = "**" + address.land.Trim() + "**"; // Needs to be bold
|
||||
string_address = "\n**" + address.land.Trim() + "**"; // Needs to be bold
|
||||
address_line_count++;
|
||||
}
|
||||
|
||||
// Alternative A: pplz valid and city existing
|
||||
if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.pplz, address.land))
|
||||
{
|
||||
string_address = address.pplz + " " + address.ort + "\n" + string_address;
|
||||
string_address = address.pplz + " " + address.ort + string_address;
|
||||
address_line_count++;
|
||||
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++;
|
||||
}
|
||||
else
|
||||
else if (!string.IsNullOrWhiteSpace(address.strasse.Trim()))
|
||||
{
|
||||
string_address = address.strasse.Trim() + "\n" + string_address;
|
||||
address_line_count++;
|
||||
@@ -133,28 +141,33 @@ public static class AddressCreator
|
||||
var nameline = CreateNameLine(address.anredzus, address.anrede, address.titel, address.vorname,
|
||||
address.adel, address.name, address.namezus);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nameline))
|
||||
if (!string.IsNullOrWhiteSpace(nameline))
|
||||
{
|
||||
string_address = nameline + string_address;
|
||||
address_line_count++;
|
||||
}
|
||||
|
||||
// REIHENFOLGE
|
||||
var nameattribs = new[]
|
||||
{ address.name1, address.name2, address.name3, address.name4, address.name5, address.abteilung };
|
||||
|
||||
for (var i = 0; i < nameattribs.Length - 1; i++)
|
||||
if (address_line_count < 7)
|
||||
var names = "";
|
||||
for (var i = 0; i < nameattribs.Length; i++)
|
||||
try
|
||||
{
|
||||
if (address_line_count >= 7) break;
|
||||
if (!string.IsNullOrWhiteSpace(nameattribs[i]))
|
||||
{
|
||||
string_address += nameattribs[i] + "\n" + string_address;
|
||||
names += "\n" + nameattribs[i];
|
||||
address_line_count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
break;
|
||||
Console.WriteLine("ERROR 15821");
|
||||
}
|
||||
|
||||
string_address = names + "\n" + string_address;
|
||||
} // Alternative B: plz valid and city existing
|
||||
else if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.plz, address.land))
|
||||
{
|
||||
@@ -165,16 +178,16 @@ public static class AddressCreator
|
||||
string_address = address.strasse.Trim() + "\n" + string_address;
|
||||
address_line_count++;
|
||||
}
|
||||
else
|
||||
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++;
|
||||
}
|
||||
|
||||
var nameline = CreateNameLine(address.anredzus, address.anrede, address.titel, address.vorname,
|
||||
address.adel, address.name, address.namezus);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nameline))
|
||||
if (!string.IsNullOrWhiteSpace(nameline))
|
||||
{
|
||||
string_address = nameline + string_address;
|
||||
address_line_count++;
|
||||
@@ -183,19 +196,23 @@ public static class AddressCreator
|
||||
var nameattribs = new[]
|
||||
{ address.name1, address.name2, address.name3, address.name4, address.name5, address.abteilung };
|
||||
|
||||
for (var i = 0; i < nameattribs.Length - 1; i++)
|
||||
if (address_line_count < 7)
|
||||
var names = "";
|
||||
for (var i = 0; i < nameattribs.Length; i++)
|
||||
try
|
||||
{
|
||||
if (address_line_count >= 7) break;
|
||||
if (!string.IsNullOrWhiteSpace(nameattribs[i]))
|
||||
{
|
||||
string_address += nameattribs[i] + "\n" + string_address;
|
||||
names += "\n" + nameattribs[i];
|
||||
address_line_count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
break;
|
||||
Console.WriteLine("ERROR 15821");
|
||||
}
|
||||
|
||||
string_address = names + "\n" + string_address;
|
||||
} // Error-Handling?
|
||||
|
||||
return string_address;
|
||||
@@ -232,19 +249,27 @@ public static class AddressCreator
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(plz)) return false;
|
||||
|
||||
// For non-german countries, accept any non-empty postal code
|
||||
if (land.ToLower().Trim() != "germany" && land.ToLower().Trim() != "ger" && land.ToLower().Trim() != "" &&
|
||||
land.ToLower().Trim() != "de" && land.ToLower().Trim() != "deutschland")
|
||||
return true;
|
||||
var trimmedPlz = plz.Trim();
|
||||
var trimmedLand = land.ToLower().Trim();
|
||||
|
||||
// For Germany, check if it's a valid numeric postal code (5 digits)
|
||||
if (int.TryParse(plz, out var iplz))
|
||||
// Check if it's a German country code
|
||||
var isGermany = trimmedLand == "germany" || trimmedLand == "ger" || trimmedLand == "de" ||
|
||||
trimmedLand == "deutschland" || trimmedLand == "";
|
||||
|
||||
if (isGermany)
|
||||
// For Germany (including empty land), accept numeric postal codes with 5 digits
|
||||
try
|
||||
{
|
||||
if (iplz > 99999 || iplz < 10000) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it can't be parsed as int, it's not a valid German postal code
|
||||
var iplz = Convert.ToInt32(trimmedPlz);
|
||||
if (trimmedPlz.Length == 5) return true;
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For non-German countries, accept any non-empty postal code
|
||||
return true;
|
||||
}
|
||||
}
|
||||
391
Tasks/PdfBuilder.cs
Normal file
391
Tasks/PdfBuilder.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using PdfSharp;
|
||||
using PdfSharp.Drawing;
|
||||
using PdfSharp.Pdf;
|
||||
|
||||
namespace Logof_Client;
|
||||
|
||||
public class PdfBuilder
|
||||
{
|
||||
// Table layout
|
||||
private const int CellsPerRow = 3;
|
||||
private const int CellsPerPage = 21; // 3 columns × 7 rows
|
||||
private readonly XFont _boldFont = new("Arial", 9, XFontStyleEx.Bold);
|
||||
private readonly double _cellHeight = 42.43; // mm
|
||||
private readonly double _cellPaddingBottom = 5; // mm
|
||||
|
||||
// Padding inside cells
|
||||
private readonly double _cellPaddingLeft = 5; // mm
|
||||
private readonly double _cellPaddingTop = 5; // mm
|
||||
|
||||
// Cell dimensions (in mm)
|
||||
private readonly double _cellWidth = 70; // mm
|
||||
|
||||
// Font settings
|
||||
private readonly double _fontSize = 9;
|
||||
|
||||
private readonly double _marginBottom = 1; // mm
|
||||
|
||||
// Paper and layout settings
|
||||
private readonly double _marginLeft = 0; // mm
|
||||
private readonly double _marginRight = 0; // mm
|
||||
private readonly double _marginTop = 0; // mm
|
||||
private readonly XFont _regularFont = new("Arial", 9, XFontStyleEx.Regular);
|
||||
private readonly XFont _smallFont = new("Arial", 6, XFontStyleEx.Regular);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PDF document with address stickers from an AddressSet in a 3×7 grid layout on A4 pages.
|
||||
/// </summary>
|
||||
/// <param name="addressSetId">The ID of the AddressSet to use</param>
|
||||
/// <param name="outputPath">Path where the PDF should be saved</param>
|
||||
public void CreateAddressLabelPdfFromAddressSet(int addressSetId, string outputPath)
|
||||
{
|
||||
// Find the AddressSet by ID
|
||||
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
|
||||
if (addressSet == null)
|
||||
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
|
||||
|
||||
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0)
|
||||
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
|
||||
|
||||
// Generate markdown addresses from all KasPersons in the set
|
||||
//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++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PDF document with address stickers from an AddressSet with a placeholder in the first cell.
|
||||
/// </summary>
|
||||
/// <param name="addressSetId">The ID of the AddressSet to use</param>
|
||||
/// <param name="placeholderText">Text for the first cell (top-left)</param>
|
||||
/// <param name="outputPath">Path where the PDF should be saved</param>
|
||||
public void CreateAddressLabelPdfFromAddressSetWithPlaceholder(int addressSetId, string placeholderText,
|
||||
string outputPath)
|
||||
{
|
||||
// Find the AddressSet by ID
|
||||
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
|
||||
if (addressSet == null)
|
||||
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
|
||||
|
||||
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0)
|
||||
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
|
||||
|
||||
// Generate markdown addresses from all KasPersons in the set
|
||||
//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++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PDF document with address stickers in a 3×7 grid layout on A4 pages.
|
||||
/// </summary>
|
||||
/// <param name="addresses">Array of addresses (from CreateFinalMarkdownString)</param>
|
||||
/// <param name="outputPath">Path where the PDF should be saved</param>
|
||||
public void CreateAddressLabelPdf(List<string> addresses, string outputPath)
|
||||
{
|
||||
if (addresses == null || addresses.Count == 0)
|
||||
throw new ArgumentException("Addresses array cannot be null or empty");
|
||||
|
||||
var document = new PdfDocument();
|
||||
|
||||
var addressIndex = 0;
|
||||
while (addressIndex < addresses.Count)
|
||||
{
|
||||
var page = document.AddPage();
|
||||
page.Size = PageSize.A4;
|
||||
|
||||
using (var gfx = XGraphics.FromPdfPage(page))
|
||||
{
|
||||
// Draw the grid and fill cells
|
||||
DrawPage(gfx, addresses, ref addressIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the document
|
||||
document.Save(outputPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PDF document with a single placeholder cell for other information.
|
||||
/// </summary>
|
||||
/// <param name="addresses">Array of addresses</param>
|
||||
/// <param name="placeholderText">Text for the first cell (top-left)</param>
|
||||
/// <param name="outputPath">Path where the PDF should be saved</param>
|
||||
public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath)
|
||||
{
|
||||
if (addresses == null || addresses.Count == 0)
|
||||
throw new ArgumentException("Addresses array cannot be null or empty");
|
||||
|
||||
var document = new PdfDocument();
|
||||
|
||||
var addressIndex = 0;
|
||||
var isFirstCell = true;
|
||||
|
||||
while (addressIndex < addresses.Count || isFirstCell)
|
||||
{
|
||||
var page = document.AddPage();
|
||||
page.Size = PageSize.A4;
|
||||
|
||||
using (var gfx = XGraphics.FromPdfPage(page))
|
||||
{
|
||||
DrawPageWithPlaceholder(gfx, addresses, ref addressIndex, ref isFirstCell, placeholderText);
|
||||
}
|
||||
}
|
||||
|
||||
document.Save(outputPath);
|
||||
}
|
||||
|
||||
private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex)
|
||||
{
|
||||
var cellCount = 0;
|
||||
|
||||
for (var row = 0; row < 7; row++)
|
||||
{
|
||||
for (var col = 0; col < 3; col++)
|
||||
{
|
||||
if (addressIndex >= addresses.Count) break;
|
||||
|
||||
var x = MmToPoints(_marginLeft + col * _cellWidth);
|
||||
var y = MmToPoints(_marginTop + row * _cellHeight);
|
||||
|
||||
DrawCell(gfx, x, y, addresses[addressIndex]);
|
||||
addressIndex++;
|
||||
cellCount++;
|
||||
}
|
||||
|
||||
if (addressIndex >= addresses.Count) break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex,
|
||||
ref bool isFirstCell, string placeholderText)
|
||||
{
|
||||
var cellCount = 0;
|
||||
|
||||
for (var row = 0; row < 7; row++)
|
||||
for (var col = 0; col < 3; col++)
|
||||
{
|
||||
var x = MmToPoints(_marginLeft + col * _cellWidth);
|
||||
var y = MmToPoints(_marginTop + row * _cellHeight);
|
||||
|
||||
// First cell: placeholder
|
||||
if (isFirstCell)
|
||||
{
|
||||
DrawCell(gfx, x, y, placeholderText);
|
||||
isFirstCell = false;
|
||||
}
|
||||
else if (addressIndex < addresses.Count)
|
||||
{
|
||||
DrawCell(gfx, x, y, addresses[addressIndex]);
|
||||
addressIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawEmptyCell(gfx, x, y);
|
||||
}
|
||||
|
||||
cellCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCell(XGraphics gfx, double x, double y, string? address)
|
||||
{
|
||||
var cellWidthPoints = MmToPoints(_cellWidth);
|
||||
var cellHeightPoints = MmToPoints(_cellHeight);
|
||||
|
||||
// Draw cell border
|
||||
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
|
||||
gfx.DrawRectangle(XPens.Black, rect);
|
||||
|
||||
// Draw address content if available
|
||||
if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints);
|
||||
}
|
||||
|
||||
private void DrawEmptyCell(XGraphics gfx, double x, double y)
|
||||
{
|
||||
var cellWidthPoints = MmToPoints(_cellWidth);
|
||||
var cellHeightPoints = MmToPoints(_cellHeight);
|
||||
|
||||
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
|
||||
gfx.DrawRectangle(XPens.Black, rect);
|
||||
}
|
||||
|
||||
private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight)
|
||||
{
|
||||
var paddingLeftPoints = MmToPoints(_cellPaddingLeft);
|
||||
var paddingTopPoints = MmToPoints(_cellPaddingTop);
|
||||
var paddingBottomPoints = MmToPoints(_cellPaddingBottom);
|
||||
|
||||
var maxWidth = cellWidth - paddingLeftPoints * 2;
|
||||
|
||||
// Split text by newlines and remove empty trailing lines
|
||||
var rawLines = text.Split(new[] { "\n" }, StringSplitOptions.None);
|
||||
var lines = rawLines.Where(l => l != null).ToArray();
|
||||
|
||||
// Use a conservative line height in points
|
||||
var lineHeight = _regularFont.Size * 1.2;
|
||||
|
||||
// Calculate total height of the text block
|
||||
var totalHeight = lines.Length * lineHeight;
|
||||
|
||||
// Start drawing from the top of the cell (align addresses to top)
|
||||
var startY = y + paddingTopPoints;
|
||||
|
||||
var currentY = startY;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
// Stop if we've reached the top boundary
|
||||
if (currentY + lineHeight > y + cellHeight - paddingBottomPoints + 0.001)
|
||||
break;
|
||||
|
||||
// Parse and draw the line with markdown support
|
||||
DrawLineWithMarkdown(gfx, line, x + paddingLeftPoints, currentY, maxWidth);
|
||||
currentY += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth)
|
||||
{
|
||||
var currentX = x;
|
||||
var i = 0;
|
||||
|
||||
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 (**)
|
||||
if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*')
|
||||
{
|
||||
// Find closing **
|
||||
var endIndex = line.IndexOf("**", i + 2, StringComparison.Ordinal);
|
||||
if (endIndex != -1)
|
||||
{
|
||||
var boldText = line.Substring(i + 2, endIndex - (i + 2));
|
||||
// Draw bold text and measure width accurately
|
||||
gfx.DrawString(boldText, _boldFont, XBrushes.Black,
|
||||
new XRect(currentX, y, maxWidth - (currentX - x), _boldFont.Size * 1.2),
|
||||
XStringFormats.TopLeft);
|
||||
var measured = gfx.MeasureString(boldText, _boldFont);
|
||||
currentX += measured.Width;
|
||||
i = endIndex + 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular text until next ** or end of line
|
||||
var nextBoldIndex = line.IndexOf("**", i, StringComparison.Ordinal);
|
||||
var textEnd = nextBoldIndex == -1 ? line.Length : nextBoldIndex;
|
||||
var regularText = line.Substring(i, textEnd - i);
|
||||
|
||||
if (!string.IsNullOrEmpty(regularText))
|
||||
{
|
||||
gfx.DrawString(regularText, _regularFont, XBrushes.Black,
|
||||
new XRect(currentX, y, maxWidth - (currentX - x), _regularFont.Size * 1.2), XStringFormats.TopLeft);
|
||||
var measured = gfx.MeasureString(regularText, _regularFont);
|
||||
currentX += measured.Width;
|
||||
}
|
||||
|
||||
i = textEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts millimeters to points (1 mm = 2.834645669 points)
|
||||
/// </summary>
|
||||
private double MmToPoints(double mm)
|
||||
{
|
||||
return mm * 2.834645669;
|
||||
}
|
||||
|
||||
// Configuration methods to allow customization
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cell dimensions in millimeters
|
||||
/// </summary>
|
||||
public void SetCellDimensions(double width, double height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
throw new ArgumentException("Cell dimensions must be positive");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the page margins in millimeters
|
||||
/// </summary>
|
||||
public void SetMargins(double left, double top, double right, double bottom)
|
||||
{
|
||||
if (left < 0 || top < 0 || right < 0 || bottom < 0)
|
||||
throw new ArgumentException("Margins cannot be negative");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user