feature: pdf field locations cached

This commit is contained in:
Gary Sharp
2022-11-03 16:56:12 +11:00
parent e463a7361e
commit 96cccab958
9 changed files with 121 additions and 29 deletions
+38 -13
View File
@@ -8,6 +8,7 @@ using Spring.Expressions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
@@ -15,16 +16,24 @@ namespace Disco.Services.Expressions
{
public sealed class Expression : List<IExpressionPart>
{
public string Name { get; private set; }
public string Source { get; private set; }
public string Name { get; }
public string Source { get; }
public bool IsDynamic { get; private set; }
public int Ordinal { get; private set; }
public int Ordinal { get; }
private Expression(string Name, string Source, int Ordinal)
public bool IsRequired { get; }
public bool IsReadOnly { get; }
public RectangleF? Position { get; }
private Expression(string name, string source, int ordinal, bool isRequired, bool isReadOnly, RectangleF? position)
{
this.Name = Name;
this.Source = Source;
this.Ordinal = Ordinal;
Name = name;
Source = source;
Ordinal = ordinal;
IsRequired = isRequired;
IsReadOnly = isReadOnly;
Position = position;
}
public static void InitializeExpressions()
@@ -71,6 +80,19 @@ namespace Disco.Services.Expressions
public Tuple<string, bool, object> Evaluate(object ExpressionContext, IDictionary Variables)
{
if (Count == 0)
return new Tuple<string, bool, object>(string.Empty, false, null);
if (!IsDynamic)
{
if (Count != 1)
throw new InvalidOperationException("Non-dynamic expressions should only have one part");
if (this[0] is TextExpressionPart textPart)
return new Tuple<string, bool, object>(textPart.RawSource, false, null);
else
throw new InvalidOperationException("Non-dynamic expressions should have a single TextExpressionPart component");
}
var resultValue = new StringBuilder();
object resultObject = null;
bool resultError = false;
@@ -103,25 +125,28 @@ namespace Disco.Services.Expressions
}
public static Expression TokenizeSingleDynamic(string Name, string ExpressionSource, int Ordinal)
{
var e = new Expression(Name, ExpressionSource, Ordinal);
var e = new Expression(Name, ExpressionSource, Ordinal, isRequired: false, isReadOnly: false, position: null);
if (ExpressionSource != null && !string.IsNullOrWhiteSpace(ExpressionSource))
e.Add(new EvaluateExpressionPart(ExpressionSource));
e.IsDynamic = true;
return e;
}
public static Expression Tokenize(string Name, string ExpressionSource, int Ordinal)
public static Expression Tokenize(string Name, string ExpressionSource, int Ordinal, bool IsRequired, bool IsReadOnly)
=> Tokenize(Name, ExpressionSource, Ordinal, IsRequired, IsReadOnly, null);
public static Expression Tokenize(string name, string expressionSource, int ordinal, bool isRequired, bool isReadOnly, RectangleF? position)
{
var e = new Expression(Name, ExpressionSource, Ordinal);
if (!ExpressionSource.Contains("{") || !ExpressionSource.Contains("}"))
var e = new Expression(name, expressionSource, ordinal, isRequired, isReadOnly, position);
if (!expressionSource.Contains("{") || !expressionSource.Contains("}"))
{
e.Add(new TextExpressionPart(ExpressionSource));
e.Add(new TextExpressionPart(expressionSource));
}
else
{
var token = new StringBuilder();
bool tokenEval = false;
int tokenEvalDepth = 0;
foreach (char c in ExpressionSource)
foreach (char c in expressionSource)
{
switch (c)
{
@@ -22,6 +22,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
}
public abstract Stream GetImage(int Width, int Height);
public abstract Stream GetImage();
protected Stream RenderImage(Image SourceImage, int Width, int Height)
{
@@ -56,7 +57,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
}
else
{ // Lossy Format - JPG
byte quality = Math.Min((byte)100, Math.Max((byte)1, Quality));
var quality = Math.Min(100, Math.Max(1, (int)Quality));
SourceImage.SaveJpg(quality, imageStream);
}
imageStream.Position = 0;
@@ -20,5 +20,10 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{
return RenderImage(Image, Width, Height);
}
public override Stream GetImage()
{
return OutputImage(Image);
}
}
}
@@ -25,5 +25,14 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
return RenderImage(SourceImage, Width, Height);
}
}
public override Stream GetImage()
{
var stream = new MemoryStream();
using (var fileStream = File.OpenRead(AbsoluteFilePath))
fileStream.CopyTo(stream);
stream.Position = 0;
return stream;
}
}
}
@@ -29,15 +29,50 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
public override Stream GetImage(int Width, int Height)
{
List<Image> Images = new List<Image>();
return DoLayout(Width, Height);
}
public override Stream GetImage()
{
return DoLayout(width: null, height: null);
}
private Stream DoLayout(int? width, int? height)
{
List<Image> images = new List<Image>();
try
{
// Load Images
foreach (string imageFilePath in AbsoluteFilePaths)
Images.Add(Bitmap.FromFile(imageFilePath));
images.Add(Image.FromFile(imageFilePath));
if (!width.HasValue || !height.HasValue)
{
int maxWidth, maxHeight;
if (MontageHorizontalLayout)
{
maxWidth = images.Sum(i => i.Width);
maxHeight = images.Max(i => i.Height);
}else if (MontageVerticalLayout)
{
maxWidth = images.Max(i => i.Width);
maxHeight = images.Sum(i => i.Height);
}
else // table layout
{
var itemAverageSize = new SizeF(images.Average(i => (float)i.Size.Width), images.Average(i => (float)i.Size.Height));
var stageSize = new Size((int)(itemAverageSize.Width / 2f * (images.Count + 1)), (int)(itemAverageSize.Height / 2f * (images.Count + 1)));
var calculatedLayout = CalculateColumnCount(stageSize, itemAverageSize, images.Count);
maxWidth = (int)(calculatedLayout.Item1 * itemAverageSize.Width);
maxHeight = (int)(calculatedLayout.Item2 * itemAverageSize.Height);
}
width = width ?? maxWidth;
height = height ?? maxHeight;
}
// Build Montage
using (Bitmap montageImage = new Bitmap(Width, Height))
using (Bitmap montageImage = new Bitmap(width.Value, height.Value))
{
using (Graphics montageGraphics = Graphics.FromImage(montageImage))
{
@@ -55,14 +90,13 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
}
if (MontageHorizontalLayout)
DoHorizontalLayout(Images, montageGraphics);
DoHorizontalLayout(images, montageGraphics);
else
if (MontageVerticalLayout)
DoVirticalLayout(Images, montageGraphics);
else
DoTableLayout(Images, montageGraphics);
DoVirticalLayout(images, montageGraphics);
else
DoTableLayout(images, montageGraphics);
}
return OutputImage(montageImage);
}
}
@@ -70,8 +104,8 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
finally
{
// Dispose of any Images
if (Images != null)
foreach (Image i in Images)
if (images != null)
foreach (Image i in images)
i.Dispose();
}
}
@@ -175,5 +209,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
return new Tuple<int, int, double>(bestColumnCount, bestRowCount, bestItemRatio);
}
}
}
@@ -4,7 +4,7 @@ namespace Disco.Services.Expressions
{
private string _Source;
bool IExpressionPart.ErrorsAllowed
public bool ErrorsAllowed
{
get
{
@@ -15,7 +15,7 @@ namespace Disco.Services.Expressions
return;
}
}
string IExpressionPart.Source
public string Source
{
get
{
@@ -26,7 +26,7 @@ namespace Disco.Services.Expressions
return;
}
}
string IExpressionPart.RawSource
public string RawSource
{
get
{
@@ -37,7 +37,7 @@ namespace Disco.Services.Expressions
return;
}
}
bool IExpressionPart.IsDynamic
public bool IsDynamic
{
get
{