Skip to content

Commit

Permalink
Issue #93: Added badges in PNG format
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpalme committed Aug 24, 2017
1 parent 7137859 commit a417bce
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The screenshots show two snippets of the generated reports:
![Screenshot 2](http://danielpalme.github.io/ReportGenerator/resources/screenshot2.png)

### Badges
Badges in SVG format can be generated if `-reporttypes:Badges` is used:
Badges in SVG and PNG format can be generated if `-reporttypes:Badges` is used:

![Sample badge](http://danielpalme.github.io/ReportGenerator/resources/badge.svg)

Expand Down
4 changes: 4 additions & 0 deletions Readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ For further details take a look at LICENSE.txt.

CHANGELOG

2.5.11.0

* New: Issue #93: Added badges in PNG format

2.5.10.0

* New: Issue #94: Added filtering for files
Expand Down
60 changes: 56 additions & 4 deletions ReportGenerator.Reporting/BadgeReportBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using Palmmedia.ReportGenerator.Parser.Analysis;
Expand Down Expand Up @@ -126,6 +128,16 @@ @keyframes fadein {{
/// </summary>
private const string CoverageTooltip = @"<rect class=""{0}"" x=""90"" y=""0"" width=""65"" height=""20"" fill-opacity=""0""><title>{1}</title></rect>";

/// <summary>
/// The template for line coverage badge in PNG format.
/// </summary>
private const string LineCoveragePngTemplate = "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";

/// <summary>
/// The template for branch coverage badge in PNG format.
/// </summary>
private const string BranchCoveragePngTemplate = "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";

/// <summary>
/// Gets the report type.
/// </summary>
Expand Down Expand Up @@ -166,21 +178,29 @@ public void CreateSummaryReport(SummaryResult summaryResult)
{
File.WriteAllText(
Path.Combine(this.TargetDirectory, "badge_linecoverage.svg"),
this.CreateBadge(summaryResult, true, false));
this.CreateSvgBadge(summaryResult, true, false));

File.WriteAllBytes(
Path.Combine(this.TargetDirectory, "badge_linecoverage.png"),
this.CreatePngBadge(summaryResult, true));
}

if (summaryResult.BranchCoverageQuota.HasValue)
{
File.WriteAllText(
Path.Combine(this.TargetDirectory, "badge_branchcoverage.svg"),
this.CreateBadge(summaryResult, false, true));
this.CreateSvgBadge(summaryResult, false, true));

File.WriteAllBytes(
Path.Combine(this.TargetDirectory, "badge_branchcoverage.png"),
this.CreatePngBadge(summaryResult, false));
}

if (summaryResult.CoverageQuota.HasValue && summaryResult.BranchCoverageQuota.HasValue)
{
File.WriteAllText(
Path.Combine(this.TargetDirectory, "badge_combined.svg"),
this.CreateBadge(summaryResult, true, true));
this.CreateSvgBadge(summaryResult, true, true));
}
}

Expand All @@ -191,7 +211,7 @@ public void CreateSummaryReport(SummaryResult summaryResult)
/// <param name="includeLineCoverage">Indicates whether line coverage should be included.</param>
/// <param name="includeBranchCoverage">Indicates whether branch coverage should be included.</param>
/// <returns>The rendered SVG.</returns>
private string CreateBadge(SummaryResult summaryResult, bool includeLineCoverage, bool includeBranchCoverage)
private string CreateSvgBadge(SummaryResult summaryResult, bool includeLineCoverage, bool includeBranchCoverage)
{
string lineCoverageClass = includeLineCoverage && includeBranchCoverage ? "linecoverage" : string.Empty;
string branchCoverageClass = includeLineCoverage && includeBranchCoverage ? "branchcoverage" : string.Empty;
Expand All @@ -208,5 +228,37 @@ private string CreateBadge(SummaryResult summaryResult, bool includeLineCoverage
includeLineCoverage ? string.Format(CoverageTooltip, lineCoverageClass, ReportResources.Coverage) : string.Empty,
includeBranchCoverage ? string.Format(CoverageTooltip, branchCoverageClass, ReportResources.BranchCoverage) : string.Empty);
}

/// <summary>
/// Renderes the PNG.
/// </summary>
/// <param name="summaryResult">Indicates whether </param>
/// <param name="lineCoverage">Indicates whether line coverage or branch coverage should be displayed.</param>
/// <returns>The rendered PNG.</returns>
private byte[] CreatePngBadge(SummaryResult summaryResult, bool lineCoverage)
{
string template = lineCoverage ? LineCoveragePngTemplate : BranchCoveragePngTemplate;
string text = (lineCoverage ? summaryResult.CoverageQuota.Value : summaryResult.BranchCoverageQuota.Value).ToString(CultureInfo.InvariantCulture) + "%";

using (var ms = new MemoryStream(Convert.FromBase64String(template)))
using (Image image = Bitmap.FromStream(ms))
using (Graphics g = Graphics.FromImage(image))
using (Font font = new Font("Verdana", 8, FontStyle.Regular, GraphicsUnit.Point))
using (MemoryStream output = new MemoryStream())
{
Rectangle rect = new Rectangle(112, 0, 155, 20);

StringFormat stringFormat = new StringFormat()
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center
};

g.DrawString(text, font, Brushes.White, rect, stringFormat);

image.Save(output, ImageFormat.Png);
return output.ToArray();
}
}
}
}
4 changes: 2 additions & 2 deletions ReportGenerator.Reporting/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.10.0")]
[assembly: AssemblyFileVersion("2.5.10.0")]
[assembly: AssemblyVersion("2.5.11.0")]
[assembly: AssemblyFileVersion("2.5.11.0")]

[assembly: CLSCompliant(true)]
4 changes: 2 additions & 2 deletions ReportGenerator/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.10.0")]
[assembly: AssemblyFileVersion("2.5.10.0")]
[assembly: AssemblyVersion("2.5.11.0")]
[assembly: AssemblyFileVersion("2.5.11.0")]
[assembly: NeutralResourcesLanguageAttribute("en-US")]

[assembly: CLSCompliant(true)]
Expand Down
4 changes: 2 additions & 2 deletions ReportGeneratorTest/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("2.5.10.0")]
[assembly: AssemblyFileVersion("2.5.10.0")]
[assembly: AssemblyVersion("2.5.11.0")]
[assembly: AssemblyFileVersion("2.5.11.0")]
[assembly: NeutralResourcesLanguageAttribute("en-US")]

0 comments on commit a417bce

Please sign in to comment.