From 268d5ae26aa9d1338a663c98185077e297b5a548 Mon Sep 17 00:00:00 2001
From: Shane32 <shane@acdmail.com>
Date: Sat, 1 Jun 2024 01:12:53 -0400
Subject: [PATCH 1/2] QrCodeBuilder experimentation

---
 .../Payloads/IConfigurableEccLevel.cs         |   7 +
 .../Builders/Payloads/IConfigurableEciMode.cs |   7 +
 .../Builders/Payloads/IConfigurableVersion.cs |   7 +
 QRCoder/Builders/Payloads/IPayload.cs         |   7 +
 .../Payloads/Implementations/EmailPayload.cs  |  41 +++++
 .../Payloads/Implementations/StringPayload.cs |  14 ++
 .../Payloads/Implementations/WiFiPayload.cs   |  46 +++++
 QRCoder/Builders/Payloads/PayloadBase.cs      |  21 +++
 .../Builders/Payloads/PayloadExtensions.cs    |  47 ++++++
 .../Renderers/IConfigurablePixelsPerModule.cs |   7 +
 .../Renderers/IConfigurableQuietZones.cs      |   7 +
 QRCoder/Builders/Renderers/IRenderer.cs       |   9 +
 QRCoder/Builders/Renderers/IStreamRenderer.cs |   9 +
 QRCoder/Builders/Renderers/ITextRenderer.cs   |   9 +
 .../Implementations/AsciiRenderer.cs          |  46 +++++
 .../Renderers/Implementations/PngRenderer.cs  |  43 +++++
 .../Renderers/Implementations/SvgRenderer.cs  |  42 +++++
 .../Implementations/SystemDrawingRenderer.cs  |  64 +++++++
 QRCoder/Builders/Renderers/RendererBase.cs    |  13 ++
 .../Builders/Renderers/RendererExtensions.cs  | 104 ++++++++++++
 QRCoder/QRCodeBuilder.cs                      |  52 ++++++
 .../QRCoder.approved.txt                      | 158 ++++++++++++++++++
 .../net60-windows/QRCoder.approved.txt        | 158 ++++++++++++++++++
 QRCoderApiTests/net60/QRCoder.approved.txt    | 149 +++++++++++++++++
 .../netstandard13/QRCoder.approved.txt        | 140 ++++++++++++++++
 QRCoderTests/BuilderTests.cs                  |  90 ++++++++++
 26 files changed, 1297 insertions(+)
 create mode 100644 QRCoder/Builders/Payloads/IConfigurableEccLevel.cs
 create mode 100644 QRCoder/Builders/Payloads/IConfigurableEciMode.cs
 create mode 100644 QRCoder/Builders/Payloads/IConfigurableVersion.cs
 create mode 100644 QRCoder/Builders/Payloads/IPayload.cs
 create mode 100644 QRCoder/Builders/Payloads/Implementations/EmailPayload.cs
 create mode 100644 QRCoder/Builders/Payloads/Implementations/StringPayload.cs
 create mode 100644 QRCoder/Builders/Payloads/Implementations/WiFiPayload.cs
 create mode 100644 QRCoder/Builders/Payloads/PayloadBase.cs
 create mode 100644 QRCoder/Builders/Payloads/PayloadExtensions.cs
 create mode 100644 QRCoder/Builders/Renderers/IConfigurablePixelsPerModule.cs
 create mode 100644 QRCoder/Builders/Renderers/IConfigurableQuietZones.cs
 create mode 100644 QRCoder/Builders/Renderers/IRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/IStreamRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/ITextRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/Implementations/AsciiRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/Implementations/PngRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/Implementations/SvgRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/Implementations/SystemDrawingRenderer.cs
 create mode 100644 QRCoder/Builders/Renderers/RendererBase.cs
 create mode 100644 QRCoder/Builders/Renderers/RendererExtensions.cs
 create mode 100644 QRCoder/QRCodeBuilder.cs
 create mode 100644 QRCoderTests/BuilderTests.cs

