Skip to content

Commit e61e4bf

Browse files
authored
Merge branch 'master' into support_base64_linux
2 parents 70b2855 + c9c9f1b commit e61e4bf

File tree

5 files changed

+146
-53
lines changed

5 files changed

+146
-53
lines changed

QRCoder/PayloadGenerator.cs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,6 +1877,20 @@ public class CalendarEvent : Payload
18771877
private readonly string subject, description, location, start, end;
18781878
private readonly EventEncoding encoding;
18791879

1880+
/// <summary>
1881+
/// Generates a calender entry/event payload.
1882+
/// </summary>
1883+
/// <param name="subject">Subject/title of the calender event</param>
1884+
/// <param name="description">Description of the event</param>
1885+
/// <param name="location">Location (lat:long or address) of the event</param>
1886+
/// <param name="start">Start time (incl. UTC offset) of the event</param>
1887+
/// <param name="end">End time (incl. UTC offset) of the event</param>
1888+
/// <param name="allDayEvent">Is it a full day event?</param>
1889+
/// <param name="encoding">Type of encoding (universal or iCal)</param>
1890+
public CalendarEvent(string subject, string description, string location, DateTimeOffset start, DateTimeOffset end, bool allDayEvent, EventEncoding encoding = EventEncoding.Universal) : this(subject, description, location, start.UtcDateTime, end.UtcDateTime, allDayEvent, encoding)
1891+
{
1892+
}
1893+
18801894
/// <summary>
18811895
/// Generates a calender entry/event payload.
18821896
/// </summary>
@@ -1893,9 +1907,17 @@ public CalendarEvent(string subject, string description, string location, DateTi
18931907
this.description = description;
18941908
this.location = location;
18951909
this.encoding = encoding;
1896-
string dtFormat = allDayEvent ? "yyyyMMdd" : "yyyyMMddTHHmmss";
1897-
this.start = start.ToString(dtFormat);
1898-
this.end = end.ToString(dtFormat);
1910+
string dtFormatStart = "yyyyMMdd", dtFormatEnd = "yyyyMMdd";
1911+
if (!allDayEvent)
1912+
{
1913+
dtFormatStart = dtFormatEnd = "yyyyMMddTHHmmss";
1914+
if (start.Kind == DateTimeKind.Utc)
1915+
dtFormatStart = "yyyyMMddTHHmmssZ";
1916+
if (end.Kind == DateTimeKind.Utc)
1917+
dtFormatEnd = "yyyyMMddTHHmmssZ";
1918+
}
1919+
this.start = start.ToString(dtFormatStart);
1920+
this.end = end.ToString(dtFormatEnd);
18991921
}
19001922

19011923
public override string ToString()
@@ -2512,6 +2534,13 @@ public override string ToString()
25122534

25132535
public byte[] ToBytes()
25142536
{
2537+
//Setup byte encoder
2538+
//Encode return string as byte[] with correct CharacterSet
2539+
#if !NET35_OR_GREATER
2540+
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
2541+
#endif
2542+
var cp = this.characterSet.ToString().Replace("_", "-");
2543+
25152544
//Calculate the seperator
25162545
separator = DetermineSeparator();
25172546

@@ -2523,21 +2552,19 @@ public byte[] ToBytes()
25232552
$"{separator}BIC={mFields.BIC}" +
25242553
$"{separator}CorrespAcc={mFields.CorrespAcc}";
25252554

2555+
//Check length of mandatory field block (-8 => Removing service data block bytes from ret length)
2556+
int bytesMandatoryLen = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret)).Length - 8;
2557+
if (bytesMandatoryLen > 300)
2558+
throw new RussiaPaymentOrderException($"Data too long. Mandatory data must not exceed 300 bytes, but actually is {bytesMandatoryLen} bytes long. Remove additional data fields or shorten strings/values.");
2559+
2560+
25262561
//Add optional fields, if filled
25272562
var optionalFieldsList = GetOptionalFieldsAsList();
25282563
if (optionalFieldsList.Count > 0)
25292564
ret += $"|{string.Join("|", optionalFieldsList.ToArray())}";
25302565
ret += separator;
25312566

