Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions QRCoder.Core/Renderers/AsciiQRCode.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using static QRCoder.Core.Generators.QRCodeGenerator;

Expand Down Expand Up @@ -99,6 +100,7 @@ public static class AsciiQRCodeHelper
/// <param name="endOfLine">The end of line.</param>
/// <param name="drawQuietZones">The draw quiet zones.</param>
/// <returns>The string result.</returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Convenience helper with many optional parameters")]
public static string GetQRCode(string plainText, int pixelsPerModule, string darkSKColorString, string whiteSpaceString, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
Expand Down
46 changes: 25 additions & 21 deletions QRCoder.Core/Renderers/Base64QRCode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using SkiaSharp;

using System.IO;
Expand Down Expand Up @@ -63,7 +64,7 @@ public sealed class Base64QRCodeGraphicOptions
/// </summary>
public class Base64QRCode : AbstractQRCode
{
private QRCode qr;
private readonly QRCode qr;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Making qr readonly cements a pre-existing inconsistency in SetQRCodeData

The Base64QRCode.SetQRCodeData override at QRCoder.Core/Renderers/Base64QRCode.cs:90-93 delegates to this.qr.SetQRCodeData(data) but never calls base.SetQRCodeData(data). This means the parent class's QrCodeData property is never updated when using this method. This is a pre-existing issue (not introduced by this PR), but adding readonly to the qr field makes it slightly harder to refactor in the future — e.g., you can't reassign qr to a new QRCode(data) instance. In practice this doesn't matter because Base64QRCode only uses qr (not QrCodeData directly) for rendering, but it's worth being aware of the asymmetry if the class is ever extended.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


/// <summary>
/// Constructor without params to be used in COM Objects connections
Expand Down Expand Up @@ -130,25 +131,6 @@ public string GetGraphic(Base64QRCodeGraphicOptions options)
: RenderGraphic(options, options.ImageType);
}

private string RenderGraphic(Base64QRCodeGraphicOptions options, ImageType imgType)
{
var base64 = string.Empty;
using (SKBitmap bmp = qr.GetGraphic(new QRCodeGraphicOptions
{
PixelsPerModule = options.PixelsPerModule,
DarkSKColor = options.DarkSKColor,
LightSKColor = options.LightSKColor,
DrawQuietZones = options.DrawQuietZones,
Icon = options.Icon,
IconSizePercent = options.IconSizePercent,
IconBorderWidth = options.IconBorderWidth
}))
{
base64 = SKBitmapToBase64(bmp, imgType);
}
return base64;
}

/// <summary>
/// Returns the graphic representation of the QR code.
/// </summary>
Expand Down Expand Up @@ -181,6 +163,8 @@ public string GetGraphic(int pixelsPerModule, SKColor darkSKColor, SKColor light
/// <param name="imgType">The img type.</param>
/// <returns>The string result.</returns>
[Obsolete("Use GetGraphic(Base64QRCodeGraphicOptions) instead.")]
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Legacy public API overload")]
[SuppressMessage("SonarAnalyzer.CSharp", "S1133", Justification = "Public API; retained for backward compatibility")]
public string GetGraphic(int pixelsPerModule, SKColor darkSKColor, SKColor lightSKColor, SKBitmap icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
var base64 = string.Empty;
Expand All @@ -200,7 +184,26 @@ public string GetGraphic(int pixelsPerModule, SKColor darkSKColor, SKColor light
return base64;
}

private string SKBitmapToBase64(SKBitmap bmp, ImageType imgType)
private string RenderGraphic(Base64QRCodeGraphicOptions options, ImageType imgType)
{
var base64 = string.Empty;
using (SKBitmap bmp = qr.GetGraphic(new QRCodeGraphicOptions
{
PixelsPerModule = options.PixelsPerModule,
DarkSKColor = options.DarkSKColor,
LightSKColor = options.LightSKColor,
DrawQuietZones = options.DrawQuietZones,
Icon = options.Icon,
IconSizePercent = options.IconSizePercent,
IconBorderWidth = options.IconBorderWidth
}))
{
base64 = SKBitmapToBase64(bmp, imgType);
}
return base64;
}
Comment on lines +187 to +204

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: RenderGraphic method was only relocated, not modified

The RenderGraphic method was moved from before GetGraphic(int, SKColor, SKColor, ...) to after it. The code is identical (only whitespace/indentation of the object initializer changed). The diff makes it look like a deletion and re-addition, but no logic was changed.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


private static string SKBitmapToBase64(SKBitmap bmp, ImageType imgType)
{
var base64 = string.Empty;
SKEncodedImageFormat encodedFormat = imgType switch
Expand Down Expand Up @@ -258,6 +261,7 @@ public static class Base64QRCodeHelper
/// <param name="drawQuietZones">The draw quiet zones.</param>
/// <param name="imgType">The img type.</param>
/// <returns>The string result.</returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Convenience helper with many optional parameters")]
public static string GetQRCode(string plainText, int pixelsPerModule, string darkSKColorHtmlHex, string lightSKColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
using (var qrGenerator = new QRCodeGenerator())
Expand Down
7 changes: 5 additions & 2 deletions QRCoder.Core/Renderers/PdfByteQRCode.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

using System.Globalization;
using System.IO;
Expand Down Expand Up @@ -58,6 +59,7 @@
/// <param name="dpi"></param>
/// <param name="jpgQuality"></param>
/// <returns></returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S1192", Justification = "PDF literal fragments reused in PDF generation")]
public byte[] GetGraphic(int pixelsPerModule, string darkSKColorHtmlHex, string lightSKColorHtmlHex, int dpi = 150, long jpgQuality = 85)
{
byte[] jpgArray = null, pngArray = null;
Expand Down Expand Up @@ -211,11 +213,11 @@
/// </summary>
/// <param name="colorString">SKColor in HEX format like #ffffff</param>
/// <returns></returns>
private byte[] HexSKColorToByteArray(string colorString)
private static byte[] HexSKColorToByteArray(string colorString)
{
if (colorString.StartsWith("#"))
if (!string.IsNullOrEmpty(colorString) && colorString[0] == '#')
colorString = colorString.Substring(1);
Comment on lines +218 to 219

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: HexSKColorToByteArray condition refactor is behaviorally equivalent

The condition was changed from colorString.StartsWith("#") to !string.IsNullOrEmpty(colorString) && colorString[0] == '#'. These are equivalent for all practical inputs: for null, both paths crash (old crashes at StartsWith, new crashes at colorString.Length on the next line); for empty string, both skip the if and return an empty byte array; for #-prefixed strings, both strip the #. The new form avoids a string allocation from StartsWith and adds a null guard that doesn't fully protect (since the next line still dereferences colorString). If full null safety is desired, an early ArgumentNullException would be clearer.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

byte[] byteSKColor = new byte[colorString.Length / 2];

Check warning on line 220 in QRCoder.Core/Renderers/PdfByteQRCode.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'colorString' is null on at least one execution path.

See more on https://sonarcloud.io/project/issues?id=afonsoft_QRCoder.Core&issues=AZ9XcidRC_eeOFJVfxtd&open=AZ9XcidRC_eeOFJVfxtd&pullRequest=85
for (int i = 0; i < byteSKColor.Length; i++)
byteSKColor[i] = byte.Parse(colorString.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
return byteSKColor;
Expand All @@ -231,6 +233,7 @@
/// <summary>
/// Generates a QR code from the given data and returns the rendered output.
/// </summary>
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Convenience helper with many optional parameters")]
public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkSKColorHtmlHex,
string lightSKColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false,
EciMode eciMode = EciMode.Default, int requestedVersion = -1)
Expand Down
2 changes: 2 additions & 0 deletions QRCoder.Core/Renderers/PngByteQRCode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using static QRCoder.Core.Generators.QRCodeGenerator;
Expand Down Expand Up @@ -344,6 +345,7 @@ public static class PngByteQRCodeHelper
/// <param name="requestedVersion">The requested version.</param>
/// <param name="drawQuietZones">The draw quiet zones.</param>
/// <returns>The byte[] result.</returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Convenience helper with many optional parameters")]
public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkSKColorRgba, byte[] lightSKColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
Expand Down
4 changes: 3 additions & 1 deletion QRCoder.Core/Renderers/PostscriptQRCode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using QRCoder.Core.Extensions;
Expand Down Expand Up @@ -153,7 +154,7 @@ public string GetGraphic(Size viewBox, SKColor darkSKColor, SKColor lightSKColor
return sb.ToString();
}

private string CleanSvgVal(double input)
private static string CleanSvgVal(double input)
{
return input.ToString("G7", CultureInfo.InvariantCulture);
}
Expand Down Expand Up @@ -295,6 +296,7 @@ public static class PostscriptQRCodeHelper
/// <param name="drawQuietZones">Draw quiet zones.</param>
/// <param name="epsFormat">Return EPS format instead of plain PostScript.</param>
/// <returns>PostScript or EPS string.</returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Convenience helper with many optional parameters")]
public static string GetQRCode(string plainText, int pointsPerModule, string darkSKColorHex, string lightSKColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, bool epsFormat = false)
{
using (var qrGenerator = new QRCodeGenerator())
Expand Down
23 changes: 13 additions & 10 deletions QRCoder.Core/Renderers/SvgQRCode.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using QRCoder.Core.Extensions;
using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using SkiaSharp;
using System.Text;
using static QRCoder.Core.Generators.QRCodeGenerator;
Expand Down Expand Up @@ -116,6 +117,7 @@ public string GetGraphic(Size viewBox, SKColor darkSKColor, SKColor lightSKColor
/// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
/// <param name="logo">A (optional) logo to be rendered on the code (either SKBitmap or SVG)</param>
/// <returns>SVG as string</returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S3776", Justification = "SVG rendering is inherently sequential")]
public string GetGraphic(Size viewBox, string darkSKColorHex, string lightSKColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
int offset = drawQuietZones ? 0 : 4;
Expand Down Expand Up @@ -222,12 +224,12 @@ public string GetGraphic(Size viewBox, string darkSKColorHex, string lightSKColo
return svgFile.ToString();
}

private bool IsBlockedByLogo(double x, double y, ImageAttributes? attr, double pixelPerModule)
private static bool IsBlockedByLogo(double x, double y, ImageAttributes? attr, double pixelPerModule)
{
return x + pixelPerModule >= attr.Value.X && x <= attr.Value.X + attr.Value.Width && y + pixelPerModule >= attr.Value.Y && y <= attr.Value.Y + attr.Value.Height;
}

private ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox)
private static ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox)
{
var imgWidth = logo.GetIconSizePercent() / 100d * viewBox.Width;
var imgHeight = logo.GetIconSizePercent() / 100d * viewBox.Height;
Expand All @@ -250,7 +252,7 @@ private struct ImageAttributes
public double Y;
}

private string CleanSvgVal(double input)
private static string CleanSvgVal(double input)
{
//Clean double values for international use/formats
//We use explicitly "G15" to avoid differences between .NET full and Core platforms
Expand Down Expand Up @@ -278,12 +280,12 @@ public enum SizingMode
/// </summary>
public class SvgLogo
{
private string _logoData;
private MediaType _mediaType;
private int _iconSizePercent;
private bool _fillLogoBackground;
private object _logoRaw;
private bool _isEmbedded;
private readonly string _logoData;
private readonly MediaType _mediaType;
private readonly int _iconSizePercent;
private readonly bool _fillLogoBackground;
private readonly object _logoRaw;
private readonly bool _isEmbedded;

/// <summary>
/// Create a logo object to be used in SvgQRCode renderer
Expand Down Expand Up @@ -383,7 +385,7 @@ public bool FillLogoBackground()
/// <summary>
/// Media types for SvgLogos
/// </summary>
public enum MediaType : int
public enum MediaType
{
/// <summary>
/// png.
Expand Down Expand Up @@ -421,6 +423,7 @@ public static class SvgQRCodeHelper
/// <param name="sizingMode">The sizing mode.</param>
/// <param name="logo">The logo.</param>
/// <returns>The string result.</returns>
[SuppressMessage("SonarAnalyzer.CSharp", "S107", Justification = "Convenience helper with many optional parameters")]
public static string GetQRCode(string plainText, int pixelsPerModule, string darkSKColorHex, string lightSKColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
using (var qrGenerator = new QRCodeGenerator())
Expand Down
Loading