diff --git a/QRCoder/Builders/Payloads/IConfigurableEccLevel.cs b/QRCoder/Builders/Payloads/IConfigurableEccLevel.cs
new file mode 100644
index 00000000..422318ad
--- /dev/null
+++ b/QRCoder/Builders/Payloads/IConfigurableEccLevel.cs
@@ -0,0 +1,7 @@
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableEccLevel
+    {
+        QRCodeGenerator.ECCLevel EccLevel { get; set; }
+    }
+}
diff --git a/QRCoder/Builders/Payloads/IConfigurableEciMode.cs b/QRCoder/Builders/Payloads/IConfigurableEciMode.cs
new file mode 100644
index 00000000..4f2c30a5
--- /dev/null
+++ b/QRCoder/Builders/Payloads/IConfigurableEciMode.cs
@@ -0,0 +1,7 @@
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableEciMode
+    {
+        QRCodeGenerator.EciMode EciMode { get; set; }
+    }
+}
diff --git a/QRCoder/Builders/Payloads/IConfigurableVersion.cs b/QRCoder/Builders/Payloads/IConfigurableVersion.cs
new file mode 100644
index 00000000..dfe9eb02
--- /dev/null
+++ b/QRCoder/Builders/Payloads/IConfigurableVersion.cs
@@ -0,0 +1,7 @@
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableVersion
+    {
+        int Version { get; set; }
+    }
+}
diff --git a/QRCoder/Builders/Payloads/IPayload.cs b/QRCoder/Builders/Payloads/IPayload.cs
new file mode 100644
index 00000000..4096f592
--- /dev/null
+++ b/QRCoder/Builders/Payloads/IPayload.cs
@@ -0,0 +1,7 @@
+namespace QRCoder.Builders.Payloads
+{
+    public interface IPayload
+    {
+        QRCodeData ToMatrix();
+    }
+}
diff --git a/QRCoder/Builders/Payloads/Implementations/EmailPayload.cs b/QRCoder/Builders/Payloads/Implementations/EmailPayload.cs
new file mode 100644
index 00000000..e94b2ca9
--- /dev/null
+++ b/QRCoder/Builders/Payloads/Implementations/EmailPayload.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class EmailPayload : PayloadBase
+    {
+        public EmailPayload(string address)
+        {
+            _address = address;
+        }
+
+        private string _address { get; set; }
+        private string _subject { get; set; }
+        private string _body { get; set; }
+        private PayloadGenerator.Mail.MailEncoding _encoding { get; set; } = PayloadGenerator.Mail.MailEncoding.MAILTO;
+
+        public EmailPayload WithSubject(string subject)
+        {
+            _subject = subject;
+            return this;
+        }
+
+        public EmailPayload WithBody(string body)
+        {
+            _body = body;
+            return this;
+        }
+
+        public EmailPayload WithEncoding(PayloadGenerator.Mail.MailEncoding encoding)
+        {
+            if (encoding != PayloadGenerator.Mail.MailEncoding.MAILTO && encoding != PayloadGenerator.Mail.MailEncoding.SMTP && encoding != PayloadGenerator.Mail.MailEncoding.MATMSG)
+            {
+                throw new ArgumentOutOfRangeException(nameof(encoding));
+            }
+            _encoding = encoding;
+            return this;
+        }
+
+        protected override string Value => new PayloadGenerator.Mail(_address, _subject, _body, _encoding).ToString();
+    }
+}
diff --git a/QRCoder/Builders/Payloads/Implementations/StringPayload.cs b/QRCoder/Builders/Payloads/Implementations/StringPayload.cs
new file mode 100644
index 00000000..e1910dff
--- /dev/null
+++ b/QRCoder/Builders/Payloads/Implementations/StringPayload.cs
@@ -0,0 +1,14 @@
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class StringPayload : PayloadBase
+    {
+        private string _data;
+
+        public StringPayload(string data)
+        {
+            _data = data;
+        }
+
+        protected override string Value => _data;
+    }
+}
diff --git a/QRCoder/Builders/Payloads/Implementations/WiFiPayload.cs b/QRCoder/Builders/Payloads/Implementations/WiFiPayload.cs
new file mode 100644
index 00000000..16ecdedd
--- /dev/null
+++ b/QRCoder/Builders/Payloads/Implementations/WiFiPayload.cs
@@ -0,0 +1,46 @@
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class WiFiPayload : PayloadBase
+    {
+        private string _ssid { get; set; }
+        private string _password { get; set; }
+        private PayloadGenerator.WiFi.Authentication _authentication { get; set; }
+        private bool _isHiddenSSID { get; set; }
+        private bool _isHexStrings { get; set; }
+
+        public WiFiPayload(string ssid)
+        {
+            _ssid = ssid;
+            _password = "";
+            _authentication = PayloadGenerator.WiFi.Authentication.nopass;
+        }
+
+        public WiFiPayload(string ssid, string password, PayloadGenerator.WiFi.Authentication authentication)
+        {
+            _ssid = ssid;
+            _password = password;
+            _authentication = authentication;
+        }
+
+        public WiFiPayload WithHiddenSSID()
+        {
+            _isHiddenSSID = true;
+            return this;
+        }
+
+        public WiFiPayload WithHexStrings()
+        {
+            _isHexStrings = true;
+            return this;
+        }
+
+        protected override string Value
+        {
+            get
+            {
+                var wifi = new PayloadGenerator.WiFi(_ssid, _password, _authentication, _isHiddenSSID, _isHexStrings);
+                return wifi.ToString();
+            }
+        }
+    }
+}
diff --git a/QRCoder/Builders/Payloads/PayloadBase.cs b/QRCoder/Builders/Payloads/PayloadBase.cs
new file mode 100644
index 00000000..e29cabec
--- /dev/null
+++ b/QRCoder/Builders/Payloads/PayloadBase.cs
@@ -0,0 +1,21 @@
+namespace QRCoder.Builders.Payloads
+{
+    public abstract class PayloadBase : IPayload, IConfigurableEccLevel, IConfigurableEciMode, IConfigurableVersion
+    {
+        protected virtual QRCodeGenerator.ECCLevel EccLevel { get; set; } = QRCodeGenerator.ECCLevel.Default;
+        QRCodeGenerator.ECCLevel IConfigurableEccLevel.EccLevel { get => EccLevel; set => EccLevel = value; }
+
+        protected virtual QRCodeGenerator.EciMode EciMode { get; set; } = QRCodeGenerator.EciMode.Default;
+        QRCodeGenerator.EciMode IConfigurableEciMode.EciMode { get => EciMode; set => EciMode = value; }
+
+        protected virtual int Version { get; set; } = -1;
+        int IConfigurableVersion.Version { get => Version; set => Version = value; }
+
+        protected abstract string Value { get; }
+
+        public virtual QRCodeData ToMatrix()
+        {
+            return QRCodeGenerator.GenerateQrCode(Value, EccLevel, false, false, EciMode, Version);
+        }
+    }
+}
diff --git a/QRCoder/Builders/Payloads/PayloadExtensions.cs b/QRCoder/Builders/Payloads/PayloadExtensions.cs
new file mode 100644
index 00000000..6f849302
--- /dev/null
+++ b/QRCoder/Builders/Payloads/PayloadExtensions.cs
@@ -0,0 +1,47 @@
+using QRCoder.Builders.Payloads;
+using QRCoder.Builders.Renderers;
+using QRCoder.Builders.Renderers.Implementations;
+
+namespace QRCoder
+{
+    public static class PayloadExtensions
+    {
+        public static T WithErrorCorrection<T>(this T payload, QRCodeGenerator.ECCLevel eccLevel)
+            where T : IConfigurableEccLevel
+        {
+            payload.EccLevel = eccLevel;
+            return payload;
+        }
+
+        public static T WithEciMode<T>(this T payload, QRCodeGenerator.EciMode eciMode)
+            where T : IConfigurableEciMode
+        {
+            payload.EciMode = eciMode;
+            return payload;
+        }
+
+        public static T WithVersion<T>(this T payload, int version)
+            where T : IConfigurableVersion
+        {
+            payload.Version = version;
+            return payload;
+        }
+
+        public static T RenderWith<T>(this IPayload payload)
+            where T : IRenderer, new()
+        {
+            var renderer = new T();
+            renderer.Payload = payload;
+            return renderer;
+        }
+
+        public static T RenderWith<T>(this IPayload payload, int pixelsPerModule)
+            where T : IRenderer, IConfigurablePixelsPerModule, new()
+        {
+            var renderer = new T();
+            renderer.Payload = payload;
+            renderer.PixelsPerModule = pixelsPerModule;
+            return renderer;
+        }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/IConfigurablePixelsPerModule.cs b/QRCoder/Builders/Renderers/IConfigurablePixelsPerModule.cs
new file mode 100644
index 00000000..5893f814
--- /dev/null
+++ b/QRCoder/Builders/Renderers/IConfigurablePixelsPerModule.cs
@@ -0,0 +1,7 @@
+namespace QRCoder.Builders.Renderers
+{
+    public interface IConfigurablePixelsPerModule
+    {
+        int PixelsPerModule { get; set; }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/IConfigurableQuietZones.cs b/QRCoder/Builders/Renderers/IConfigurableQuietZones.cs
new file mode 100644
index 00000000..7c6be637
--- /dev/null
+++ b/QRCoder/Builders/Renderers/IConfigurableQuietZones.cs
@@ -0,0 +1,7 @@
+namespace QRCoder.Builders.Renderers
+{
+    public interface IConfigurableQuietZones
+    {
+        bool QuietZone { get; set; }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/IRenderer.cs b/QRCoder/Builders/Renderers/IRenderer.cs
new file mode 100644
index 00000000..ef20ac23
--- /dev/null
+++ b/QRCoder/Builders/Renderers/IRenderer.cs
@@ -0,0 +1,9 @@
+using QRCoder.Builders.Payloads;
+
+namespace QRCoder.Builders.Renderers
+{
+    public interface IRenderer
+    {
+        IPayload Payload { set; }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/IStreamRenderer.cs b/QRCoder/Builders/Renderers/IStreamRenderer.cs
new file mode 100644
index 00000000..2f4e3c44
--- /dev/null
+++ b/QRCoder/Builders/Renderers/IStreamRenderer.cs
@@ -0,0 +1,9 @@
+using System.IO;
+
+namespace QRCoder.Builders.Renderers
+{
+    public interface IStreamRenderer
+    {
+        MemoryStream ToStream();
+    }
+}
diff --git a/QRCoder/Builders/Renderers/ITextRenderer.cs b/QRCoder/Builders/Renderers/ITextRenderer.cs
new file mode 100644
index 00000000..897752af
--- /dev/null
+++ b/QRCoder/Builders/Renderers/ITextRenderer.cs
@@ -0,0 +1,9 @@
+using System.IO;
+
+namespace QRCoder.Builders.Renderers
+{
+    public interface ITextRenderer
+    {
+        string ToString();
+    }
+}
diff --git a/QRCoder/Builders/Renderers/Implementations/AsciiRenderer.cs b/QRCoder/Builders/Renderers/Implementations/AsciiRenderer.cs
new file mode 100644
index 00000000..9d34f388
--- /dev/null
+++ b/QRCoder/Builders/Renderers/Implementations/AsciiRenderer.cs
@@ -0,0 +1,46 @@
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class AsciiRenderer : RendererBase, ITextRenderer
+    {
+        private string _darkString = "██";
+        private string _lightString = "  ";
+        private int _repeatPerModule = 1;
+        private string _endOfLine = System.Environment.NewLine;
+        private bool _inverseDarkLight = false;
+
+        public AsciiRenderer WithText(string darkString, string lightString)
+        {
+            _darkString = darkString;
+            _lightString = lightString;
+            return this;
+        }
+
+        public AsciiRenderer WithRepeatPerModule(int repeatPerModule)
+        {
+            _repeatPerModule = repeatPerModule;
+            return this;
+        }
+
+        public AsciiRenderer WithEndOfLine(string endOfLine)
+        {
+            _endOfLine = endOfLine;
+            return this;
+        }
+
+        public AsciiRenderer WithInverseDarkLight()
+        {
+            _inverseDarkLight = true;
+            return this;
+        }
+
+        public override string ToString()
+        {
+            return new AsciiQRCode(QrCodeData).GetGraphic(
+                _repeatPerModule,
+                _inverseDarkLight ? _lightString : _darkString,
+                _inverseDarkLight ? _darkString : _lightString,
+                QuietZone,
+                _endOfLine);
+        }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/Implementations/PngRenderer.cs b/QRCoder/Builders/Renderers/Implementations/PngRenderer.cs
new file mode 100644
index 00000000..21a24957
--- /dev/null
+++ b/QRCoder/Builders/Renderers/Implementations/PngRenderer.cs
@@ -0,0 +1,43 @@
+using System.IO;
+using QRCoder.Builders.Payloads;
+
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class PngRenderer : RendererBase, IConfigurablePixelsPerModule, IStreamRenderer
+    {
+        private int _pixelsPerModule = 10;
+        private byte[] _darkColor;
+        private byte[] _lightColor;
+
+        int IConfigurablePixelsPerModule.PixelsPerModule { get => _pixelsPerModule; set => _pixelsPerModule = value; }
+
+#if !NETSTANDARD1_3
+        public PngRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor)
+        {
+            _darkColor = new byte[] { darkColor.R, darkColor.G, darkColor.B, darkColor.A };
+            _lightColor = new byte[] { lightColor.R, lightColor.G, lightColor.B, lightColor.A };
+            return this;
+        }
+#endif
+
+        public PngRenderer WithColors(byte[] darkColor, byte[] lightColor)
+        {
+            _darkColor = darkColor;
+            _lightColor = lightColor;
+            return this;
+        }
+
+        public byte[] ToArray()
+        {
+            if (_darkColor == null && _lightColor == null)
+                return new PngByteQRCode(QrCodeData).GetGraphic(_pixelsPerModule, QuietZone);
+            return new PngByteQRCode(QrCodeData).GetGraphic(_pixelsPerModule, _darkColor, _lightColor, QuietZone);
+        }
+
+        public MemoryStream ToStream()
+        {
+            var arr = ToArray();
+            return new MemoryStream(arr, 0, arr.Length, false, true);
+        }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/Implementations/SvgRenderer.cs b/QRCoder/Builders/Renderers/Implementations/SvgRenderer.cs
new file mode 100644
index 00000000..30cc87ff
--- /dev/null
+++ b/QRCoder/Builders/Renderers/Implementations/SvgRenderer.cs
@@ -0,0 +1,42 @@
+#if !NETSTANDARD1_3
+using System.Drawing;
+
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class SvgRenderer : RendererBase, IConfigurablePixelsPerModule, ITextRenderer
+    {
+        private int _pixelsPerModule = 10;
+        private Color _darkColor;
+        private Color _lightColor;
+        private SvgQRCode.SvgLogo _logo;
+        private SvgQRCode.SizingMode _sizingMode = SvgQRCode.SizingMode.WidthHeightAttribute;
+
+        int IConfigurablePixelsPerModule.PixelsPerModule { get => _pixelsPerModule; set => _pixelsPerModule = value; }
+
+        public SvgRenderer WithColors(Color darkColor, Color lightColor)
+        {
+            _darkColor = darkColor;
+            _lightColor = lightColor;
+            return this;
+        }
+
+        public SvgRenderer WithLogo(SvgQRCode.SvgLogo logo)
+        {
+            _logo = logo;
+            return this;
+        }
+
+        public SvgRenderer WithSizingMode(SvgQRCode.SizingMode sizingMode)
+        {
+            _sizingMode = sizingMode;
+            return this;
+        }
+
+        public override string ToString()
+        {
+            return new SvgQRCode(QrCodeData).GetGraphic(
+                _pixelsPerModule, _darkColor, _lightColor, QuietZone, _sizingMode, _logo);
+        }
+    }
+}
+#endif
diff --git a/QRCoder/Builders/Renderers/Implementations/SystemDrawingRenderer.cs b/QRCoder/Builders/Renderers/Implementations/SystemDrawingRenderer.cs
new file mode 100644
index 00000000..5d3025e2
--- /dev/null
+++ b/QRCoder/Builders/Renderers/Implementations/SystemDrawingRenderer.cs
@@ -0,0 +1,64 @@
+#if SYSTEM_DRAWING
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class SystemDrawingRenderer : RendererBase, IConfigurablePixelsPerModule, IStreamRenderer
+    {
+        private Color _darkColor = Color.Black;
+        private Color _lightColor = Color.White;
+        private int _pixelsPerModule;
+        private Bitmap _icon;
+        private double _iconSizePercent = 15;
+        private int _iconBorderWidth = 0;
+        private Color? _iconBackgroundColor;
+        private ImageFormat _imageFormat = ImageFormat.Png;
+
+        int IConfigurablePixelsPerModule.PixelsPerModule { get => _pixelsPerModule; set => _pixelsPerModule = value; }
+
+        public SystemDrawingRenderer WithColors(Color darkColor, Color lightColor)
+        {
+            _darkColor = darkColor;
+            _lightColor = lightColor;
+            return this;
+        }
+
+        public SystemDrawingRenderer WithIcon(Bitmap icon, double iconSizePercent = 15, int iconBorderWidth = 0, Color? iconBackgroundColor = null)
+        {
+            _icon = icon;
+            _iconSizePercent = iconSizePercent;
+            _iconBorderWidth = iconBorderWidth;
+            _iconBackgroundColor = iconBackgroundColor;
+            return this;
+        }
+
+        public SystemDrawingRenderer WithImageFormat(ImageFormat imageFormat)
+        {
+            _imageFormat = imageFormat;
+            return this;
+        }
+
+        public Bitmap ToBitmap()
+        {
+            return new QRCode(QrCodeData).GetGraphic(
+                _pixelsPerModule, _darkColor, _lightColor, 
+                _icon, (int)Math.Round(_iconSizePercent), _iconBorderWidth, 
+                QuietZone, _iconBackgroundColor);
+        }
+
+        public MemoryStream ToStream()
+        {
+            var ms = new MemoryStream();
+            using (var bitmap = ToBitmap())
+            {
+                bitmap.Save(ms, _imageFormat);
+            }
+            ms.Position = 0;
+            return ms;
+        }
+    }
+}
+#endif
\ No newline at end of file
diff --git a/QRCoder/Builders/Renderers/RendererBase.cs b/QRCoder/Builders/Renderers/RendererBase.cs
new file mode 100644
index 00000000..7478e4fe
--- /dev/null
+++ b/QRCoder/Builders/Renderers/RendererBase.cs
@@ -0,0 +1,13 @@
+using QRCoder.Builders.Payloads;
+
+namespace QRCoder.Builders.Renderers
+{
+    public abstract class RendererBase : IRenderer, IConfigurableQuietZones
+    {
+        protected QRCodeData QrCodeData { private set; get; }
+
+        protected bool QuietZone { get; set; } = true;
+        bool IConfigurableQuietZones.QuietZone { get => QuietZone; set => QuietZone = value; }
+        IPayload IRenderer.Payload { set => QrCodeData = value.ToMatrix(); }
+    }
+}
diff --git a/QRCoder/Builders/Renderers/RendererExtensions.cs b/QRCoder/Builders/Renderers/RendererExtensions.cs
new file mode 100644
index 00000000..3eb661ef
--- /dev/null
+++ b/QRCoder/Builders/Renderers/RendererExtensions.cs
@@ -0,0 +1,104 @@
+using System;
+using System.IO;
+using System.Text;
+using QRCoder.Builders.Renderers;
+
+namespace QRCoder
+{
+    public static class RendererExtensions
+    {
+        public static T WithQuietZone<T>(this T payload, bool value)
+            where T : IConfigurableQuietZones
+        {
+            payload.QuietZone = value;
+            return payload;
+        }
+
+        public static T WithPixelsPerModule<T>(this T payload, int value)
+            where T : IConfigurablePixelsPerModule
+        {
+            payload.PixelsPerModule = value;
+            return payload;
+        }
+
+        public static void ToStream(this IStreamRenderer streamRenderer, Stream stream)
+        {
+            var memoryStream = streamRenderer.ToStream();
+            memoryStream.CopyTo(stream);
+        }
+
+        public static byte[] ToArray(this IStreamRenderer streamRenderer)
+        {
+            var memoryStream = streamRenderer.ToStream();
+#if NETSTANDARD || NETCOREAPP // todo: target .NET Framework 4.6 or newer so this code path is supported
+            // by using TryGetBuffer, there is extremely small consequence to wrapping a byte[] in a MemoryStream temporarily
+            if (memoryStream.TryGetBuffer(out var buffer) && buffer.Count == buffer.Array.Length)
+            {
+                return buffer.Array;
+            }
+#endif
+            return memoryStream.ToArray();
+        }
+
+        public static ArraySegment<byte> ToArraySegment(this IStreamRenderer streamRenderer)
+        {
+            var memoryStream = streamRenderer.ToStream();
+#if NETSTANDARD || NETCOREAPP // todo: target .NET Framework 4.6 or newer so this code path is supported
+            if (memoryStream.TryGetBuffer(out var buffer))
+            {
+                return buffer;
+            }
+#else
+            try
+            {
+                var buffer = memoryStream.GetBuffer();
+                return new ArraySegment<byte>(buffer, 0, (int)memoryStream.Length);
+            }
+            catch { }
+#endif
+            return new ArraySegment<byte>(memoryStream.ToArray());
+        }
+
+        public static string ToBase64String(this IStreamRenderer streamRenderer)
+        {
+            var data = ToArraySegment(streamRenderer);
+            return Convert.ToBase64String(data.Array, data.Offset, data.Count);
+        }
+
+        public static void ToFile(this IStreamRenderer streamRenderer, string fileName)
+        {
+            var memoryStream = streamRenderer.ToStream();
+            using (var file = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
+            {
+                memoryStream.CopyTo(file);
+            }
+        }
+
+        public static void ToFile(this ITextRenderer textRenderer, string fileName, Encoding encoding = null)
+        {
+            File.WriteAllText(fileName, textRenderer.ToString(), encoding ?? Encoding.UTF8);
+        }
+
+        public static void ToStream(this ITextRenderer textRenderer, Stream stream, Encoding encoding = null)
+        {
+            using (var writer = new StreamWriter(stream, encoding))
+            {
+                writer.Write(textRenderer.ToString());
+                writer.Flush();
+            }
+        }
+
+        public static MemoryStream ToStream(this ITextRenderer textRenderer, Encoding encoding = null)
+        {
+            var str = textRenderer.ToString();
+            var ms = new MemoryStream(str.Length);
+            using (var writer = new StreamWriter(ms, encoding ?? Encoding.UTF8))
+            {
+                writer.Write(str);
+                writer.Flush();
+            }
+            ms.Position = 0;
+            return ms;
+        }
+    }
+}
diff --git a/QRCoder/QRCodeBuilder.cs b/QRCoder/QRCodeBuilder.cs
new file mode 100644
index 00000000..4cb42c8d
--- /dev/null
+++ b/QRCoder/QRCodeBuilder.cs
@@ -0,0 +1,52 @@
+using QRCoder.Builders.Payloads.Implementations;
+
+namespace QRCoder
+{
+    public static class QRCodeBuilder
+    {
+        public static StringPayload CreateUrl(string url)
+        {
+            return new StringPayload(url);
+        }
+
+        public static StringPayload CreateSms(string number, PayloadGenerator.SMS.SMSEncoding encoding = PayloadGenerator.SMS.SMSEncoding.SMS)
+        {
+            return new StringPayload(new PayloadGenerator.SMS(number, encoding).ToString());
+        }
+
+        public static EmailPayload CreateEmail(string address)
+        {
+            return new EmailPayload(address);
+        }
+
+        public static WiFiPayload CreateWiFi(string ssid)
+        {
+            return new WiFiPayload(ssid);
+        }
+
+        public static WiFiPayload CreateWiFi(string ssid, string password, PayloadGenerator.WiFi.Authentication authentication)
+        {
+            return new WiFiPayload(ssid, password, authentication);
+        }
+
+        public static StringPayload CreateBookmark(string url, string title)
+        {
+            return new StringPayload(new PayloadGenerator.Bookmark(url, title).ToString());
+        }
+
+        public static StringPayload CreateMMS(string number, PayloadGenerator.MMS.MMSEncoding encoding = PayloadGenerator.MMS.MMSEncoding.MMS)
+        {
+            return new StringPayload(new PayloadGenerator.MMS(number, encoding).ToString());
+        }
+
+        public static StringPayload CreateMMS(string number, string subject, PayloadGenerator.MMS.MMSEncoding encoding = PayloadGenerator.MMS.MMSEncoding.MMS)
+        {
+            return new StringPayload(new PayloadGenerator.MMS(number, subject, encoding).ToString());
+        }
+
+        public static StringPayload CreatePhoneNumber(string number)
+        {
+            return new StringPayload("tel:" + number);
+        }
+    }
+}
diff --git a/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt b/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
index 4c5794a4..37bc8feb 100644
--- a/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
+++ b/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
@@ -95,6 +95,19 @@ namespace QRCoder
         public static byte[] GetQRCode(string txt, QRCoder.QRCodeGenerator.ECCLevel eccLevel, int size) { }
         public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
     }
+    public static class PayloadExtensions
+    {
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload)
+            where T : QRCoder.Builders.Renderers.IRenderer, new () { }
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload, int pixelsPerModule)
+            where T : QRCoder.Builders.Renderers.IRenderer, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, new () { }
+        public static T WithEciMode<T>(this T payload, QRCoder.QRCodeGenerator.EciMode eciMode)
+            where T : QRCoder.Builders.Payloads.IConfigurableEciMode { }
+        public static T WithErrorCorrection<T>(this T payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel)
+            where T : QRCoder.Builders.Payloads.IConfigurableEccLevel { }
+        public static T WithVersion<T>(this T payload, int version)
+            where T : QRCoder.Builders.Payloads.IConfigurableVersion { }
+    }
     public static class PayloadGenerator
     {
         public static bool ChecksumMod10(string digits) { }
@@ -867,6 +880,18 @@ namespace QRCoder
         public System.Drawing.Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true) { }
         public System.Drawing.Bitmap GetGraphic(int pixelsPerModule, System.Drawing.Color darkColor, System.Drawing.Color lightColor, System.Drawing.Bitmap icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true, System.Drawing.Color? iconBackgroundColor = default) { }
     }
+    public static class QRCodeBuilder
+    {
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateBookmark(string url, string title) { }
+        public static QRCoder.Builders.Payloads.Implementations.EmailPayload CreateEmail(string address) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, string subject, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreatePhoneNumber(string number) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateSms(string number, QRCoder.PayloadGenerator.SMS.SMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateUrl(string url) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+    }
     public class QRCodeData : System.IDisposable
     {
         public QRCodeData(int version) { }
@@ -917,6 +942,21 @@ namespace QRCoder
     {
         public static System.Drawing.Bitmap GetQRCode(string plainText, int pixelsPerModule, System.Drawing.Color darkColor, System.Drawing.Color lightColor, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, System.Drawing.Bitmap icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true) { }
     }
+    public static class RendererExtensions
+    {
+        public static byte[] ToArray(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static System.ArraySegment<byte> ToArraySegment(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static string ToBase64String(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, string fileName) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, string fileName, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, System.IO.Stream stream) { }
+        public static System.IO.MemoryStream ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.IO.Stream stream, System.Text.Encoding encoding = null) { }
+        public static T WithPixelsPerModule<T>(this T payload, int value)
+            where T : QRCoder.Builders.Renderers.IConfigurablePixelsPerModule { }
+        public static T WithQuietZone<T>(this T payload, bool value)
+            where T : QRCoder.Builders.Renderers.IConfigurableQuietZones { }
+    }
     public class SvgQRCode : QRCoder.AbstractQRCode, System.IDisposable
     {
         public SvgQRCode() { }
@@ -957,6 +997,124 @@ namespace QRCoder
         public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, bool drawQuietZones = true, QRCoder.SvgQRCode.SizingMode sizingMode = 0, QRCoder.SvgQRCode.SvgLogo logo = null) { }
     }
 }
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableEccLevel
+    {
+        QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+    }
+    public interface IConfigurableEciMode
+    {
+        QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+    }
+    public interface IConfigurableVersion
+    {
+        int Version { get; set; }
+    }
+    public interface IPayload
+    {
+        QRCoder.QRCodeData ToMatrix();
+    }
+    public abstract class PayloadBase : QRCoder.Builders.Payloads.IConfigurableEccLevel, QRCoder.Builders.Payloads.IConfigurableEciMode, QRCoder.Builders.Payloads.IConfigurableVersion, QRCoder.Builders.Payloads.IPayload
+    {
+        protected PayloadBase() { }
+        protected abstract string Value { get; }
+        protected virtual QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+        protected virtual QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+        protected virtual int Version { get; set; }
+        public virtual QRCoder.QRCodeData ToMatrix() { }
+    }
+}
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class EmailPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public EmailPayload(string address) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithBody(string body) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithEncoding(QRCoder.PayloadGenerator.Mail.MailEncoding encoding) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithSubject(string subject) { }
+    }
+    public class StringPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public StringPayload(string data) { }
+        protected override string Value { get; }
+    }
+    public class WiFiPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public WiFiPayload(string ssid) { }
+        public WiFiPayload(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHexStrings() { }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHiddenSSID() { }
+    }
+}
+namespace QRCoder.Builders.Renderers
+{
+    public interface IConfigurablePixelsPerModule
+    {
+        int PixelsPerModule { get; set; }
+    }
+    public interface IConfigurableQuietZones
+    {
+        bool QuietZone { get; set; }
+    }
+    public interface IRenderer
+    {
+        QRCoder.Builders.Payloads.IPayload Payload { set; }
+    }
+    public interface IStreamRenderer
+    {
+        System.IO.MemoryStream ToStream();
+    }
+    public interface ITextRenderer
+    {
+        string ToString();
+    }
+    public abstract class RendererBase : QRCoder.Builders.Renderers.IConfigurableQuietZones, QRCoder.Builders.Renderers.IRenderer
+    {
+        protected RendererBase() { }
+        protected QRCoder.QRCodeData QrCodeData { get; }
+        protected bool QuietZone { get; set; }
+    }
+}
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class AsciiRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public AsciiRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithEndOfLine(string endOfLine) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithInverseDarkLight() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithRepeatPerModule(int repeatPerModule) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithText(string darkString, string lightString) { }
+    }
+    public class PngRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.IStreamRenderer
+    {
+        public PngRenderer() { }
+        public byte[] ToArray() { }
+        public System.IO.MemoryStream ToStream() { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(byte[] darkColor, byte[] lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+    }
+    public class SvgRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public SvgRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithLogo(QRCoder.SvgQRCode.SvgLogo logo) { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithSizingMode(QRCoder.SvgQRCode.SizingMode sizingMode) { }
+    }
+    public class SystemDrawingRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.IStreamRenderer
+    {
+        public SystemDrawingRenderer() { }
+        public System.Drawing.Bitmap ToBitmap() { }
+        public System.IO.MemoryStream ToStream() { }
+        public QRCoder.Builders.Renderers.Implementations.SystemDrawingRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.SystemDrawingRenderer WithIcon(System.Drawing.Bitmap icon, double iconSizePercent = 15, int iconBorderWidth = 0, System.Drawing.Color? iconBackgroundColor = default) { }
+        public QRCoder.Builders.Renderers.Implementations.SystemDrawingRenderer WithImageFormat(System.Drawing.Imaging.ImageFormat imageFormat) { }
+    }
+}
 namespace QRCoder.Exceptions
 {
     public class DataTooLongException : System.Exception
diff --git a/QRCoderApiTests/net60-windows/QRCoder.approved.txt b/QRCoderApiTests/net60-windows/QRCoder.approved.txt
index 52c12031..cda47a06 100644
--- a/QRCoderApiTests/net60-windows/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60-windows/QRCoder.approved.txt
@@ -100,6 +100,19 @@ namespace QRCoder
         public static byte[] GetQRCode(string txt, QRCoder.QRCodeGenerator.ECCLevel eccLevel, int size) { }
         public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
     }
+    public static class PayloadExtensions
+    {
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload)
+            where T : QRCoder.Builders.Renderers.IRenderer, new () { }
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload, int pixelsPerModule)
+            where T : QRCoder.Builders.Renderers.IRenderer, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, new () { }
+        public static T WithEciMode<T>(this T payload, QRCoder.QRCodeGenerator.EciMode eciMode)
+            where T : QRCoder.Builders.Payloads.IConfigurableEciMode { }
+        public static T WithErrorCorrection<T>(this T payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel)
+            where T : QRCoder.Builders.Payloads.IConfigurableEccLevel { }
+        public static T WithVersion<T>(this T payload, int version)
+            where T : QRCoder.Builders.Payloads.IConfigurableVersion { }
+    }
     public static class PayloadGenerator
     {
         public static bool ChecksumMod10(string digits) { }
@@ -875,6 +888,18 @@ namespace QRCoder
         public System.Drawing.Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true) { }
         public System.Drawing.Bitmap GetGraphic(int pixelsPerModule, System.Drawing.Color darkColor, System.Drawing.Color lightColor, System.Drawing.Bitmap icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true, System.Drawing.Color? iconBackgroundColor = default) { }
     }
