feature: document template QR Code image extensions

This commit is contained in:
Gary Sharp
2023-10-12 15:51:33 +11:00
parent 7353405b16
commit a3fb09440d
17 changed files with 299 additions and 65 deletions
@@ -9,7 +9,6 @@ using Disco.Services.Interop.ActiveDirectory;
using iTextSharp.text.pdf; using iTextSharp.text.pdf;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.Entity;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
+21 -5
View File
@@ -1,8 +1,8 @@
using Disco.BI.Extensions; using Disco.BI.Extensions;
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.Expressions;
using Disco.Models.Repository; using Disco.Models.Repository;
using Disco.Models.Services.Documents; using Disco.Models.Services.Documents;
using Disco.Models.Services.Expressions.Extensions;
using Disco.Services; using Disco.Services;
using Disco.Services.Documents; using Disco.Services.Documents;
using Disco.Services.Expressions; using Disco.Services.Expressions;
@@ -270,14 +270,15 @@ namespace Disco.BI.Interop.Pdf
// Encode to QRCode byte array // Encode to QRCode byte array
var pageUniqueIdWidth = (int)pdfFieldPosition.position.Width; var pageUniqueIdWidth = (int)pdfFieldPosition.position.Width;
var pageUniqueIdHeight = (int)pdfFieldPosition.position.Height; var pageUniqueIdHeight = (int)pdfFieldPosition.position.Height;
var pageUniqueIdEncoded = QRCodeBinaryEncoder.Encode(pageUniqueIdBytes, pageUniqueIdWidth, pageUniqueIdHeight); var pageUniqueIdEncoded = QRCodeBinaryEncoder.Encode(pageUniqueIdBytes, out var qrWidth, out var qrHeight);
// Encode byte array to Image // Encode byte array to Image
var pageUniqueIdImageData = CCITTG4Encoder.Compress(pageUniqueIdEncoded, pageUniqueIdWidth, pageUniqueIdHeight); var pageUniqueIdImageData = CCITTG4Encoder.Compress(pageUniqueIdEncoded, qrWidth, qrHeight);
var pageUniqueIdImage = iTextSharp.text.Image.GetInstance(pageUniqueIdWidth, pageUniqueIdHeight, false, 256, 1, pageUniqueIdImageData, null); var pageUniqueIdImage = iTextSharp.text.Image.GetInstance(qrWidth, qrHeight, false, 256, 1, pageUniqueIdImageData, null);
// Add to the pdf page // Add to the pdf page
pageUniqueIdImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom); pageUniqueIdImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
pageUniqueIdImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height);
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pageUniqueIdImage); pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pageUniqueIdImage);
} }
// Hide Fields // Hide Fields
@@ -315,7 +316,22 @@ namespace Disco.BI.Interop.Pdf
for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++) for (int pdfFieldOrdinal = 0; pdfFieldOrdinal < fields.Size; pdfFieldOrdinal++)
{ {
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal]; AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage((int)(pdfFieldPosition.position.Width * 1.6), (int)(pdfFieldPosition.position.Height * 1.6)));
iTextSharp.text.Image pdfImage;
var imageWidth = (int)(pdfFieldPosition.position.Width * 1.6);
var imageHeight = (int)(pdfFieldPosition.position.Height * 1.6);
if (imageResult.Format == ImageExpressionFormat.Jpeg || imageResult.Format == ImageExpressionFormat.Png)
{
pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage(imageWidth, imageHeight));
}
else if (imageResult.Format == ImageExpressionFormat.CcittG4)
{
var imageData = imageResult.GetImage(out imageWidth, out imageHeight);
pdfImage = iTextSharp.text.Image.GetInstance(imageWidth, imageHeight, false, 256, 1, imageData.GetBuffer(), null);
}
else
throw new NotSupportedException($"Unexpected image format {imageResult.Format}");
pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom); pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
pdfImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height); pdfImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height);
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage); pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage);
+7
View File
@@ -1,5 +1,7 @@
using iTextSharp.text; using iTextSharp.text;
using iTextSharp.text.pdf; using iTextSharp.text.pdf;
using iTextSharp.text.pdf.codec;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -7,6 +9,11 @@ namespace Disco.BI.Interop.Pdf
{ {
public static class Utilities public static class Utilities
{ {
public static Func<byte[], int, int, byte[]> GetCCITTG4EncoderCompressDelegate()
{
return CCITTG4Encoder.Compress;
}
public static Stream JoinPdfs(bool InsertBlankPages, List<Stream> Pdfs) public static Stream JoinPdfs(bool InsertBlankPages, List<Stream> Pdfs)
{ {
if (Pdfs.Count == 0) if (Pdfs.Count == 0)
+2 -1
View File
@@ -49,6 +49,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="BI\Config\OrganisationAddress.cs" /> <Compile Include="BI\Config\OrganisationAddress.cs" />
<Compile Include="Services\Expressions\Extensions\ImageExpressionFormat.cs" />
<Compile Include="ClientServices\EnrolmentInformation\BaseBoard.cs" /> <Compile Include="ClientServices\EnrolmentInformation\BaseBoard.cs" />
<Compile Include="ClientServices\EnrolmentInformation\Battery.cs" /> <Compile Include="ClientServices\EnrolmentInformation\Battery.cs" />
<Compile Include="ClientServices\EnrolmentInformation\Bios.cs" /> <Compile Include="ClientServices\EnrolmentInformation\Bios.cs" />
@@ -86,7 +87,7 @@
<Compile Include="Services\Authorization\IClaimNavigatorItem.cs" /> <Compile Include="Services\Authorization\IClaimNavigatorItem.cs" />
<Compile Include="Services\Authorization\IRoleToken.cs" /> <Compile Include="Services\Authorization\IRoleToken.cs" />
<Compile Include="Services\Documents\DocumentState.cs" /> <Compile Include="Services\Documents\DocumentState.cs" />
<Compile Include="BI\Expressions\IImageExpressionResult.cs" /> <Compile Include="Services\Expressions\Extensions\IImageExpressionResult.cs" />
<Compile Include="Services\Jobs\Statistics\DailyOpenedClosedItem.cs" /> <Compile Include="Services\Jobs\Statistics\DailyOpenedClosedItem.cs" />
<Compile Include="ClientServices\EnrolResponse.cs" /> <Compile Include="ClientServices\EnrolResponse.cs" />
<Compile Include="ClientServices\MacEnrol.cs" /> <Compile Include="ClientServices\MacEnrol.cs" />
@@ -1,13 +1,13 @@
using System.IO; using System.IO;
namespace Disco.Models.BI.Expressions namespace Disco.Models.Services.Expressions.Extensions
{ {
public interface IImageExpressionResult public interface IImageExpressionResult
{ {
Stream GetImage(int Width, int Height); MemoryStream GetImage(int width, int height);
Stream GetImage(); MemoryStream GetImage(out int width, out int height);
byte Quality { get; set; } byte Quality { get; set; }
bool LosslessFormat { get; set; } ImageExpressionFormat Format { get; set; }
bool ShowField { get; set; } bool ShowField { get; set; }
string BackgroundColour { get; set; } string BackgroundColour { get; set; }
bool BackgroundPreferTransparent { get; set; } bool BackgroundPreferTransparent { get; set; }
@@ -0,0 +1,9 @@
namespace Disco.Models.Services.Expressions.Extensions
{
public enum ImageExpressionFormat
{
Jpeg,
Png,
CcittG4,
}
}
+1
View File
@@ -317,6 +317,7 @@
<Compile Include="Expressions\Extensions\ImageResultImplementations\BitmapImageExpressionResult.cs" /> <Compile Include="Expressions\Extensions\ImageResultImplementations\BitmapImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\FileImageExpressionResult.cs" /> <Compile Include="Expressions\Extensions\ImageResultImplementations\FileImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\FileMontageImageExpressionResult.cs" /> <Compile Include="Expressions\Extensions\ImageResultImplementations\FileMontageImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\QrCodeImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\UserExt.cs" /> <Compile Include="Expressions\Extensions\UserExt.cs" />
<Compile Include="Expressions\IExpressionPart.cs" /> <Compile Include="Expressions\IExpressionPart.cs" />
<Compile Include="Expressions\LazyDictionary.cs" /> <Compile Include="Expressions\LazyDictionary.cs" />
@@ -113,18 +113,42 @@ namespace Disco.Services.Documents.AttachmentImport
{ {
var qrReader = new QRCodeMultiReader(); var qrReader = new QRCodeMultiReader();
var qrReaderHints = new Dictionary<DecodeHintType, object>() {
{ DecodeHintType.TRY_HARDER, true }
};
var qrImageSource = new BitmapLuminanceSource((Bitmap)Image); var qrImageSource = new BitmapLuminanceSource((Bitmap)Image);
var qrBinarizer = new HybridBinarizer(qrImageSource); var qrBinarizer = new HybridBinarizer(qrImageSource);
var qrBinaryBitmap = new BinaryBitmap(qrBinarizer); var qrBinaryBitmap = new BinaryBitmap(qrBinarizer);
Result DetectDocumentUniqueIdentifier(BinaryBitmap bitmap)
{
var qrReaderHints = new Dictionary<DecodeHintType, object>() {
{ DecodeHintType.TRY_HARDER, true }
};
var qrCodeResults = qrReader.decodeMultiple(bitmap, qrReaderHints);
if (qrCodeResults != null && qrCodeResults.Length > 0)
{
if (qrCodeResults.Length > 1)
{
// multiple qr codes on page, test for byte-mark
foreach (var qr in qrCodeResults)
{
if (qr.ResultMetadata.TryGetValue(ResultMetadataType.BYTE_SEGMENTS, out var byteSegments))
{
var qrBytes = ((List<byte[]>)byteSegments)[0];
if (DocumentUniqueIdentifier.IsDocumentUniqueIdentifier(qrBytes))
return qr;
}
}
}
return qrCodeResults[0];
}
return null;
}
try try
{ {
qrCodeResult = qrReader.decodeMultiple(qrBinaryBitmap, qrReaderHints)?.FirstOrDefault(); qrCodeResult = DetectDocumentUniqueIdentifier(qrBinaryBitmap);
qrCodeResultScale = 1F; qrCodeResultScale = 1F;
} }
catch (ReaderException) catch (ReaderException)
@@ -148,7 +172,7 @@ namespace Disco.Services.Documents.AttachmentImport
try try
{ {
qrCodeResult = qrReader.decodeMultiple(qrBinaryBitmap, qrReaderHints)?.FirstOrDefault(); qrCodeResult = DetectDocumentUniqueIdentifier(qrBinaryBitmap);
qrCodeResultScale = 1.75F; qrCodeResultScale = 1.75F;
} }
catch (ReaderException) catch (ReaderException)
@@ -171,7 +195,7 @@ namespace Disco.Services.Documents.AttachmentImport
try try
{ {
qrCodeResult = qrReader.decodeMultiple(qrBinaryBitmap, qrReaderHints)?.FirstOrDefault(); qrCodeResult = DetectDocumentUniqueIdentifier(qrBinaryBitmap);
qrCodeResultScale = 2F; qrCodeResultScale = 2F;
} }
catch (ReaderException) catch (ReaderException)
+33 -14
View File
@@ -8,8 +8,24 @@ namespace Disco.Services.Documents
{ {
public static class QRCodeBinaryEncoder public static class QRCodeBinaryEncoder
{ {
public static byte[] Encode(string content, ErrorCorrectionLevel ecLevel, out int width, out int height)
{
var code = Encoder.encode(content, ecLevel, null);
public static byte[] Encode(byte[] Content, int Width, int Height) var array = code.Matrix.Array;
width = code.Matrix.Width;
height = code.Matrix.Height;
return scaleMatrix(code.Matrix.Array, width, height);
}
public static byte[] Encode(string content, ErrorCorrectionLevel ecLevel, int width, int height)
{
var code = Encoder.encode(content, ecLevel, null);
return scaleMatrix(code.Matrix.Array, width, height);
}
public static byte[] Encode(byte[] content, out int width, out int height)
{ {
var ecLevel = ErrorCorrectionLevel.L; var ecLevel = ErrorCorrectionLevel.L;
var bits = new BitArray(); var bits = new BitArray();
@@ -17,7 +33,7 @@ namespace Disco.Services.Documents
var mode = Mode.BYTE; var mode = Mode.BYTE;
var bitsNeeded = 4 + var bitsNeeded = 4 +
mode.getCharacterCountBits(ZXing.QrCode.Internal.Version.getVersionForNumber(1)) + mode.getCharacterCountBits(ZXing.QrCode.Internal.Version.getVersionForNumber(1)) +
(Content.Length * 8); (content.Length * 8);
var version = ChooseVersion(bitsNeeded, out ecLevel); var version = ChooseVersion(bitsNeeded, out ecLevel);
var ecBlocks = version.getECBlocksForLevel(ecLevel); var ecBlocks = version.getECBlocksForLevel(ecLevel);
@@ -28,12 +44,12 @@ namespace Disco.Services.Documents
bits.appendBits(mode.Bits, 4); bits.appendBits(mode.Bits, 4);
// Write the number of bytes // Write the number of bytes
bits.appendBits(Content.Length, mode.getCharacterCountBits(version)); bits.appendBits(content.Length, mode.getCharacterCountBits(version));
// Write the bytes // Write the bytes
for (int i = 0; i < Content.Length; i++) for (int i = 0; i < content.Length; i++)
{ {
bits.appendBits(Content[i], 8); bits.appendBits(content[i], 8);
} }
// Terminate the bit stream // Terminate the bit stream
@@ -72,7 +88,10 @@ namespace Disco.Services.Documents
MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
// Render matrix to bytes // Render matrix to bytes
return scaleMatrix(matrix.Array, Width, Height); width = matrix.Width;
height = matrix.Height;
return scaleMatrix(matrix.Array, width, height);
//return FlattenMatrix(matrix.Array, width, height);
} }
private static ZXing.QrCode.Internal.Version ChooseVersion(int RequiredBits, out ErrorCorrectionLevel ECLevel) private static ZXing.QrCode.Internal.Version ChooseVersion(int RequiredBits, out ErrorCorrectionLevel ECLevel)
@@ -242,17 +261,17 @@ namespace Disco.Services.Documents
return bestMaskPattern; return bestMaskPattern;
} }
private static byte[] scaleMatrix(byte[][] matrix, int Width, int Height) private static byte[] scaleMatrix(byte[][] matrix, int width, int height)
{ {
var matrixWidth = matrix[0].Length; var matrixWidth = matrix[0].Length;
var matrixHeight = matrix.Length; var matrixHeight = matrix.Length;
Width = Math.Max(Width, matrixWidth); width = Math.Max(width, matrixWidth);
Height = Math.Max(Height, matrixHeight); height = Math.Max(height, matrixHeight);
var byteColumns = (Width + 7) / 8; var byteColumns = (width + 7) / 8;
var outputBytes = new byte[byteColumns * Height]; var outputBytes = new byte[byteColumns * height];
var scale = Math.Min(Width / (matrixWidth + 1), Height / (matrixHeight + 1)); var scale = Math.Max(1, Math.Min(width / (matrixWidth + 1), height / (matrixHeight + 1)));
var offsetX = (Width - (matrixWidth * scale)) / 2; var offsetX = (width - (matrixWidth * scale)) / 2;
var offsetY = (Height - (matrixHeight * scale)) / 2; var offsetY = (height - (matrixHeight * scale)) / 2;
// initialize output bytes // initialize output bytes
for (int i = 0; i < outputBytes.Length; i++) for (int i = 0; i < outputBytes.Length; i++)
{ {
+1 -1
View File
@@ -1,7 +1,7 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.Expressions;
using Disco.Models.Repository; using Disco.Models.Repository;
using Disco.Models.Services.Documents; using Disco.Models.Services.Documents;
using Disco.Models.Services.Expressions.Extensions;
using Disco.Services.Plugins.Features.DetailsProvider; using Disco.Services.Plugins.Features.DetailsProvider;
using Spring.Core.TypeResolution; using Spring.Core.TypeResolution;
using Spring.Expressions; using Spring.Expressions;
@@ -118,15 +118,28 @@ namespace Disco.Services.Expressions.Extensions
} }
public static BitmapImageExpressionResult OrganisationLogo() public static BitmapImageExpressionResult OrganisationLogo()
{ {
var configCache = new Disco.Data.Configuration.SystemConfiguration(null); var configCache = new Data.Configuration.SystemConfiguration(null);
BitmapImageExpressionResult result; BitmapImageExpressionResult result;
using (var orgLogo = configCache.OrganisationLogo) using (var orgLogo = configCache.OrganisationLogo)
{ {
result = ImageFromStream(orgLogo); result = ImageFromStream(orgLogo);
} }
result.LosslessFormat = true; result.Format = Models.Services.Expressions.Extensions.ImageExpressionFormat.Png;
return result; 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]);
}
} }
} }
@@ -1,4 +1,4 @@
using Disco.Models.BI.Expressions; using Disco.Models.Services.Expressions.Extensions;
using System; using System;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
@@ -8,33 +8,35 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
public abstract class BaseImageExpressionResult : IImageExpressionResult public abstract class BaseImageExpressionResult : IImageExpressionResult
{ {
public byte Quality { get; set; } public byte Quality { get; set; }
public bool LosslessFormat { get; set; } public ImageExpressionFormat Format { get; set; }
public bool ShowField { get; set; } public bool ShowField { get; set; }
public string BackgroundColour { get; set; } public string BackgroundColour { get; set; }
public bool BackgroundPreferTransparent { get; set; } public bool BackgroundPreferTransparent { get; set; }
public BaseImageExpressionResult() public BaseImageExpressionResult()
{ {
LosslessFormat = false; Format = ImageExpressionFormat.Jpeg;
Quality = 90; Quality = 90;
ShowField = false; ShowField = false;
BackgroundPreferTransparent = true; BackgroundPreferTransparent = true;
} }
public abstract Stream GetImage(int Width, int Height); public abstract MemoryStream GetImage(int width, int height);
public abstract Stream GetImage(); 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) if (SourceImage == null)
throw new ArgumentNullException("SourceImage"); throw new ArgumentNullException(nameof(SourceImage));
if (Width <= 0) if (Width <= 0)
throw new ArgumentOutOfRangeException("Width", "Width must be > 0"); throw new ArgumentOutOfRangeException(nameof(Width), "Width must be > 0");
if (Height <= 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; Brush backgroundBrush = null;
if (!LosslessFormat || !BackgroundPreferTransparent) if (Format == ImageExpressionFormat.Jpeg || !BackgroundPreferTransparent)
{ {
if (string.IsNullOrEmpty(BackgroundColour)) if (string.IsNullOrEmpty(BackgroundColour))
backgroundBrush = Brushes.White; backgroundBrush = Brushes.White;
@@ -44,22 +46,28 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
using (Image resizedImage = SourceImage.ResizeImage(Width, Height, backgroundBrush)) 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(); MemoryStream imageStream = new MemoryStream();
if (LosslessFormat) if (Format == ImageExpressionFormat.Png)
{ // Lossless Format - PNG { // Lossless Format - PNG
SourceImage.SavePng(imageStream); SourceImage.SavePng(imageStream);
} }
else else if (Format == ImageExpressionFormat.Jpeg)
{ // Lossy Format - JPG { // Lossy Format - JPG
var quality = Math.Min(100, Math.Max(1, (int)Quality)); var quality = Math.Min(100, Math.Max(1, (int)Quality));
SourceImage.SaveJpg(quality, imageStream); SourceImage.SaveJpg(quality, imageStream);
} }
else
throw new NotSupportedException($"Unexpected format {Format}");
imageStream.Position = 0; imageStream.Position = 0;
return imageStream; return imageStream;
} }
@@ -16,14 +16,19 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
this.Image = Image; 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);
} }
} }
} }
@@ -18,20 +18,28 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
this.AbsoluteFilePath = AbsoluteFilePath; 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(); var stream = new MemoryStream();
using (var fileStream = File.OpenRead(AbsoluteFilePath)) using (var fileStream = File.OpenRead(AbsoluteFilePath))
fileStream.CopyTo(stream); fileStream.CopyTo(stream);
stream.Position = 0; stream.Position = 0;
using (var sourceImage = Image.FromStream(stream))
{
width = sourceImage.Width;
height = sourceImage.Height;
}
stream.Position = 0;
return stream; return stream;
} }
} }
@@ -27,17 +27,17 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
Padding = 4; 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>(); List<Image> images = new List<Image>();
try try
@@ -81,7 +81,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
montageGraphics.SmoothingMode = SmoothingMode.HighQuality; montageGraphics.SmoothingMode = SmoothingMode.HighQuality;
// Draw Background // Draw Background
if (!LosslessFormat || !BackgroundPreferTransparent) if (Format == Models.Services.Expressions.Extensions.ImageExpressionFormat.Jpeg || !BackgroundPreferTransparent)
{ {
Brush backgroundBrush = Brushes.White; Brush backgroundBrush = Brushes.White;
if (!string.IsNullOrEmpty(BackgroundColour)) if (!string.IsNullOrEmpty(BackgroundColour))
@@ -96,7 +96,9 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
else else
DoTableLayout(images, montageGraphics); DoTableLayout(images, montageGraphics);
} }
return OutputImage(montageImage); resultWidth = width.Value;
resultHeight = height.Value;
return OutputBitmapImage(montageImage);
} }
} }
catch (Exception) { throw; } catch (Exception) { throw; }
@@ -111,7 +113,6 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
private void DoHorizontalLayout(List<Image> Images, Graphics MontageGraphics) private void DoHorizontalLayout(List<Image> Images, Graphics MontageGraphics)
{ {
float imageScale; float imageScale;
float imagePosition = 0; float imagePosition = 0;
int imagesWidthTotal = Images.Sum(i => i.Width); int imagesWidthTotal = Images.Sum(i => i.Width);
@@ -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;
}
}
}
+1
View File
@@ -53,6 +53,7 @@ namespace Disco.Web
InitalizeCoreEnvironment(Database); InitalizeCoreEnvironment(Database);
// Initialize Expressions // Initialize Expressions
Disco.Services.Expressions.Extensions.ImageResultImplementations.QrCodeImageExpressionResult.CCITTG4EncoderCompressDelegate = Disco.BI.Interop.Pdf.Utilities.GetCCITTG4EncoderCompressDelegate();
Disco.Services.Expressions.Expression.InitializeExpressions(); Disco.Services.Expressions.Expression.InitializeExpressions();
// Initialize Job Queues // Initialize Job Queues