feature: document template QR Code image extensions
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.BI.Expressions;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Documents;
|
||||
using Disco.Models.Services.Expressions.Extensions;
|
||||
using Disco.Services.Plugins.Features.DetailsProvider;
|
||||
using Spring.Core.TypeResolution;
|
||||
using Spring.Expressions;
|
||||
|
||||
@@ -118,15 +118,28 @@ namespace Disco.Services.Expressions.Extensions
|
||||
}
|
||||
public static BitmapImageExpressionResult OrganisationLogo()
|
||||
{
|
||||
var configCache = new Disco.Data.Configuration.SystemConfiguration(null);
|
||||
var configCache = new Data.Configuration.SystemConfiguration(null);
|
||||
BitmapImageExpressionResult result;
|
||||
using (var orgLogo = configCache.OrganisationLogo)
|
||||
{
|
||||
result = ImageFromStream(orgLogo);
|
||||
}
|
||||
result.LosslessFormat = true;
|
||||
result.Format = Models.Services.Expressions.Extensions.ImageExpressionFormat.Png;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static QrCodeImageExpressionResult QrCode(string content)
|
||||
{
|
||||
return new QrCodeImageExpressionResult(content, 'M');
|
||||
}
|
||||
|
||||
public static QrCodeImageExpressionResult QrCode(string content, string errorCorrectionLevel)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(errorCorrectionLevel))
|
||||
errorCorrectionLevel = "M";
|
||||
|
||||
return new QrCodeImageExpressionResult(content, errorCorrectionLevel[0]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+22
-14
@@ -1,4 +1,4 @@
|
||||
using Disco.Models.BI.Expressions;
|
||||
using Disco.Models.Services.Expressions.Extensions;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
@@ -8,33 +8,35 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
public abstract class BaseImageExpressionResult : IImageExpressionResult
|
||||
{
|
||||
public byte Quality { get; set; }
|
||||
public bool LosslessFormat { get; set; }
|
||||
public ImageExpressionFormat Format { get; set; }
|
||||
public bool ShowField { get; set; }
|
||||
public string BackgroundColour { get; set; }
|
||||
public bool BackgroundPreferTransparent { get; set; }
|
||||
|
||||
public BaseImageExpressionResult()
|
||||
{
|
||||
LosslessFormat = false;
|
||||
Format = ImageExpressionFormat.Jpeg;
|
||||
Quality = 90;
|
||||
ShowField = false;
|
||||
BackgroundPreferTransparent = true;
|
||||
}
|
||||
|
||||
public abstract Stream GetImage(int Width, int Height);
|
||||
public abstract Stream GetImage();
|
||||
public abstract MemoryStream GetImage(int width, int height);
|
||||
public abstract MemoryStream GetImage(out int width, out int height);
|
||||
|
||||
protected Stream RenderImage(Image SourceImage, int Width, int Height)
|
||||
protected MemoryStream RenderBitmapImage(Image SourceImage, int Width, int Height)
|
||||
{
|
||||
if (SourceImage == null)
|
||||
throw new ArgumentNullException("SourceImage");
|
||||
throw new ArgumentNullException(nameof(SourceImage));
|
||||
if (Width <= 0)
|
||||
throw new ArgumentOutOfRangeException("Width", "Width must be > 0");
|
||||
throw new ArgumentOutOfRangeException(nameof(Width), "Width must be > 0");
|
||||
if (Height <= 0)
|
||||
throw new ArgumentOutOfRangeException("Height", "Height must be > 0");
|
||||
throw new ArgumentOutOfRangeException(nameof(Height), "Height must be > 0");
|
||||
if (Format != ImageExpressionFormat.Jpeg && Format != ImageExpressionFormat.Png)
|
||||
throw new NotSupportedException($"The format {Format} is not supported by this method");
|
||||
|
||||
Brush backgroundBrush = null;
|
||||
if (!LosslessFormat || !BackgroundPreferTransparent)
|
||||
if (Format == ImageExpressionFormat.Jpeg || !BackgroundPreferTransparent)
|
||||
{
|
||||
if (string.IsNullOrEmpty(BackgroundColour))
|
||||
backgroundBrush = Brushes.White;
|
||||
@@ -44,22 +46,28 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
|
||||
using (Image resizedImage = SourceImage.ResizeImage(Width, Height, backgroundBrush))
|
||||
{
|
||||
return OutputImage(resizedImage);
|
||||
return OutputBitmapImage(resizedImage);
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream OutputImage(Image SourceImage)
|
||||
protected MemoryStream OutputBitmapImage(Image SourceImage)
|
||||
{
|
||||
if (Format != ImageExpressionFormat.Jpeg && Format != ImageExpressionFormat.Png)
|
||||
throw new NotSupportedException($"The format {Format} is not supported by this method");
|
||||
|
||||
MemoryStream imageStream = new MemoryStream();
|
||||
if (LosslessFormat)
|
||||
if (Format == ImageExpressionFormat.Png)
|
||||
{ // Lossless Format - PNG
|
||||
SourceImage.SavePng(imageStream);
|
||||
}
|
||||
else
|
||||
else if (Format == ImageExpressionFormat.Jpeg)
|
||||
{ // Lossy Format - JPG
|
||||
var quality = Math.Min(100, Math.Max(1, (int)Quality));
|
||||
SourceImage.SaveJpg(quality, imageStream);
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException($"Unexpected format {Format}");
|
||||
|
||||
imageStream.Position = 0;
|
||||
return imageStream;
|
||||
}
|
||||
|
||||
+9
-4
@@ -16,14 +16,19 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
this.Image = Image;
|
||||
}
|
||||
|
||||
public override Stream GetImage(int Width, int Height)
|
||||
public override MemoryStream GetImage(int width, int height)
|
||||
{
|
||||
return RenderImage(Image, Width, Height);
|
||||
return RenderBitmapImage(Image, width, height);
|
||||
}
|
||||
|
||||
public override Stream GetImage()
|
||||
public override MemoryStream GetImage(out int width, out int height)
|
||||
{
|
||||
return OutputImage(Image);
|
||||
var image = Image;
|
||||
|
||||
width = image.Width;
|
||||
height = image.Height;
|
||||
|
||||
return OutputBitmapImage(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -18,20 +18,28 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
this.AbsoluteFilePath = AbsoluteFilePath;
|
||||
}
|
||||
|
||||
public override Stream GetImage(int Width, int Height)
|
||||
public override MemoryStream GetImage(int width, int height)
|
||||
{
|
||||
using (Image SourceImage = Bitmap.FromFile(AbsoluteFilePath))
|
||||
using (var sourceImage = Image.FromFile(AbsoluteFilePath))
|
||||
{
|
||||
return RenderImage(SourceImage, Width, Height);
|
||||
return RenderBitmapImage(sourceImage, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream GetImage()
|
||||
public override MemoryStream GetImage(out int width, out int height)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
using (var fileStream = File.OpenRead(AbsoluteFilePath))
|
||||
fileStream.CopyTo(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
using (var sourceImage = Image.FromStream(stream))
|
||||
{
|
||||
width = sourceImage.Width;
|
||||
height = sourceImage.Height;
|
||||
}
|
||||
stream.Position = 0;
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -27,17 +27,17 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
Padding = 4;
|
||||
}
|
||||
|
||||
public override Stream GetImage(int Width, int Height)
|
||||
public override MemoryStream GetImage(int width, int height)
|
||||
{
|
||||
return DoLayout(Width, Height);
|
||||
return DoLayout(width, height, out _, out _);
|
||||
}
|
||||
|
||||
public override Stream GetImage()
|
||||
public override MemoryStream GetImage(out int width, out int height)
|
||||
{
|
||||
return DoLayout(width: null, height: null);
|
||||
return DoLayout(width: null, height: null, out width, out height);
|
||||
}
|
||||
|
||||
private Stream DoLayout(int? width, int? height)
|
||||
private MemoryStream DoLayout(int? width, int? height, out int resultWidth, out int resultHeight)
|
||||
{
|
||||
List<Image> images = new List<Image>();
|
||||
try
|
||||
@@ -81,7 +81,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
montageGraphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||
|
||||
// Draw Background
|
||||
if (!LosslessFormat || !BackgroundPreferTransparent)
|
||||
if (Format == Models.Services.Expressions.Extensions.ImageExpressionFormat.Jpeg || !BackgroundPreferTransparent)
|
||||
{
|
||||
Brush backgroundBrush = Brushes.White;
|
||||
if (!string.IsNullOrEmpty(BackgroundColour))
|
||||
@@ -96,7 +96,9 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
else
|
||||
DoTableLayout(images, montageGraphics);
|
||||
}
|
||||
return OutputImage(montageImage);
|
||||
resultWidth = width.Value;
|
||||
resultHeight = height.Value;
|
||||
return OutputBitmapImage(montageImage);
|
||||
}
|
||||
}
|
||||
catch (Exception) { throw; }
|
||||
@@ -111,7 +113,6 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
|
||||
private void DoHorizontalLayout(List<Image> Images, Graphics MontageGraphics)
|
||||
{
|
||||
|
||||
float imageScale;
|
||||
float imagePosition = 0;
|
||||
int imagesWidthTotal = Images.Sum(i => i.Width);
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
using Disco.Models.Services.Expressions.Extensions;
|
||||
using Disco.Services.Documents;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using ZXing.QrCode.Internal;
|
||||
|
||||
namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
|
||||
{
|
||||
public class QrCodeImageExpressionResult : BaseImageExpressionResult
|
||||
{
|
||||
public static Func<byte[], int, int, byte[]> CCITTG4EncoderCompressDelegate;
|
||||
public string Data { get; }
|
||||
public char ErrorCorrectionLevel { get; }
|
||||
private readonly ErrorCorrectionLevel ecLevel;
|
||||
|
||||
public QrCodeImageExpressionResult(string data, char errorCorrectionLevel)
|
||||
{
|
||||
Data = data;
|
||||
ErrorCorrectionLevel = errorCorrectionLevel;
|
||||
|
||||
switch (errorCorrectionLevel)
|
||||
{
|
||||
case 'l':
|
||||
case 'L':
|
||||
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.L;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.M;
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.Q;
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
ecLevel = ZXing.QrCode.Internal.ErrorCorrectionLevel.H;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Error correction level must be L, M, Q or H");
|
||||
}
|
||||
|
||||
Format = ImageExpressionFormat.CcittG4;
|
||||
Quality = 90;
|
||||
ShowField = false;
|
||||
BackgroundPreferTransparent = true;
|
||||
}
|
||||
|
||||
public override MemoryStream GetImage(int width, int height)
|
||||
{
|
||||
var rawImage = QRCodeBinaryEncoder.Encode(Data, ecLevel, width, height);
|
||||
return RenderStream(rawImage, width, height);
|
||||
}
|
||||
|
||||
public override MemoryStream GetImage(out int width, out int height)
|
||||
{
|
||||
var rawImage = QRCodeBinaryEncoder.Encode(Data, ecLevel, out width, out height);
|
||||
return RenderStream(rawImage, width, height);
|
||||
}
|
||||
|
||||
private MemoryStream RenderStream(byte[] data, int width, int height)
|
||||
{
|
||||
if (Format == ImageExpressionFormat.CcittG4)
|
||||
{
|
||||
if (CCITTG4EncoderCompressDelegate == null)
|
||||
throw new InvalidOperationException($"The {CCITTG4EncoderCompressDelegate} delegate has not been initialized");
|
||||
|
||||
var result = CCITTG4EncoderCompressDelegate(data, width, height);
|
||||
return new MemoryStream(result, 0, result.Length, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var bitmap = RenderBitmap(data, width, height))
|
||||
{
|
||||
return OutputBitmapImage(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Image RenderBitmap(byte[] data, int width, int height)
|
||||
{
|
||||
var bitmap = new Bitmap(width, height, PixelFormat.Format1bppIndexed);
|
||||
bitmap.Palette.Entries[0] = Color.Black;
|
||||
bitmap.Palette.Entries[0] = Color.White;
|
||||
|
||||
var pixelData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
|
||||
try
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
var offset = y * width;
|
||||
var buffer = new byte[(width + 7) / 8];
|
||||
var bufferOffset = 0;
|
||||
var bitOffset = 0;
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
var pixel = data[offset + x];
|
||||
if (pixel != 0)
|
||||
{
|
||||
buffer[bufferOffset] = (byte)(buffer[bufferOffset] | (byte)((128) >> bitOffset));
|
||||
}
|
||||
if (bitOffset == 8)
|
||||
{
|
||||
bufferOffset++;
|
||||
bitOffset = 0;
|
||||
}
|
||||
}
|
||||
Marshal.Copy(buffer, 0, pixelData.Scan0, buffer.Length);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap.UnlockBits(pixelData);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user