+    public static class QRCodeBuilder
+    {
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateBookmark(string url, string title) { }
+        public static QRCoder.Builders.Payloads.Implementations.EmailPayload CreateEmail(string address) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, string subject, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreatePhoneNumber(string number) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateSms(string number, QRCoder.PayloadGenerator.SMS.SMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateUrl(string url) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+    }
     public class QRCodeData : System.IDisposable
     {
         public QRCodeData(int version) { }
@@ -926,6 +951,21 @@ namespace QRCoder
     {
         public static System.Drawing.Bitmap GetQRCode(string plainText, int pixelsPerModule, System.Drawing.Color darkColor, System.Drawing.Color lightColor, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, System.Drawing.Bitmap icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true) { }
     }
+    public static class RendererExtensions
+    {
+        public static byte[] ToArray(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static System.ArraySegment<byte> ToArraySegment(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static string ToBase64String(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, string fileName) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, string fileName, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, System.IO.Stream stream) { }
+        public static System.IO.MemoryStream ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.IO.Stream stream, System.Text.Encoding encoding = null) { }
+        public static T WithPixelsPerModule<T>(this T payload, int value)
+            where T : QRCoder.Builders.Renderers.IConfigurablePixelsPerModule { }
+        public static T WithQuietZone<T>(this T payload, bool value)
+            where T : QRCoder.Builders.Renderers.IConfigurableQuietZones { }
+    }
     public class SvgQRCode : QRCoder.AbstractQRCode, System.IDisposable
     {
         public SvgQRCode() { }
@@ -967,6 +1007,124 @@ namespace QRCoder
         public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, bool drawQuietZones = true, QRCoder.SvgQRCode.SizingMode sizingMode = 0, QRCoder.SvgQRCode.SvgLogo logo = null) { }
     }
 }
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableEccLevel
+    {
+        QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+    }
+    public interface IConfigurableEciMode
+    {
+        QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+    }
+    public interface IConfigurableVersion
+    {
+        int Version { get; set; }
+    }
+    public interface IPayload
+    {
+        QRCoder.QRCodeData ToMatrix();
+    }
+    public abstract class PayloadBase : QRCoder.Builders.Payloads.IConfigurableEccLevel, QRCoder.Builders.Payloads.IConfigurableEciMode, QRCoder.Builders.Payloads.IConfigurableVersion, QRCoder.Builders.Payloads.IPayload
+    {
+        protected PayloadBase() { }
+        protected abstract string Value { get; }
+        protected virtual QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+        protected virtual QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+        protected virtual int Version { get; set; }
+        public virtual QRCoder.QRCodeData ToMatrix() { }
+    }
+}
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class EmailPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public EmailPayload(string address) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithBody(string body) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithEncoding(QRCoder.PayloadGenerator.Mail.MailEncoding encoding) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithSubject(string subject) { }
+    }
+    public class StringPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public StringPayload(string data) { }
+        protected override string Value { get; }
+    }
+    public class WiFiPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public WiFiPayload(string ssid) { }
+        public WiFiPayload(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHexStrings() { }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHiddenSSID() { }
+    }
+}
+namespace QRCoder.Builders.Renderers
+{
+    public interface IConfigurablePixelsPerModule
+    {
+        int PixelsPerModule { get; set; }
+    }
+    public interface IConfigurableQuietZones
+    {
+        bool QuietZone { get; set; }
+    }
+    public interface IRenderer
+    {
+        QRCoder.Builders.Payloads.IPayload Payload { set; }
+    }
+    public interface IStreamRenderer
+    {
+        System.IO.MemoryStream ToStream();
+    }
+    public interface ITextRenderer
+    {
+        string ToString();
+    }
+    public abstract class RendererBase : QRCoder.Builders.Renderers.IConfigurableQuietZones, QRCoder.Builders.Renderers.IRenderer
+    {
+        protected RendererBase() { }
+        protected QRCoder.QRCodeData QrCodeData { get; }
+        protected bool QuietZone { get; set; }
+    }
+}
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class AsciiRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public AsciiRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithEndOfLine(string endOfLine) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithInverseDarkLight() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithRepeatPerModule(int repeatPerModule) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithText(string darkString, string lightString) { }
+    }
+    public class PngRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.IStreamRenderer
+    {
+        public PngRenderer() { }
+        public byte[] ToArray() { }
+        public System.IO.MemoryStream ToStream() { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(byte[] darkColor, byte[] lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+    }
+    public class SvgRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public SvgRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithLogo(QRCoder.SvgQRCode.SvgLogo logo) { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithSizingMode(QRCoder.SvgQRCode.SizingMode sizingMode) { }
+    }
+    public class SystemDrawingRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.IStreamRenderer
+    {
+        public SystemDrawingRenderer() { }
+        public System.Drawing.Bitmap ToBitmap() { }
+        public System.IO.MemoryStream ToStream() { }
+        public QRCoder.Builders.Renderers.Implementations.SystemDrawingRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.SystemDrawingRenderer WithIcon(System.Drawing.Bitmap icon, double iconSizePercent = 15, int iconBorderWidth = 0, System.Drawing.Color? iconBackgroundColor = default) { }
+        public QRCoder.Builders.Renderers.Implementations.SystemDrawingRenderer WithImageFormat(System.Drawing.Imaging.ImageFormat imageFormat) { }
+    }
+}
 namespace QRCoder.Exceptions
 {
     public class DataTooLongException : System.Exception
diff --git a/QRCoderApiTests/net60/QRCoder.approved.txt b/QRCoderApiTests/net60/QRCoder.approved.txt
index fd21ee83..6d7a1b97 100644
--- a/QRCoderApiTests/net60/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60/QRCoder.approved.txt
@@ -58,6 +58,19 @@ namespace QRCoder
         public static byte[] GetQRCode(string txt, QRCoder.QRCodeGenerator.ECCLevel eccLevel, int size) { }
         public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
     }
+    public static class PayloadExtensions
+    {
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload)
+            where T : QRCoder.Builders.Renderers.IRenderer, new () { }
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload, int pixelsPerModule)
+            where T : QRCoder.Builders.Renderers.IRenderer, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, new () { }
+        public static T WithEciMode<T>(this T payload, QRCoder.QRCodeGenerator.EciMode eciMode)
+            where T : QRCoder.Builders.Payloads.IConfigurableEciMode { }
+        public static T WithErrorCorrection<T>(this T payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel)
+            where T : QRCoder.Builders.Payloads.IConfigurableEccLevel { }
+        public static T WithVersion<T>(this T payload, int version)
+            where T : QRCoder.Builders.Payloads.IConfigurableVersion { }
+    }
     public static class PayloadGenerator
     {
         public static bool ChecksumMod10(string digits) { }
@@ -809,6 +822,18 @@ namespace QRCoder
     {
         public static string GetQRCode(string plainText, int pointsPerModule, string darkColorHex, string lightColorHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, bool drawQuietZones = true, bool epsFormat = false) { }
     }
+    public static class QRCodeBuilder
+    {
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateBookmark(string url, string title) { }
+        public static QRCoder.Builders.Payloads.Implementations.EmailPayload CreateEmail(string address) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, string subject, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreatePhoneNumber(string number) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateSms(string number, QRCoder.PayloadGenerator.SMS.SMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateUrl(string url) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+    }
     public class QRCodeData : System.IDisposable
     {
         public QRCodeData(int version) { }
@@ -855,6 +880,21 @@ namespace QRCoder
             Utf8 = 26,
         }
     }
+    public static class RendererExtensions
+    {
+        public static byte[] ToArray(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static System.ArraySegment<byte> ToArraySegment(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static string ToBase64String(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, string fileName) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, string fileName, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, System.IO.Stream stream) { }
+        public static System.IO.MemoryStream ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.IO.Stream stream, System.Text.Encoding encoding = null) { }
+        public static T WithPixelsPerModule<T>(this T payload, int value)
+            where T : QRCoder.Builders.Renderers.IConfigurablePixelsPerModule { }
+        public static T WithQuietZone<T>(this T payload, bool value)
+            where T : QRCoder.Builders.Renderers.IConfigurableQuietZones { }
+    }
     public class SvgQRCode : QRCoder.AbstractQRCode, System.IDisposable
     {
         public SvgQRCode() { }
@@ -894,6 +934,115 @@ namespace QRCoder
         public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, bool drawQuietZones = true, QRCoder.SvgQRCode.SizingMode sizingMode = 0, QRCoder.SvgQRCode.SvgLogo logo = null) { }
     }
 }
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableEccLevel
+    {
+        QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+    }
+    public interface IConfigurableEciMode
+    {
+        QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+    }
+    public interface IConfigurableVersion
+    {
+        int Version { get; set; }
+    }
+    public interface IPayload
+    {
+        QRCoder.QRCodeData ToMatrix();
+    }
+    public abstract class PayloadBase : QRCoder.Builders.Payloads.IConfigurableEccLevel, QRCoder.Builders.Payloads.IConfigurableEciMode, QRCoder.Builders.Payloads.IConfigurableVersion, QRCoder.Builders.Payloads.IPayload
+    {
+        protected PayloadBase() { }
+        protected abstract string Value { get; }
+        protected virtual QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+        protected virtual QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+        protected virtual int Version { get; set; }
+        public virtual QRCoder.QRCodeData ToMatrix() { }
+    }
+}
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class EmailPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public EmailPayload(string address) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithBody(string body) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithEncoding(QRCoder.PayloadGenerator.Mail.MailEncoding encoding) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithSubject(string subject) { }
+    }
+    public class StringPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public StringPayload(string data) { }
+        protected override string Value { get; }
+    }
+    public class WiFiPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public WiFiPayload(string ssid) { }
+        public WiFiPayload(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHexStrings() { }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHiddenSSID() { }
+    }
+}
+namespace QRCoder.Builders.Renderers
+{
+    public interface IConfigurablePixelsPerModule
+    {
+        int PixelsPerModule { get; set; }
+    }
+    public interface IConfigurableQuietZones
+    {
+        bool QuietZone { get; set; }
+    }
+    public interface IRenderer
+    {
+        QRCoder.Builders.Payloads.IPayload Payload { set; }
+    }
+    public interface IStreamRenderer
+    {
+        System.IO.MemoryStream ToStream();
+    }
+    public interface ITextRenderer
+    {
+        string ToString();
+    }
+    public abstract class RendererBase : QRCoder.Builders.Renderers.IConfigurableQuietZones, QRCoder.Builders.Renderers.IRenderer
+    {
+        protected RendererBase() { }
+        protected QRCoder.QRCodeData QrCodeData { get; }
+        protected bool QuietZone { get; set; }
+    }
+}
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class AsciiRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public AsciiRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithEndOfLine(string endOfLine) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithInverseDarkLight() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithRepeatPerModule(int repeatPerModule) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithText(string darkString, string lightString) { }
+    }
+    public class PngRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.IStreamRenderer
+    {
+        public PngRenderer() { }
+        public byte[] ToArray() { }
+        public System.IO.MemoryStream ToStream() { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(byte[] darkColor, byte[] lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+    }
+    public class SvgRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public SvgRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithLogo(QRCoder.SvgQRCode.SvgLogo logo) { }
+        public QRCoder.Builders.Renderers.Implementations.SvgRenderer WithSizingMode(QRCoder.SvgQRCode.SizingMode sizingMode) { }
+    }
+}
 namespace QRCoder.Exceptions
 {
     public class DataTooLongException : System.Exception
diff --git a/QRCoderApiTests/netstandard13/QRCoder.approved.txt b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
index bada43f1..df46f4f1 100644
--- a/QRCoderApiTests/netstandard13/QRCoder.approved.txt
+++ b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
@@ -38,6 +38,19 @@ namespace QRCoder
         public static byte[] GetQRCode(string txt, QRCoder.QRCodeGenerator.ECCLevel eccLevel, int size) { }
         public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
     }