2532-
//Encode return string as byte[] with correct CharacterSet
2533-
#if !NET35_OR_GREATER
2534-
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
2535-
#endif
2536-
var cp = this.characterSet.ToString().Replace("_", "-");
2537-
byte[] bytesOut = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret));
2538-
if (bytesOut.Length > 300)
2539-
throw new RussiaPaymentOrderException($"Data too long. Payload must not exceed 300 bytes, but actually is {bytesOut.Length} bytes long. Remove additional data fields or shorten strings/values.");
2540-
return bytesOut;
2567+
return Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret));
25412568
}
25422569

25432570

QRCoder/SvgQRCode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex
126126
for (int xi = 0; xi < drawableModulesCount; xi += 1)
127127
{
128128
matrix[yi, xi] = 0;
129-
if (bitArray[xi+offset] && (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo((xi+offset)*pixelsPerModule, (yi+offset) * pixelsPerModule, logoAttr, pixelsPerModule)))
129+
if (bitArray[xi+offset] && (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(xi * pixelsPerModule, yi * pixelsPerModule, logoAttr, pixelsPerModule)))
130130
{
131131
if(x0 == -1)
132132
{

QRCoderTests/PayloadGeneratorTests.cs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ public void calendarevent_should_build_universal()
816816
var location = "Programmer's paradise, Beachtown, Paradise";
817817
var alldayEvent = false;
818818
var begin = new DateTime(2016, 01, 03, 12, 00, 00);
819-
var end = new DateTime(2016, 01, 03, 14, 30, 0);
819+
var end = new DateTime(2016, 01, 03, 14, 30, 00);
820820
var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal;
821821

822822
var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding);
@@ -843,6 +843,42 @@ public void calendarevent_should_build_ical()
843843
}
844844

845845

846+
[Fact]
847+
[Category("PayloadGenerator/CalendarEvent")]
848+
public void calendarevent_should_build_with_utc_datetime()
849+
{
850+
var subject = "Release party";
851+
var description = "A small party for the new QRCoder. Bring some beer!";
852+
var location = "Programmer's paradise, Beachtown, Paradise";
853+
var alldayEvent = false;
854+
var begin = new DateTime(2016, 01, 03, 12, 00, 00, DateTimeKind.Utc);
855+
var end = new DateTime(2016, 01, 03, 14, 30, 00, DateTimeKind.Utc);
856+
var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal;
857+
858+
var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding);
859+
860+
generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000Z{Environment.NewLine}DTEND:20160103T143000Z{Environment.NewLine}END:VEVENT");
861+
}
862+
863+
864+
[Fact]
865+
[Category("PayloadGenerator/CalendarEvent")]
866+
public void calendarevent_should_build_with_utc_offset()
867+
{
868+
var subject = "Release party";
869+
var description = "A small party for the new QRCoder. Bring some beer!";
870+
var location = "Programmer's paradise, Beachtown, Paradise";
871+
var alldayEvent = false;
872+
var begin = new DateTimeOffset(2016, 01, 03, 12, 00, 00, new TimeSpan(3, 0, 0));
873+
var end = new DateTimeOffset(2016, 01, 03, 14, 30, 00, new TimeSpan(3, 0, 0));
874+
var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal;
875+
876+
var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding);
877+
878+
generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T090000Z{Environment.NewLine}DTEND:20160103T113000Z{Environment.NewLine}END:VEVENT");
879+
}
880+
881+
846882
[Fact]
847883
[Category("PayloadGenerator/CalendarEvent")]
848884
public void calendarevent_should_build_allday()
@@ -3280,24 +3316,44 @@ public void russiapayment_generator_should_throw_no_separator_exception()
32803316
[Fact]
32813317
[Category("PayloadGenerator/RussiaPaymentOrder")]
32823318
public void russiapayment_generator_should_throw_data_too_long_exception()
3319+
{
3320+
var account = "40702810138250123017";
3321+
var bic = "044525225";
3322+
var bankName = "A very very very very very long bank name";
3323+
// We use € symbol for the test case, because it needs 2-bytes. Otherwise we couldn't generate more than 300 bytes
3324+
// of mandatory data to trigger the test case and stay at the same time within the 160 chars field validation limit
3325+
var name = "A very €€€€ €€€€ €€€€ €€€€ very very very very very very very very very very very very very very very very very very very very very very very very ver long name";
3326+
var correspAcc = "30101810400000000225";
3327+
var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc);
3328+
3329+
var exception = Record.Exception(() => generator.ToString());
3330+
Assert.NotNull(exception);
3331+
Assert.IsType<PayloadGenerator.RussiaPaymentOrder.RussiaPaymentOrderException>(exception);
3332+
exception.Message.ShouldStartWith("Data too long");
3333+
}
3334+
3335+
[Fact]
3336+
[Category("PayloadGenerator/RussiaPaymentOrder")]
3337+
public void russiapayment_generator_should_throw_no_data_too_long_exception()
32833338
{
32843339
var account = "40702810138250123017";
32853340
var bic = "044525225";
32863341
var bankName = "ОАО | \"БАНК\"";
3287-
var name = "A very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long name";
3342+
var name = "A name";
32883343
var correspAcc = "30101810400000000225";
32893344
var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields()
32903345
{
32913346
FirstName = "Another long long long long long long long long long long long long long long firstname",
32923347
LastName = "Another long long long long long long long long long long long long long long lastname",
3348+
Category = "A pretty long long long long long long long long long long long long long category",
32933349
Sum = "125000"
32943350
};
32953351
var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields);
32963352

