From 524525fb808d427a3880479b218474fb816ff4d6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Jan 2026 21:30:01 +0000 Subject: [PATCH 1/2] refactor: extract reusable ContactItemIcon component from Footer - Extract duplicated email/phone icon rendering into nested ContactItemIcon class - Replace magic numbers with named constants (IconSize, IconTextSize, etc.) - Separate canvas drawing logic into DrawIconWithBackground method - Make contact items data-driven for easier extensibility - Remove unused commented-out code --- Flixen.CurriculumVitae.Layouts/Footer.cs | 125 +++++++++++------------ 1 file changed, 57 insertions(+), 68 deletions(-) diff --git a/Flixen.CurriculumVitae.Layouts/Footer.cs b/Flixen.CurriculumVitae.Layouts/Footer.cs index 4e2b78c..197bec6 100644 --- a/Flixen.CurriculumVitae.Layouts/Footer.cs +++ b/Flixen.CurriculumVitae.Layouts/Footer.cs @@ -8,7 +8,10 @@ namespace Flixen.CurriculumVitae.Builder; public class Footer : IComponent { private readonly string _color; - private readonly int _iconSize = 30; + private const int IconSize = 30; + private const int IconTextSize = 15; + private const int ItemSpacing = 5; + private const int RowSpacing = 30; public Footer(string color) { @@ -17,85 +20,71 @@ public Footer(string color) public void Compose(IContainer container) { + var contactItems = new[] + { + (icon: "fa-envelope", text: "felix@flixen.se"), + (icon: "fa-phone", text: "+46737120411") + }; container.AlignCenter().Row(row => { - row.Spacing(30); - row.AutoItem().Column(col => + row.Spacing(RowSpacing); + foreach (var item in contactItems) { - col.Spacing(5); - col.Item() - .AlignCenter() - .Height(_iconSize) - .Width(_iconSize) - .Canvas((canvas, size) => - { - using var paint = new SKPaint - { - Color = SKColor.Parse(_color), - IsStroke = false, - StrokeWidth = 2, - IsAntialias = true - }; - canvas.DrawRoundRect(0, 0, size.Width, size.Height, 100, 100, paint); + row.AutoItem().Component(new ContactItemIcon(_color, item.icon, item.text)); + } + }); + } - using var textPaint = new SKPaint(); - textPaint.TextAlign = SKTextAlign.Center; - textPaint.FilterQuality = SKFilterQuality.High; - textPaint.TextSize = 15; - textPaint.Typeface = SKTypeface.FromFamilyName("Arial"); - canvas.DrawIconifiedText("{{fa-envelope color=000000}}", size.Width / 2, size.Height / 2 + textPaint.TextSize/3, - textPaint); - }); - col.Item() - .Text("felix@flixen.se") - .Bold() - .FontSize(15); - }); - row.AutoItem().Column(col => + private class ContactItemIcon : IComponent + { + private readonly string _backgroundColor; + private readonly string _iconName; + private readonly string _text; + + public ContactItemIcon(string backgroundColor, string iconName, string text) + { + _backgroundColor = backgroundColor; + _iconName = iconName; + _text = text; + } + + public void Compose(IContainer container) + { + container.Column(col => { - col.Spacing(5); + col.Spacing(ItemSpacing); col.Item() .AlignCenter() - .Height(_iconSize) - .Width(_iconSize) - .Canvas((canvas, size) => - { - using var paint = new SKPaint - { - Color = SKColor.Parse(_color), - IsStroke = false, - StrokeWidth = 2, - IsAntialias = true - }; - canvas.DrawRoundRect(0, 0, size.Width, size.Height, 100, 100, paint); - - using var textPaint = new SKPaint(); - textPaint.TextAlign = SKTextAlign.Center; - textPaint.FilterQuality = SKFilterQuality.High; - textPaint.TextSize = 15; - textPaint.Typeface = SKTypeface.FromFamilyName("Arial"); - canvas.DrawIconifiedText("{{fa-phone color=000000}}", size.Width / 2, size.Height / 2 + textPaint.TextSize/3, - textPaint); - }); - + .Height(IconSize) + .Width(IconSize) + .Canvas(DrawIconWithBackground); col.Item() - .Text("+46737120411") + .Text(_text) .Bold() - .FontSize(15);; + .FontSize(IconTextSize); }); - }); + } + private void DrawIconWithBackground(SKCanvas canvas, QuestPDF.Helpers.Size size) + { + using var backgroundPaint = new SKPaint + { + Color = SKColor.Parse(_backgroundColor), + IsStroke = false, + IsAntialias = true + }; + canvas.DrawRoundRect(0, 0, size.Width, size.Height, 100, 100, backgroundPaint); - // container.Table(table => - // { - // table.ColumnsDefinition(def => - // { - // def.RelativeColumn(); - // def.RelativeColumn(); - // }); - // - // table.Cell() - // }); + using var iconPaint = new SKPaint + { + TextAlign = SKTextAlign.Center, + FilterQuality = SKFilterQuality.High, + TextSize = IconTextSize, + Typeface = SKTypeface.FromFamilyName("Arial") + }; + var iconCode = "{{" + _iconName + " color=000000}}"; + canvas.DrawIconifiedText(iconCode, size.Width / 2, size.Height / 2 + iconPaint.TextSize / 3, iconPaint); + } } } \ No newline at end of file From 0542bb6d3be03cb7c679b5a8f2078a444078a583 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 08:19:26 +0000 Subject: [PATCH 2/2] fix: use inline lambda for Canvas delegate compatibility Convert method reference to inline lambda to ensure compatibility with QuestPDF's Canvas delegate signature. --- Flixen.CurriculumVitae.Layouts/Footer.cs | 42 +++++++++++------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Flixen.CurriculumVitae.Layouts/Footer.cs b/Flixen.CurriculumVitae.Layouts/Footer.cs index 197bec6..fc51ea8 100644 --- a/Flixen.CurriculumVitae.Layouts/Footer.cs +++ b/Flixen.CurriculumVitae.Layouts/Footer.cs @@ -58,33 +58,31 @@ public void Compose(IContainer container) .AlignCenter() .Height(IconSize) .Width(IconSize) - .Canvas(DrawIconWithBackground); + .Canvas((canvas, size) => + { + using var backgroundPaint = new SKPaint + { + Color = SKColor.Parse(_backgroundColor), + IsStroke = false, + IsAntialias = true + }; + canvas.DrawRoundRect(0, 0, size.Width, size.Height, 100, 100, backgroundPaint); + + using var iconPaint = new SKPaint + { + TextAlign = SKTextAlign.Center, + FilterQuality = SKFilterQuality.High, + TextSize = IconTextSize, + Typeface = SKTypeface.FromFamilyName("Arial") + }; + var iconCode = "{{" + _iconName + " color=000000}}"; + canvas.DrawIconifiedText(iconCode, size.Width / 2, size.Height / 2 + iconPaint.TextSize / 3, iconPaint); + }); col.Item() .Text(_text) .Bold() .FontSize(IconTextSize); }); } - - private void DrawIconWithBackground(SKCanvas canvas, QuestPDF.Helpers.Size size) - { - using var backgroundPaint = new SKPaint - { - Color = SKColor.Parse(_backgroundColor), - IsStroke = false, - IsAntialias = true - }; - canvas.DrawRoundRect(0, 0, size.Width, size.Height, 100, 100, backgroundPaint); - - using var iconPaint = new SKPaint - { - TextAlign = SKTextAlign.Center, - FilterQuality = SKFilterQuality.High, - TextSize = IconTextSize, - Typeface = SKTypeface.FromFamilyName("Arial") - }; - var iconCode = "{{" + _iconName + " color=000000}}"; - canvas.DrawIconifiedText(iconCode, size.Width / 2, size.Height / 2 + iconPaint.TextSize / 3, iconPaint); - } } } \ No newline at end of file