+    public static class PayloadExtensions
+    {
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload)
+            where T : QRCoder.Builders.Renderers.IRenderer, new () { }
+        public static T RenderWith<T>(this QRCoder.Builders.Payloads.IPayload payload, int pixelsPerModule)
+            where T : QRCoder.Builders.Renderers.IRenderer, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, new () { }
+        public static T WithEciMode<T>(this T payload, QRCoder.QRCodeGenerator.EciMode eciMode)
+            where T : QRCoder.Builders.Payloads.IConfigurableEciMode { }
+        public static T WithErrorCorrection<T>(this T payload, QRCoder.QRCodeGenerator.ECCLevel eccLevel)
+            where T : QRCoder.Builders.Payloads.IConfigurableEccLevel { }
+        public static T WithVersion<T>(this T payload, int version)
+            where T : QRCoder.Builders.Payloads.IConfigurableVersion { }
+    }
     public static class PayloadGenerator
     {
         public static bool ChecksumMod10(string digits) { }
@@ -774,6 +787,18 @@ namespace QRCoder
         public static byte[] GetQRCode(string txt, QRCoder.QRCodeGenerator.ECCLevel eccLevel, int size, bool drawQuietZones = true) { }
         public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1, bool drawQuietZones = true) { }
     }
+    public static class QRCodeBuilder
+    {
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateBookmark(string url, string title) { }
+        public static QRCoder.Builders.Payloads.Implementations.EmailPayload CreateEmail(string address) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateMMS(string number, string subject, QRCoder.PayloadGenerator.MMS.MMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreatePhoneNumber(string number) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateSms(string number, QRCoder.PayloadGenerator.SMS.SMSEncoding encoding = 0) { }
+        public static QRCoder.Builders.Payloads.Implementations.StringPayload CreateUrl(string url) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid) { }
+        public static QRCoder.Builders.Payloads.Implementations.WiFiPayload CreateWiFi(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+    }
     public class QRCodeData : System.IDisposable
     {
         public QRCodeData(int version) { }
@@ -820,6 +845,121 @@ namespace QRCoder
             Utf8 = 26,
         }
     }