3353+
// Should throw no exception as the 300 byte limit applies only to the mandatory fields
3354+
// See https://github.com/codebude/QRCoder/issues/392
32973355
var exception = Record.Exception(() => generator.ToString());
3298-
Assert.NotNull(exception);
3299-
Assert.IsType<PayloadGenerator.RussiaPaymentOrder.RussiaPaymentOrderException>(exception);
3300-
exception.Message.ShouldStartWith("Data too long");
3356+
Assert.Null(exception);
33013357
}
33023358

33033359
[Fact]

QRCoderTests/SvgQRCodeRendererTests.cs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,6 @@ namespace QRCoderTests
1515
public class SvgQRCodeRendererTests
1616
{
1717

18-
private string GetAssemblyPath()
19-
{
20-
return
21-
#if NET5_0
22-
AppDomain.CurrentDomain.BaseDirectory;
23-
#else
24-
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).Replace("file:\\", "");
25-
#endif
26-
}
27-
2818
[Fact]
2919
[Category("QRRenderer/SvgQRCode")]
3020
public void can_render_svg_qrcode_simple()
@@ -113,7 +103,7 @@ public void can_render_svg_qrcode_with_png_logo_bitmap()
113103
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
114104

115105
//Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346
116-
var logoBitmap = (Bitmap)Image.FromFile(GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png");
106+
var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png");
117107
var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15);
118108
logoObj.GetMediaType().ShouldBe<SvgQRCode.SvgLogo.MediaType>(SvgQRCode.SvgLogo.MediaType.PNG);
119109

@@ -122,6 +112,44 @@ public void can_render_svg_qrcode_with_png_logo_bitmap()
122112
var result = HelperFunctions.StringToHash(svg);
123113
result.ShouldBe("78e02e8ba415f15817d5ed88c4afca31");
124114
}
115+
116+
[Fact]
117+
[Category("QRRenderer/SvgQRCode")]
118+
public void can_render_svg_qrcode_with_png_logo_bitmap_without_background()
119+
{
120+
//Create QR code
121+
var gen = new QRCodeGenerator();
122+
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
123+
124+
//Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346
125+
var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png");
126+
var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15, false);
127+
logoObj.GetMediaType().ShouldBe<SvgQRCode.SvgLogo.MediaType>(SvgQRCode.SvgLogo.MediaType.PNG);
128+
129+
var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj);
130+
131+
var result = HelperFunctions.StringToHash(svg);
132+
result.ShouldBe("f221b2baecc2883f8e8ae54f12ba701b");
133+
}
134+
135+
[Fact]
136+
[Category("QRRenderer/SvgQRCode")]
137+
public void can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones()
138+
{
139+
//Create QR code
140+
var gen = new QRCodeGenerator();
141+
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
142+
143+
//Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346
144+
var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png");
145+
var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15);
146+
logoObj.GetMediaType().ShouldBe<SvgQRCode.SvgLogo.MediaType>(SvgQRCode.SvgLogo.MediaType.PNG);
147+
148+
var svg = new SvgQRCode(data).GetGraphic(10, Color.Black, Color.White, drawQuietZones: false, logo: logoObj);
149+
150+
var result = HelperFunctions.StringToHash(svg);
151+
result.ShouldBe("8b4d114136c7fd26e0b34e5a15daac3b");
152+
}
125153
#endif
126154

