diff --git a/StockHistory/PdfPigLetterExtractor.cs b/StockHistory/PdfPigLetterExtractor.cs new file mode 100644 index 0000000..351d425 --- /dev/null +++ b/StockHistory/PdfPigLetterExtractor.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UglyToad.PdfPig; +using UglyToad.PdfPig.Content; + + +namespace StockHistory; + + +public static class PdfPigLetterExtractor +{ + public static List ExtractLines(List pages, double lineTolerance = 3.0, double wordSpacingFactor = 0.5) + { + var result = new List(); + + foreach (var page in pages) + { + var letters = page.FoundLetters; + + // 1. Gruppér tecken till rader baserat på Y-position + var lines = letters + .GroupBy(l => Math.Round(l.BoundingBox.Top / lineTolerance)) + .OrderBy(g => g.Key); + + foreach (var lineGroup in lines) + { + var lineLetters = lineGroup + .OrderBy(l => l.BoundingBox.Left) + .ToList(); + + // 2. Bygg ord genom att titta på avstånd mellan tecken + var words = BuildWords(lineLetters, wordSpacingFactor); + + result.Add(string.Join(" ", words)); + } + } + + + return result; + } + + private static List BuildWords(List letters, double spacingFactor) + { + var words = new List(); + var currentWord = new List(); + + for (int i = 0; i < letters.Count; i++) + { + var letter = letters[i]; + currentWord.Add(letter); + + if (i < letters.Count - 1) + { + var next = letters[i + 1]; + + // Avstånd mellan tecken + double gap = next.BoundingBox.Left - letter.BoundingBox.Right; + + // Typisk teckenbredd + double avgWidth = letter.BoundingBox.Width; + + // Om gapet är större än spacingFactor * teckenbredd → nytt ord + if (gap > avgWidth * spacingFactor) + { + //words.Add(new string(currentWord.Select(l => l.Value).ToArray())); + words.Add(string.Concat(currentWord.Select(l => l.Value))); + currentWord.Clear(); + } + } + } + + if (currentWord.Count > 0) + { + //words.Add(new string(currentWord.Select(l => l.Value).ToArray())); + words.Add(string.Concat(currentWord.Select(l => l.Value))); + } + + return words; + } +} diff --git a/StockHistory/StockHistory.csproj b/StockHistory/StockHistory.csproj index 8766b0e..7c37198 100644 --- a/StockHistory/StockHistory.csproj +++ b/StockHistory/StockHistory.csproj @@ -7,6 +7,10 @@ true enable + + + + diff --git a/StockHistory/frmStockHistoryInit.Designer.cs b/StockHistory/frmStockHistoryInit.Designer.cs index 2b5252a..68f97e1 100644 --- a/StockHistory/frmStockHistoryInit.Designer.cs +++ b/StockHistory/frmStockHistoryInit.Designer.cs @@ -44,6 +44,9 @@ CommissionHeader = new ColumnHeader(); AmountToPayHeader = new ColumnHeader(); btnClose = new Button(); + btnAnalysMany = new Button(); + txtVald = new TextBox(); + lblAntal = new Label(); SuspendLayout(); // // btnChooseFile @@ -74,7 +77,7 @@ btnAnalysera.BackColor = Color.FromArgb(128, 255, 128); btnAnalysera.FlatStyle = FlatStyle.Flat; btnAnalysera.Font = new Font("Segoe UI", 13F); - btnAnalysera.Location = new Point(26, 86); + btnAnalysera.Location = new Point(26, 126); btnAnalysera.Name = "btnAnalysera"; btnAnalysera.Size = new Size(143, 37); btnAnalysera.TabIndex = 2; @@ -84,10 +87,10 @@ // // txtFound // - txtFound.Location = new Point(26, 149); + txtFound.Location = new Point(26, 180); txtFound.Multiline = true; txtFound.Name = "txtFound"; - txtFound.Size = new Size(468, 334); + txtFound.Size = new Size(468, 303); txtFound.TabIndex = 3; // // btnSaveToDb @@ -95,7 +98,7 @@ btnSaveToDb.BackColor = Color.FromArgb(128, 255, 128); btnSaveToDb.FlatStyle = FlatStyle.Flat; btnSaveToDb.Font = new Font("Segoe UI", 13F); - btnSaveToDb.Location = new Point(197, 86); + btnSaveToDb.Location = new Point(351, 126); btnSaveToDb.Name = "btnSaveToDb"; btnSaveToDb.Size = new Size(143, 37); btnSaveToDb.TabIndex = 4; @@ -168,12 +171,36 @@ btnClose.UseVisualStyleBackColor = false; btnClose.Click += btnClose_Click; // + // btnAnalysMany + // + btnAnalysMany.Location = new Point(0, 0); + btnAnalysMany.Name = "btnAnalysMany"; + btnAnalysMany.Size = new Size(75, 23); + btnAnalysMany.TabIndex = 2; + // + // txtVald + // + txtVald.Location = new Point(0, 0); + txtVald.Name = "txtVald"; + txtVald.Size = new Size(100, 27); + txtVald.TabIndex = 1; + // + // lblAntal + // + lblAntal.Location = new Point(0, 0); + lblAntal.Name = "lblAntal"; + lblAntal.Size = new Size(100, 23); + lblAntal.TabIndex = 0; + // // frmStockHistoryInit // AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleMode = AutoScaleMode.Font; BackColor = Color.LightGray; ClientSize = new Size(1413, 553); + Controls.Add(lblAntal); + Controls.Add(txtVald); + Controls.Add(btnAnalysMany); Controls.Add(btnClose); Controls.Add(lwTransactions); Controls.Add(btnSaveToDb); @@ -205,5 +232,8 @@ private ColumnHeader CommissionHeader; private ColumnHeader AmountToPayHeader; private Button btnClose; + private Button btnAnalysMany; + private TextBox txtVald; + private Label lblAntal; } } \ No newline at end of file diff --git a/StockHistory/frmStockHistoryInit.cs b/StockHistory/frmStockHistoryInit.cs index 08ab0db..dbc459a 100644 --- a/StockHistory/frmStockHistoryInit.cs +++ b/StockHistory/frmStockHistoryInit.cs @@ -12,207 +12,228 @@ using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; -namespace StockHistory +namespace StockHistory; + +public partial class frmStockHistoryInit : Form { - public partial class frmStockHistoryInit : Form + private readonly IPdfOpener _pdfOpener; + private readonly IPdfFormatter _pdfFormatter; + private readonly ITransactionNotesServices _transactionNotes; + private List _pdfSidor = new(); + private List localPdfSida = new(); + + public frmStockHistoryInit( + IPdfOpener pdfOpener, + IPdfFormatter pdfFormatter, + ITransactionNotesServices transactionNotes + ) { - private readonly IPdfOpener _pdfOpener; - private readonly IPdfFormatter _pdfFormatter; - private readonly ITransactionNotesServices _transactionNotes; - private List _pdfSidor = new(); + InitializeComponent(); + _pdfOpener = pdfOpener; + _pdfFormatter = pdfFormatter; + _transactionNotes = transactionNotes; + btnAnalysera.Enabled = false; - public frmStockHistoryInit( - IPdfOpener pdfOpener, - IPdfFormatter pdfFormatter, - ITransactionNotesServices transactionNotes - ) + Shown += async (s, e) => await LoadBusinessAsync(); + } + + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public List Selected { get; set; } = new(); + + private void btnChooseFile_Click(object sender, EventArgs e) + { + ofdFiler.Title = "Välj en PDF-fil"; + ofdFiler.FileName = ""; + ofdFiler.Filter = "PDF-filer (*.pdf)|*.pdf|ExcelTrans (*.csv)|*.csv|Alla filer (*.*)|*.*"; + var result = ofdFiler.ShowDialog(); + + if (result == DialogResult.OK) { - InitializeComponent(); - _pdfOpener = pdfOpener; - _pdfFormatter = pdfFormatter; - _transactionNotes = transactionNotes; - btnAnalysera.Enabled = false; - Shown += async (s, e) => await LoadBusinessAsync(); - } - - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public List Selected { get; set; } = new(); - - private void btnChooseFile_Click(object sender, EventArgs e) - { - ofdFiler.Title = "Välj en PDF-fil"; - ofdFiler.FileName = ""; - ofdFiler.Filter = "PDF-filer (*.pdf)|*.pdf|Alla filer (*.*)|*.*"; - var result = ofdFiler.ShowDialog(); - - if (result == DialogResult.OK) - { - lblFileName.Text = ofdFiler.FileName; - } - } - - private void lblFileName_TextChanged(object sender, EventArgs e) - { - if (lblFileName.Text.Length > 5 && lblFileName.Text.EndsWith(".pdf")) - { - _pdfOpener.OpenPdf(lblFileName.Text); - _pdfSidor = _pdfOpener.PdfSidor; - btnAnalysera.Enabled = true; - } - else - { - btnAnalysera.Enabled = false; - } - } - - private void btnAnalysera_Click(object sender, EventArgs e) - { - string PdfResult = _pdfFormatter.ExtractFormattedTextUsingWords(_pdfSidor); - txtFound.Text = string.Empty; - List result = PdfResult.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(); - //List selected = new(); - - foreach (var line in result) - { - if (!string.IsNullOrWhiteSpace(line) && line.StartsWith("Affärsdag") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Handelstidpunkt") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Värdepapper") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Kurs") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Genomsnittlig") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Antal") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Köpeskilling") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Courtage") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Belopp") - || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Vi bekräftar") - ) - { - Selected.Add(line); - txtFound.Text += line + Environment.NewLine; - txtFound.Refresh(); - - } - } - btnAnalysera.Enabled = false; - lblFileName.Text = string.Empty; - } - - private async void btnSaveToDb_Click(object sender, EventArgs e) - { - TransactionNote trans = new(); - - foreach (var line in Selected) - { - if (line.StartsWith("Affärsdag")) - { - trans.TransactionDate = DateTime.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]); - } - if (line.StartsWith("Värdepapper")) - { - var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); - if (templist.Length > 2) - trans.StockCode = templist[1] + " " + templist[2]; - else - trans.StockCode = templist[1]; - } - if (line.StartsWith("Kurs")) - { - trans.StockPrice = decimal.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]); - } - if (line.StartsWith("Antal")) - { - trans.StockQuantity = int.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]); - } - if (line.StartsWith("Köpeskilling")) - { - var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); - if (templist.Length > 3) - trans.TotalAmount = decimal.Parse(templist[2] + templist[3]); - else - trans.TotalAmount = decimal.Parse(templist[2]); - } - if (line.StartsWith("Courtage")) - { - var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); - trans.Commission = decimal.Parse(tmpList[tmpList.Length - 1]); - } - if (line.StartsWith("Belopp")) - { - var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); - if (templist.Length > 5) - trans.AmountToPay = decimal.Parse(templist[4] + templist[5]); - else - trans.AmountToPay = decimal.Parse(templist[4]); - } - if (line.StartsWith("Vi bekräftar")) - { - foreach (var word in line.Split(" ", StringSplitOptions.RemoveEmptyEntries)) - { - if (word.ToUpper().Contains("KÖP") || word.ToUpper().Contains("FÖRSÄLJ")) - { - trans.TransactionType = word; - break; - } - } - } - if (line.StartsWith("Genomsnittlig")) - { - var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); - trans.StockPrice = decimal.Parse(tmpList[tmpList.Length - 1]); - } - if (line.StartsWith("Handelstidpunkt")) - { - var time = line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]; - if (time.Contains(".")) - { - var hours = int.Parse(time.Split(".")[0]); - var minutes = int.Parse(time.Split(".")[1]); - trans.TransactionDate = trans.TransactionDate.AddHours(hours).AddMinutes(minutes); - } - } - } - try - { - await _transactionNotes.AddAsync(trans); - txtFound.Text = string.Empty; - } - catch (DbUpdateException ex) when (ex.InnerException is SqliteException sqlite && sqlite.SqliteErrorCode == 19) - { - MessageBox.Show("Den här transaktionen finns redan."); - } - catch (Exception ex) - { - MessageBox.Show("Fel vid sparande till databas: " + ex.Message); - } - - Selected.Clear(); - - await LoadBusinessAsync(); - - } - - private async Task LoadBusinessAsync() - { - - var transactions = await _transactionNotes.GetAllAsync(); - - lwTransactions.Items.Clear(); - foreach (var t in transactions) - { - var item = new ListViewItem(t.StockCode); - item.SubItems.Add(t.TransactionType); - item.SubItems.Add(t.TransactionDate.ToString()); - item.SubItems.Add(t.StockQuantity.ToString()); - item.SubItems.Add(t.StockPrice.ToString()); - item.SubItems.Add(t.TotalAmount.ToString()); - item.SubItems.Add(t.Commission.ToString()); - item.SubItems.Add(t.AmountToPay.ToString()); - lwTransactions.Items.Add(item); - } - } - - private void btnClose_Click(object sender, EventArgs e) - { - this.Close(); + lblFileName.Text = ofdFiler.FileName; } } -} \ No newline at end of file + + private void lblFileName_TextChanged(object sender, EventArgs e) + { + if (lblFileName.Text.Length > 5 && lblFileName.Text.EndsWith(".pdf")) + { + _pdfOpener.OpenPdf(lblFileName.Text); + _pdfSidor = _pdfOpener.PdfSidor; + + btnAnalysera.Enabled = true; + + } + else + { + btnAnalysera.Enabled = false; + } + } + + + private void btnAnalysera_Click(object sender, EventArgs e) + { + string PdfResult=string.Empty; + if (lblFileName.Text.ToLower().Contains("avanza")) + { + //var Result = PdfPigLetterExtractor.ExtractLines(_pdfSidor); + //SelectPdfLetterData(Result); + } + else + { + PdfResult = _pdfFormatter.ExtractFormattedTextUsingWords(_pdfSidor); + SelectPdfData(PdfResult); + } + btnAnalysera.Enabled = false; + lblFileName.Text = string.Empty; + } + + + private void SelectPdfData(string PdfResult) + { + txtFound.Text = string.Empty; + List result = PdfResult.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(); + //List selected = new(); + + foreach (var line in result) + { + if (!string.IsNullOrWhiteSpace(line) && line.StartsWith("Affärsdag") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Handelstidpunkt") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Värdepapper") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Kurs") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Genomsnittlig") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Antal") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Köpeskilling") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Courtage") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Belopp") + || !string.IsNullOrWhiteSpace(line) && line.StartsWith("Vi bekräftar") + ) + { + Selected.Add(line); + txtFound.Text += line + Environment.NewLine; + txtFound.Refresh(); + + } + } + } + + private async void btnSaveToDb_Click(object sender, EventArgs e) + { + TransactionNote trans = new(); + + foreach (var line in Selected) + { + if (line.StartsWith("Affärsdag")) + { + trans.TransactionDate = DateTime.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]); + } + if (line.StartsWith("Värdepapper")) + { + var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (templist.Length > 2) + trans.StockCode = templist[1] + " " + templist[2]; + else + trans.StockCode = templist[1]; + } + if (line.StartsWith("Kurs")) + { + trans.StockPrice = decimal.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]); + } + if (line.StartsWith("Antal")) + { + trans.StockQuantity = int.Parse(line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1]); + } + if (line.StartsWith("Köpeskilling")) + { + var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (templist.Length > 3) + trans.TotalAmount = decimal.Parse(templist[2] + templist[3]); + else + trans.TotalAmount = decimal.Parse(templist[2]); + } + if (line.StartsWith("Courtage")) + { + var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + trans.Commission = decimal.Parse(tmpList[tmpList.Length - 1]); + } + if (line.StartsWith("Belopp")) + { + var templist = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + if (templist.Length > 5) + trans.AmountToPay = decimal.Parse(templist[4] + templist[5]); + else + trans.AmountToPay = decimal.Parse(templist[4]); + } + if (line.StartsWith("Vi bekräftar")) + { + foreach (var word in line.Split(" ", StringSplitOptions.RemoveEmptyEntries)) + { + if (word.ToUpper().Contains("KÖP") || word.ToUpper().Contains("FÖRSÄLJ")) + { + trans.TransactionType = word; + break; + } + } + } + if (line.StartsWith("Genomsnittlig")) + { + var tmpList = line.Split(" ", StringSplitOptions.RemoveEmptyEntries); + trans.StockPrice = decimal.Parse(tmpList[tmpList.Length - 1]); + } + if (line.StartsWith("Handelstidpunkt")) + { + var time = line.Split(" ", StringSplitOptions.RemoveEmptyEntries)[2]; + if (time.Contains(".")) + { + var hours = int.Parse(time.Split(".")[0]); + var minutes = int.Parse(time.Split(".")[1]); + trans.TransactionDate = trans.TransactionDate.AddHours(hours).AddMinutes(minutes); + } + } + } + try + { + await _transactionNotes.AddAsync(trans); + txtFound.Text = string.Empty; + } + catch (DbUpdateException ex) when (ex.InnerException is SqliteException sqlite && sqlite.SqliteErrorCode == 19) + { + MessageBox.Show("Den här transaktionen finns redan."); + } + catch (Exception ex) + { + MessageBox.Show("Fel vid sparande till databas: " + ex.Message); + } + + Selected.Clear(); + + await LoadBusinessAsync(); + + } + + private async Task LoadBusinessAsync() + { + + var transactions = await _transactionNotes.GetAllAsync(); + + lwTransactions.Items.Clear(); + foreach (var t in transactions) + { + var item = new ListViewItem(t.StockCode); + item.SubItems.Add(t.TransactionType); + item.SubItems.Add(t.TransactionDate.ToString()); + item.SubItems.Add(t.StockQuantity.ToString()); + item.SubItems.Add(t.StockPrice.ToString()); + item.SubItems.Add(t.TotalAmount.ToString()); + item.SubItems.Add(t.Commission.ToString()); + item.SubItems.Add(t.AmountToPay.ToString()); + lwTransactions.Items.Add(item); + } + } + + private void btnClose_Click(object sender, EventArgs e) + { + this.Close(); + } + +} +