+    public static class RendererExtensions
+    {
+        public static byte[] ToArray(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static System.ArraySegment<byte> ToArraySegment(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static string ToBase64String(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, string fileName) { }
+        public static void ToFile(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, string fileName, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.IStreamRenderer streamRenderer, System.IO.Stream stream) { }
+        public static System.IO.MemoryStream ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.Text.Encoding encoding = null) { }
+        public static void ToStream(this QRCoder.Builders.Renderers.ITextRenderer textRenderer, System.IO.Stream stream, System.Text.Encoding encoding = null) { }
+        public static T WithPixelsPerModule<T>(this T payload, int value)
+            where T : QRCoder.Builders.Renderers.IConfigurablePixelsPerModule { }
+        public static T WithQuietZone<T>(this T payload, bool value)
+            where T : QRCoder.Builders.Renderers.IConfigurableQuietZones { }
+    }
+}
+namespace QRCoder.Builders.Payloads
+{
+    public interface IConfigurableEccLevel
+    {
+        QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+    }
+    public interface IConfigurableEciMode
+    {
+        QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+    }
+    public interface IConfigurableVersion
+    {
+        int Version { get; set; }
+    }
+    public interface IPayload
+    {
+        QRCoder.QRCodeData ToMatrix();
+    }
+    public abstract class PayloadBase : QRCoder.Builders.Payloads.IConfigurableEccLevel, QRCoder.Builders.Payloads.IConfigurableEciMode, QRCoder.Builders.Payloads.IConfigurableVersion, QRCoder.Builders.Payloads.IPayload
+    {
+        protected PayloadBase() { }
+        protected abstract string Value { get; }
+        protected virtual QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; set; }
+        protected virtual QRCoder.QRCodeGenerator.EciMode EciMode { get; set; }
+        protected virtual int Version { get; set; }
+        public virtual QRCoder.QRCodeData ToMatrix() { }
+    }
+}
+namespace QRCoder.Builders.Payloads.Implementations
+{
+    public class EmailPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public EmailPayload(string address) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithBody(string body) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithEncoding(QRCoder.PayloadGenerator.Mail.MailEncoding encoding) { }
+        public QRCoder.Builders.Payloads.Implementations.EmailPayload WithSubject(string subject) { }
+    }
+    public class StringPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public StringPayload(string data) { }
+        protected override string Value { get; }
+    }
+    public class WiFiPayload : QRCoder.Builders.Payloads.PayloadBase
+    {
+        public WiFiPayload(string ssid) { }
+        public WiFiPayload(string ssid, string password, QRCoder.PayloadGenerator.WiFi.Authentication authentication) { }
+        protected override string Value { get; }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHexStrings() { }
+        public QRCoder.Builders.Payloads.Implementations.WiFiPayload WithHiddenSSID() { }
+    }
+}
+namespace QRCoder.Builders.Renderers
+{
+    public interface IConfigurablePixelsPerModule
+    {
+        int PixelsPerModule { get; set; }
+    }
+    public interface IConfigurableQuietZones
+    {
+        bool QuietZone { get; set; }
+    }
+    public interface IRenderer
+    {
+        QRCoder.Builders.Payloads.IPayload Payload { set; }
+    }
+    public interface IStreamRenderer
+    {
+        System.IO.MemoryStream ToStream();
+    }
+    public interface ITextRenderer
+    {
+        string ToString();
+    }
+    public abstract class RendererBase : QRCoder.Builders.Renderers.IConfigurableQuietZones, QRCoder.Builders.Renderers.IRenderer
+    {
+        protected RendererBase() { }
+        protected QRCoder.QRCodeData QrCodeData { get; }
+        protected bool QuietZone { get; set; }
+    }
+}
+namespace QRCoder.Builders.Renderers.Implementations
+{
+    public class AsciiRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.ITextRenderer
+    {
+        public AsciiRenderer() { }
+        public override string ToString() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithEndOfLine(string endOfLine) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithInverseDarkLight() { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithRepeatPerModule(int repeatPerModule) { }
+        public QRCoder.Builders.Renderers.Implementations.AsciiRenderer WithText(string darkString, string lightString) { }
+    }
+    public class PngRenderer : QRCoder.Builders.Renderers.RendererBase, QRCoder.Builders.Renderers.IConfigurablePixelsPerModule, QRCoder.Builders.Renderers.IStreamRenderer
+    {
+        public PngRenderer() { }
+        public byte[] ToArray() { }
+        public System.IO.MemoryStream ToStream() { }
+        public QRCoder.Builders.Renderers.Implementations.PngRenderer WithColors(byte[] darkColor, byte[] lightColor) { }
+    }
 }
 namespace QRCoder.Exceptions
 {
diff --git a/QRCoderTests/BuilderTests.cs b/QRCoderTests/BuilderTests.cs
new file mode 100644
index 00000000..16390d3a
--- /dev/null
+++ b/QRCoderTests/BuilderTests.cs
@@ -0,0 +1,90 @@
+#if NET5_0_OR_GREATER && SYSTEM_DRAWING
+using QRCoder;
+using QRCoder.Builders.Renderers.Implementations;
+using Shouldly;
+using Xunit;
+
+namespace QRCoderTests
+{
+    public class BuilderTests
+    {
+        [Fact]
+        public void EmailAsAscii()
+        {
+            var code = QRCodeBuilder.CreateEmail("test@microsoft.com")
+                .WithSubject("Testing")
+                .WithBody("Hello World!")
+                .WithErrorCorrection(QRCodeGenerator.ECCLevel.H)
+                .RenderWith<AsciiRenderer>()
+                .WithQuietZone(false)
+                .ToString();
+
+            code.ShouldBe(@"██████████████  ██        ████████████    ██████  ██      ████          ██  ██████████████
+██          ██  ██████    ██████████    ██████    ██    ██  ████  ██  ██    ██          ██
+██  ██████  ██  ██████                ████  ██      ████  ██  ██████  ██    ██  ██████  ██
+██  ██████  ██    ██      ██████        ██    ██████    ████          ████  ██  ██████  ██
+██  ██████  ██      ████████    ██    ████████████  ██  ██████  ██  ██████  ██  ██████  ██
+██          ██  ████████████████        ██      ████              ██        ██          ██
+██████████████  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██  ██████████████
+                ██████████████  ██████████      ██          ██  ██████  ██                
+    ██████  ██  ██  ██    ██████████  ████████████    ██████  ██  ██████  ██████    ██████
+    ████      ██████████      ██      ████  ██  ████  ██████████      ██████  ██  ██    ██
+    ██  ██  ████    ██    ██████      ████  ████    ██  ████  ██    ██  ████  ██  ██  ██  
+  ██  ██████  ██  ██  ████████  ██████████████████████  ████  ████    ████████████  ████  
+████████  ████  ██    ██████    ██████        ████    ████  ████  ██████                ██
+██    ████      ██        ██      ████████████  ██  ██    ██████      ██████  ██████  ████
+██  ██  ██  ██      ████        ██  ██  ██  ████  ████  ██    ██    ████  ████    ██████  
+██  ██  ████      ██  ██████    ████  ████████████████      ██  ██      ██████████████████
+  ████    ████████  ██  ████████  ██            ████  ██████████  ██      ████        ████
+    ██            ████              ██  ████    ██  ██████  ████████  ██████  ████      ██
+████  ██  ██████  ██████    ██████  ██████  ██    ██  ██    ██      ████  ██████  ██████  
+  ████  ████  ████      ██    ██████  ██    ████  ██████████  ██████████    ████████████  
+        ████████████████  ██      ████████████████  ██        ██      ████████████    ████
+██████████      ██          ████████  ████      ██████    ██  ██████  ████      ██  ██  ██
+██████████  ██  ████████████    ██████  ██  ██  ██  ██  ██  ██      ██  ██  ██  ████  ██  
+████    ██      ██    ██  ██    ██  ██████      ██    ████    ████  ██  ██      ██████████
+        ██████████        ██  ██    ████████████████  ██          ██  ██████████████  ████
+        ██          ████████  ██  ████  ████████  ████    ██  ██      ██    ██    ██    ██
+    ██████████          ██    ██  ██    ████  ██  ██████    ████  ██████  ██  ████    ██  
+████  ██  ██        ████  ████    ██        ████  ██    ██      ██    ██    ████  ████    
+████    ██████  ██          ██    ██  ████    ████    ██      ██  ██          ████    ████
+  ██  ██  ██      ██      ████████      ██████████  ████    ██        ██    ██    ████  ██
+██████    ████████████    ████        ████  ██  ████  ██  ████  ████████████      ██████  
+██    ██████  ██████  ██████  ████      ██████          ██  ██  ██    ██    ██████  ██    
+    ██  ██  ████  ████  ██  ████    ████    ██████████    ██████  ██      ██    ████  ██  
+████  ████            ████        ██  ████  ██    ██        ██    ██  ██  ██████    ██████
+        ██  ██████  ██          ██    ██  ████████████    ██████  ██████████  ████████    
+  ████████      ██  ██        ██        ██████  ████  ██████    ██  ████    ████  ██████  
+██    ████  ██  ████    ██████          ██████████████    ██████        ██████████    ████
+                ██    ██  ██████      ████      ██          ██  ██    ████      ██  ██  ██
+██████████████    ██  ██  ██  ██    ██████  ██  ██  ████████  ████      ██  ██  ████  ██  
+██          ██          ████        ██████      ████████      ████  ██████      ██████    
+██  ██████  ██  ████  ████  ██    ██    ██████████  ██    ██  ██        ████████████      
+██  ██████  ██  ████  ████    ██  ██████      ██████████    ████          ██  ████████████
+██  ██████  ██  ██  ████████████  ██  ████  ██        ██  ██  ██  ██  ████    ██  ████    
+██          ██        ████  ██    ██    ██  ██    ████████  ██  ██    ██  ██    ██████    
+██████████████    ██      ████████        ████  ██      ████      ██    ██  ██        ██  ", StringCompareShould.IgnoreLineEndings);
+        }
+
+        [Fact]
+        public void PhoneAsBitmap()
+        {
+            var image = QRCodeBuilder.CreatePhoneNumber("1234567890")
+                .WithErrorCorrection(QRCodeGenerator.ECCLevel.H)
+                .RenderWith<SystemDrawingRenderer>(20)
+                .WithQuietZone(false)
+                .ToBitmap();
+        }
+
+        [Fact]
+        public void MmsAsBase64()
+        {
+            var base64 = QRCodeBuilder.CreateMMS("1234567890", "Hello")
+                .RenderWith<PngRenderer>()
+                .ToBase64String();
+
+            base64.ShouldBe("iVBORw0KGgoAAAANSUhEUgAAAUoAAAFKAQAAAABTUiuoAAABdUlEQVR4nO2aSQ7DIAwAkfqAPilf50l9QCQK3qBtpPSGD8MhzTK5jIVtSEv7d9QCigEMYAADGMhhoNh49PNXsXtHP9NxgKYyIBftNdB2FqUOvfSnoHkM+DM703v9cgQbNLEBAyTOoMkNaA5dXwLNZsCTpte0sz+4ya+guwxEQ7LMxbveBXSTgTlKeUpDYjFdBmgWAx5EK2ytdl7afrsHmsuAtPgS2DEX9fBs/hJoKgM2+foaumrmtHSp0f3Mr6C7DUgHUiWS0efbPYs4aCYDkTm9xPkCwPasQPMYWLpETZVx+ClxoNsNSImzwibzbszAQdWfdQHofgM+A70D8bOLpAm63YD8RNKc4zq/gu40MMeyr6i9/8icoKkMeBhjs2PuVPmaDTSPgbkBrPMuvodZxQNNZSA+rnyNubkImtCA7n1IqpwrNdC0BmKTypdmoNkMeNL8+Fx5Kg+azEA0JL6kljpn/20DzWXgnwGKAQxgAAMYqNvLxhsMs1uBEKIWRQAAAABJRU5ErkJggg==");
+        }
+    }
+}
+#endif
\ No newline at end of file

From b4e935248ded887bffd281eb1a22a167a670713d Mon Sep 17 00:00:00 2001
From: Shane32 <shane@acdmail.com>
Date: Sat, 1 Jun 2024 01:55:23 -0400
Subject: [PATCH 2/2] Update ToArray

---
 QRCoder/Builders/Renderers/RendererExtensions.cs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/QRCoder/Builders/Renderers/RendererExtensions.cs b/QRCoder/Builders/Renderers/RendererExtensions.cs
index 3eb661ef..7d3bfe56 100644
--- a/QRCoder/Builders/Renderers/RendererExtensions.cs
+++ b/QRCoder/Builders/Renderers/RendererExtensions.cs
@@ -36,6 +36,14 @@ public static byte[] ToArray(this IStreamRenderer streamRenderer)
             {
                 return buffer.Array;
             }
+#else
+            try
+            {
+                var buffer = memoryStream.GetBuffer();
+                if (buffer.Length == memoryStream.Length)
+                return buffer;
+            }
+            catch { }
 #endif
             return memoryStream.ToArray();
         }