127155
[Fact]
@@ -133,7 +161,7 @@ public void can_render_svg_qrcode_with_png_logo_bytearray()
133161
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
134162

135163
//Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346
136-
var logoBitmap = System.IO.File.ReadAllBytes(GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png");
164+
var logoBitmap = System.IO.File.ReadAllBytes(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png");
137165
var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15);
138166
logoObj.GetMediaType().ShouldBe<SvgQRCode.SvgLogo.MediaType>(SvgQRCode.SvgLogo.MediaType.PNG);
139167

@@ -152,7 +180,7 @@ public void can_render_svg_qrcode_with_svg_logo_embedded()
152180
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
153181

154182
//Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361
155-
var logoSvg = File.ReadAllText(GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg");
183+
var logoSvg = File.ReadAllText(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg");
156184
var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20);
157185
logoObj.GetMediaType().ShouldBe<SvgQRCode.SvgLogo.MediaType>(SvgQRCode.SvgLogo.MediaType.SVG);
158186

@@ -171,7 +199,7 @@ public void can_render_svg_qrcode_with_svg_logo_image_tag()
171199
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
172200

173201
//Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361
174-
var logoSvg = File.ReadAllText(GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg");
202+
var logoSvg = File.ReadAllText(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg");
175203
var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20, iconEmbedded: false);
176204

177205
var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj);

readme.md

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ PM> Install-Package QRCoder
3535

3636
#### CI builds
3737
The NuGet feed contains only **major/stable** releases. If you want the latest functions and features, you can use the CI builds [via Github packages](https://github.com/codebude/qrcoder/packages).
38-
_(More information on how to use Github Packages in Nuget Package Manager can be [found here](http://webcache.googleusercontent.com/search?q=cache:i_gL6oIwpr8J:www.catiawidgets.net/2021/04/20/creating-github-packages-and-accessing-them-in-nuget/+&cd=1&hl=de&ct=clnk&gl=de).)_
38+
_(More information on how to use Github Packages in Nuget Package Manager can be [found here](https://samlearnsazure.blog/2021/08/08/consuming-a-nuget-package-from-github-packages/).)_
3939

4040

4141
## Usage
4242

43-
You only need four lines of code, to generate and view your first QR code.
43+
You only need a couple lines of code, to generate your first QR code.
4444

4545
```csharp
4646
QRCodeGenerator qrGenerator = new QRCodeGenerator();
@@ -49,29 +49,11 @@ QRCode qrCode = new PngByteQRCode(qrCodeData);
4949
byte[] qrCodeImage = qrCode.GetGraphic(20);
5050
```
5151

52-
### Optional parameters and overloads
53-
54-
The GetGraphics-method has some more overloads. The first two enable you to set the color of the QR code graphic. One uses Color-class-types, the other HTML hex color notation.
55-
56-
```csharp
57-
//Set color by using Color-class types
58-
Bitmap qrCodeImage = qrCode.GetGraphic(20, Color.DarkRed, Color.PaleGreen, true);
59-
60-
//Set color by using HTML hex color notation
61-
Bitmap qrCodeImage = qrCode.GetGraphic(20, "#000ff0", "#0ff000");
62-
```
63-
64-
The other overload enables you to render a logo/image in the center of the QR code.
65-
66-
```csharp
67-
Bitmap qrCodeImage = qrCode.GetGraphic(20, Color.Black, Color.White, (Bitmap)Bitmap.FromFile("C:\\myimage.png"));
68-
```
69-
7052
There are a plenty of other options. So feel free to read more on that in our wiki: [Wiki: How to use QRCoder](https://github.com/codebude/QRCoder/wiki/How-to-use-QRCoder)
7153

7254
### Special rendering types
7355

74-
Besides the normal QRCode class (which is shown in the example above) for creating QR codes in Bitmap format, there are some more QR code rendering classes, each for another special purpose.
56+
Besides the normal PngByteQRCode-class (which is shown in the example above) for creating QR codes in Bitmap format, there are some more QR code rendering classes, each for another special purpose.
7557

7658
* [QRCode](https://github.com/codebude/QRCoder/wiki/Advanced-usage---QR-Code-renderers#21-qrcode-renderer-in-detail)
7759
* [ArtQRCode](https://github.com/codebude/QRCoder/wiki/Advanced-usage---QR-Code-renderers#211-artqrcode-renderer-in-detail)

0 commit comments

Comments
 (0)