Nu ook outlook

alphons <alphons@heijden.com> 9 Mar 2025, 23:36
e253ba945bca9ff1ef09a4d5ebb387e3b5245416
10 files changed
  • Eml2Pdf.sln
  • MimePart2Pdf/MimePart2Pdf.csproj
  • MimePart2Pdf/PdfHelper.cs
  • Msg2PdfWinApp/Form1.Designer.cs
  • Msg2PdfWinApp/Form1.cs
  • Msg2PdfWinApp/Form1.resx
  • Msg2PdfWinApp/Msg2PdfWinApp.csproj
  • Msg2PdfWinApp/Program.cs
  • Msg2PdfWinApp/input/MustExist.txt
  • Msg2PdfWinApp/pdf/MustExist.txt
diff --git a/Eml2Pdf.sln b/Eml2Pdf.sln
index 2dc3afb..73f7c39 100644
--- a/Eml2Pdf.sln
+++ b/Eml2Pdf.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmlFastDecoder", "EmlFastDe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eml2MimePart", "Eml2MimePart\Eml2MimePart.csproj", "{6E7CB274-7632-4D7D-B3CC-4EDE7AFEB6A9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Msg2PdfWinApp", "Msg2PdfWinApp\Msg2PdfWinApp.csproj", "{24FF978F-6BA3-4C26-9B68-ED79A9C8403B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{6E7CB274-7632-4D7D-B3CC-4EDE7AFEB6A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E7CB274-7632-4D7D-B3CC-4EDE7AFEB6A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E7CB274-7632-4D7D-B3CC-4EDE7AFEB6A9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {24FF978F-6BA3-4C26-9B68-ED79A9C8403B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {24FF978F-6BA3-4C26-9B68-ED79A9C8403B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {24FF978F-6BA3-4C26-9B68-ED79A9C8403B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {24FF978F-6BA3-4C26-9B68-ED79A9C8403B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MimePart2Pdf/MimePart2Pdf.csproj b/MimePart2Pdf/MimePart2Pdf.csproj
index 4259fac..cb8d53c 100644
--- a/MimePart2Pdf/MimePart2Pdf.csproj
+++ b/MimePart2Pdf/MimePart2Pdf.csproj
@@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="HtmlRenderer.PdfSharp.NetStandard2" Version="1.5.1.3" />
+ <PackageReference Include="MsgReader" Version="5.7.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
diff --git a/MimePart2Pdf/PdfHelper.cs b/MimePart2Pdf/PdfHelper.cs
index 55eaf94..73a9a0f 100644
--- a/MimePart2Pdf/PdfHelper.cs
+++ b/MimePart2Pdf/PdfHelper.cs
@@ -1,9 +1,11 @@
using Eml2MimePart;
+using MsgReader.Outlook;
using PdfSharp.Pdf;
using System.Globalization;
-using System.IO;
using System.Text;
using TheArtOfDev.HtmlRenderer.PdfSharp;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
namespace MimePart2Pdf;
@@ -36,7 +38,7 @@ public class PdfHelper
}
}
- private static readonly CultureInfo Nederland = new ("nl-NL");
+ private static readonly CultureInfo Nederland = new("nl-NL");
public static void CreatePdf(MimePart email, string pdfPath)
@@ -79,7 +81,7 @@ public class PdfHelper
htmlHeader.AppendLine($"<div><b>Verzonden:</b> {datum}</div>");
htmlHeader.AppendLine($"<div><b>Aan:</b> {HtmlEscape(email["To"])}</div>");
var cc = email["CC"];
- if(!string.IsNullOrWhiteSpace(cc))
+ if (!string.IsNullOrWhiteSpace(cc))
htmlHeader.AppendLine($"<div><b>Cc:</b> {HtmlEscape(cc)}</div>");
htmlHeader.AppendLine($"<div><b>Onderwerp:</b> {HtmlEscape(email["Subject"])}</div>");
var priority = email["Priority"];
@@ -147,5 +149,94 @@ public class PdfHelper
}
+ /// <summary>
+ /// Outlook msg decoder and make pdf
+ /// </summary>
+ /// <param name="msgFilePath">path to ootlook msg file</param>
+ /// <param name="pdfPath">path to output for pdf file</param>
+ /// <param name="SaveAttachements">when true, save also all attachements</param>
+ public static void CreatePdf(string msgFilePath, string pdfPath, bool SaveAttachements)
+ {
+ try
+ {
+ using var msg = new Storage.Message(msgFilePath);
+
+ var html = msg.BodyHtml ?? msg.BodyText;
+
+ if (string.IsNullOrWhiteSpace(html))
+ return;
+
+ var dtm = (msg.SentOn ?? DateTimeOffset.Now).DateTime;
+
+ int index = html.IndexOf("<body");
+ if (index != -1)
+ {
+ int eindIndex = html.IndexOf('>', index);
+ if (eindIndex != -1)
+ {
+ var htmlHeader = new StringBuilder(Environment.NewLine);
+
+ var datum = dtm.ToString("dddd d MMMM yyyy HH:mm", Nederland);
+
+ var recipientsTo = msg.GetEmailRecipients(RecipientType.To, false, false);
+ var recipientsCc = msg.GetEmailRecipients(RecipientType.Cc, false, false);
+ var recipientsBcc = msg.GetEmailRecipients(RecipientType.Bcc, false, false);
+ htmlHeader.AppendLine($"<div><b>Van:</b> {HtmlEscape((msg.Sender?.DisplayName ?? "N/A") + " <" + (msg.Sender?.Email ?? "N/A") + ">")}</div>");
+ htmlHeader.AppendLine($"<div><b>Verzonden:</b> {datum}</div>");
+ htmlHeader.AppendLine($"<div><b>Aan:</b> {HtmlEscape((recipientsTo != null ? string.Join(", ", recipientsTo) : "N/A"))}</div>");
+
+ var cc = (recipientsCc != null ? string.Join(", ", recipientsCc) : "N/A");
+ if (!string.IsNullOrWhiteSpace(recipientsCc))
+ htmlHeader.AppendLine($"<div><b>Cc:</b> {HtmlEscape(cc)}</div>");
+
+ var bcc = (recipientsBcc != null ? string.Join(", ", recipientsBcc) : "N/A");
+ if (!string.IsNullOrWhiteSpace(recipientsBcc))
+ htmlHeader.AppendLine($"<div><b>Bcc:</b> {HtmlEscape(bcc)}</div>");
+
+ htmlHeader.AppendLine($"<div><b>Onderwerp:</b> {HtmlEscape(msg.Subject ?? "N/A")}</div>");
+
+ var priority = ""+msg.GetMapiProperty("Priority");
+ if (!string.IsNullOrWhiteSpace(priority))
+ htmlHeader.AppendLine($"<div><b>Prioriteit:</b> {HtmlEscape(priority)}</div>");
+
+ var importance = "" + msg.GetMapiProperty("Importance");
+ if (!string.IsNullOrWhiteSpace(importance))
+ htmlHeader.AppendLine($"<div><b>Urgentie:</b> {HtmlEscape(importance)}</div>");
+
+ htmlHeader.AppendLine("<div>&nbsp;</div>");
+
+ html = html.Insert(eindIndex + 1, htmlHeader.ToString());
+ }
+ }
+
+ using PdfDocument pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
+
+ pdf.Save(pdfPath);
+
+ File.SetLastWriteTimeUtc(pdfPath, dtm);
+ File.SetCreationTime(pdfPath, dtm);
+
+ if (SaveAttachements && msg.Attachments.Count > 0)
+ {
+ var dir = Path.GetDirectoryName(pdfPath) ?? @"c:\temp";
+ foreach (var attachment in msg.Attachments)
+ {
+ if (attachment is Storage.Attachment storageAttachment)
+ {
+ var outputPath = Path.Combine(dir, storageAttachment.FileName);
+ File.WriteAllBytes(outputPath, storageAttachment.Data);
+ }
+ }
+ }
+ else
+ {
+ //Console.WriteLine("\nNo attachments found.");
+ }
+ }
+ catch (Exception ex)
+ {
+ //Console.WriteLine("Error reading .msg file: " + ex.Message);
+ }
+ }
}
diff --git a/Msg2PdfWinApp/Form1.Designer.cs b/Msg2PdfWinApp/Form1.Designer.cs
new file mode 100644
index 0000000..33d77d0
--- /dev/null
+++ b/Msg2PdfWinApp/Form1.Designer.cs
@@ -0,0 +1,100 @@
+namespace Msg2PdfWinApp
+{
+ partial class Form1
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ txtLog = new TextBox();
+ txtPdf = new TextBox();
+ button1 = new Button();
+ txtInput = new TextBox();
+ SuspendLayout();
+ //
+ // txtLog
+ //
+ txtLog.AcceptsReturn = true;
+ txtLog.AcceptsTab = true;
+ txtLog.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
+ txtLog.Location = new Point(12, 107);
+ txtLog.Multiline = true;
+ txtLog.Name = "txtLog";
+ txtLog.ReadOnly = true;
+ txtLog.ScrollBars = ScrollBars.Both;
+ txtLog.Size = new Size(411, 222);
+ txtLog.TabIndex = 11;
+ //
+ // txtPdf
+ //
+ txtPdf.Location = new Point(12, 69);
+ txtPdf.Name = "txtPdf";
+ txtPdf.PlaceholderText = "pdf directory";
+ txtPdf.Size = new Size(269, 23);
+ txtPdf.TabIndex = 10;
+ txtPdf.Text = "pdf";
+ //
+ // button1
+ //
+ button1.Location = new Point(291, 42);
+ button1.Name = "button1";
+ button1.Size = new Size(125, 23);
+ button1.TabIndex = 8;
+ button1.Text = "Msg2Pdf";
+ button1.UseVisualStyleBackColor = true;
+ button1.Click += Button_Click;
+ //
+ // txtInput
+ //
+ txtInput.Location = new Point(12, 12);
+ txtInput.Name = "txtInput";
+ txtInput.PlaceholderText = "input directory";
+ txtInput.Size = new Size(269, 23);
+ txtInput.TabIndex = 6;
+ txtInput.Text = "input";
+ //
+ // Form1
+ //
+ AutoScaleDimensions = new SizeF(7F, 15F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(435, 341);
+ Controls.Add(txtLog);
+ Controls.Add(txtPdf);
+ Controls.Add(button1);
+ Controls.Add(txtInput);
+ Name = "Form1";
+ Text = "Outlook msg to pdf";
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private TextBox txtLog;
+ private TextBox txtPdf;
+ private Button button1;
+ private TextBox txtInput;
+ }
+}
diff --git a/Msg2PdfWinApp/Form1.cs b/Msg2PdfWinApp/Form1.cs
new file mode 100644
index 0000000..2aad3f8
--- /dev/null
+++ b/Msg2PdfWinApp/Form1.cs
@@ -0,0 +1,35 @@
+using Eml2MimePart;
+using MimePart2Pdf;
+using MsgReader.Outlook;
+using System.Text;
+
+namespace Msg2PdfWinApp
+{
+ public partial class Form1 : Form
+ {
+ public Form1()
+ {
+ InitializeComponent();
+ }
+
+ private void Button_Click(object sender, EventArgs e)
+ {
+ this.button1.Enabled = false;
+ this.txtInput.Enabled = false;
+
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ foreach (var msgFilePath in Directory.GetFiles(this.txtInput.Text, "*.msg"))
+ {
+ var name = Path.GetFileNameWithoutExtension(msgFilePath);
+
+ var pdfPath = Path.Combine(this.txtPdf.Text, $"{name}.pdf");
+
+ PdfHelper.CreatePdf(msgFilePath, pdfPath, false);
+ }
+
+ this.txtInput.Enabled = true;
+ this.button1.Enabled = true;
+ }
+ }
+}
diff --git a/Msg2PdfWinApp/Form1.resx b/Msg2PdfWinApp/Form1.resx
new file mode 100644
index 0000000..8b2ff64
--- /dev/null
+++ b/Msg2PdfWinApp/Form1.resx
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root>
\ No newline at end of file
diff --git a/Msg2PdfWinApp/Msg2PdfWinApp.csproj b/Msg2PdfWinApp/Msg2PdfWinApp.csproj
new file mode 100644
index 0000000..d4cd292
--- /dev/null
+++ b/Msg2PdfWinApp/Msg2PdfWinApp.csproj
@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>WinExe</OutputType>
+ <TargetFramework>net8.0-windows</TargetFramework>
+ <Nullable>enable</Nullable>
+ <UseWindowsForms>true</UseWindowsForms>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <SatelliteResourceLanguages>none</SatelliteResourceLanguages>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\MimePart2Pdf\MimePart2Pdf.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Update="input\MustExist.txt">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Update="pdf\MustExist.txt">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
+</Project>
\ No newline at end of file
diff --git a/Msg2PdfWinApp/Program.cs b/Msg2PdfWinApp/Program.cs
new file mode 100644
index 0000000..6070629
--- /dev/null
+++ b/Msg2PdfWinApp/Program.cs
@@ -0,0 +1,17 @@
+namespace Msg2PdfWinApp
+{
+ internal static class Program
+ {
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ [STAThread]
+ static void Main()
+ {
+ // To customize application configuration such as set high DPI settings or default font,
+ // see https://aka.ms/applicationconfiguration.
+ ApplicationConfiguration.Initialize();
+ Application.Run(new Form1());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Msg2PdfWinApp/input/MustExist.txt b/Msg2PdfWinApp/input/MustExist.txt
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/Msg2PdfWinApp/input/MustExist.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Msg2PdfWinApp/pdf/MustExist.txt b/Msg2PdfWinApp/pdf/MustExist.txt
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/Msg2PdfWinApp/pdf/MustExist.txt
@@ -0,0 +1 @@
+
\ No newline at end of file