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
@@ -10,6 +10,7 @@ using iTextSharp.text.pdf;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -31,8 +32,21 @@ namespace Disco.BI.Extensions
int pdfFieldOrdinal = 0; int pdfFieldOrdinal = 0;
foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys) foreach (string pdfFieldKey in pdfReader.AcroFields.Fields.Keys)
{ {
var pdfField = pdfReader.AcroFields.Fields[pdfFieldKey];
var pdfFieldPositions = pdfReader.AcroFields.GetFieldPositions(pdfFieldKey);
var pdfFieldFlags = pdfField.GetMerged(0).GetAsNumber(PdfName.FF)?.IntValue ?? 0;
var isRequired = (pdfFieldFlags & 2) == 2;
var isReadOnly = (pdfFieldFlags & 1) == 1;
var pdfFieldValue = pdfReader.AcroFields.GetField(pdfFieldKey); var pdfFieldValue = pdfReader.AcroFields.GetField(pdfFieldKey);
ExpressionCache.SetValue(cacheModuleKey, pdfFieldKey, Expression.Tokenize(pdfFieldKey, pdfFieldValue, pdfFieldOrdinal)); var pdfFieldPosition = default(RectangleF?);
if (pdfFieldPositions != null && pdfFieldPositions.Count > 0)
{
var position = pdfFieldPositions.First().position;
pdfFieldPosition = new RectangleF(position.Left, position.Top, position.Width, position.Height);
}
ExpressionCache.SetValue(cacheModuleKey, pdfFieldKey, Expression.Tokenize(pdfFieldKey, pdfFieldValue, pdfFieldOrdinal, isRequired, isReadOnly, pdfFieldPosition));
pdfFieldOrdinal++; pdfFieldOrdinal++;
} }
pdfReader.Close(); pdfReader.Close();
+1
View File
@@ -290,6 +290,7 @@ namespace Disco.BI.Interop.Pdf
AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal]; AcroFields.FieldPosition pdfFieldPosition = pdfFieldPositions[pdfFieldOrdinal];
iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage((int)pdfFieldPosition.position.Width, (int)pdfFieldPosition.position.Height)); iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(imageResult.GetImage((int)pdfFieldPosition.position.Width, (int)pdfFieldPosition.position.Height));
pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom); pdfImage.SetAbsolutePosition(pdfFieldPosition.position.Left, pdfFieldPosition.position.Bottom);
pdfImage.ScaleToFit(pdfFieldPosition.position.Width, pdfFieldPosition.position.Height);
pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage); pdfStamper.GetOverContent(pdfFieldPosition.page).AddImage(pdfImage);
} }
if (!fieldExpressionResult.Item2 && !imageResult.ShowField) if (!fieldExpressionResult.Item2 && !imageResult.ShowField)
@@ -5,6 +5,7 @@ namespace Disco.Models.BI.Expressions
public interface IImageExpressionResult public interface IImageExpressionResult
{ {
Stream GetImage(int Width, int Height); Stream GetImage(int Width, int Height);
Stream GetImage();
byte Quality { get; set; } byte Quality { get; set; }
bool LosslessFormat { get; set; } bool LosslessFormat { get; set; }
bool ShowField { get; set; } bool ShowField { get; set; }
+38 -13
View File
@@ -8,6 +8,7 @@ using Spring.Expressions;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -15,16 +16,24 @@ namespace Disco.Services.Expressions
{ {
public sealed class Expression : List<IExpressionPart> public sealed class Expression : List<IExpressionPart>
{ {
public string Name { get; private set; } public string Name { get; }
public string Source { get; private set; } public string Source { get; }
public bool IsDynamic { get; private set; } 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; Name = name;
this.Source = Source; Source = source;
this.Ordinal = Ordinal; Ordinal = ordinal;
IsRequired = isRequired;
IsReadOnly = isReadOnly;
Position = position;
} }
public static void InitializeExpressions() public static void InitializeExpressions()
@@ -71,6 +80,19 @@ namespace Disco.Services.Expressions
public Tuple<string, bool, object> Evaluate(object ExpressionContext, IDictionary Variables) 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(); var resultValue = new StringBuilder();
object resultObject = null; object resultObject = null;
bool resultError = false; bool resultError = false;
@@ -103,25 +125,28 @@ namespace Disco.Services.Expressions
} }
public static Expression TokenizeSingleDynamic(string Name, string ExpressionSource, int Ordinal) 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)) if (ExpressionSource != null && !string.IsNullOrWhiteSpace(ExpressionSource))
e.Add(new EvaluateExpressionPart(ExpressionSource)); e.Add(new EvaluateExpressionPart(ExpressionSource));
e.IsDynamic = true; e.IsDynamic = true;
return e; 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); var e = new Expression(name, expressionSource, ordinal, isRequired, isReadOnly, position);
if (!ExpressionSource.Contains("{") || !ExpressionSource.Contains("}")) if (!expressionSource.Contains("{") || !expressionSource.Contains("}"))
{ {
e.Add(new TextExpressionPart(ExpressionSource)); e.Add(new TextExpressionPart(expressionSource));
} }
else else
{ {
var token = new StringBuilder(); var token = new StringBuilder();
bool tokenEval = false; bool tokenEval = false;
int tokenEvalDepth = 0; int tokenEvalDepth = 0;
foreach (char c in ExpressionSource) foreach (char c in expressionSource)
{ {
switch (c) switch (c)
{ {
@@ -22,6 +22,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
} }
public abstract Stream GetImage(int Width, int Height); public abstract Stream GetImage(int Width, int Height);
public abstract Stream GetImage();
protected Stream RenderImage(Image SourceImage, int Width, int Height) protected Stream RenderImage(Image SourceImage, int Width, int Height)
{ {
@@ -56,7 +57,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
} }
else else
{ // Lossy Format - JPG { // 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); SourceImage.SaveJpg(quality, imageStream);
} }
imageStream.Position = 0; imageStream.Position = 0;
@@ -20,5 +20,10 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{ {
return RenderImage(Image, Width, Height); 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); 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) 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 try
{ {
// Load Images // Load Images
foreach (string imageFilePath in AbsoluteFilePaths) 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 // Build Montage
using (Bitmap montageImage = new Bitmap(Width, Height)) using (Bitmap montageImage = new Bitmap(width.Value, height.Value))
{ {
using (Graphics montageGraphics = Graphics.FromImage(montageImage)) using (Graphics montageGraphics = Graphics.FromImage(montageImage))
{ {
@@ -55,14 +90,13 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
} }
if (MontageHorizontalLayout) if (MontageHorizontalLayout)
DoHorizontalLayout(Images, montageGraphics); DoHorizontalLayout(images, montageGraphics);
else else
if (MontageVerticalLayout) if (MontageVerticalLayout)
DoVirticalLayout(Images, montageGraphics); DoVirticalLayout(images, montageGraphics);
else else
DoTableLayout(Images, montageGraphics); DoTableLayout(images, montageGraphics);
} }
return OutputImage(montageImage); return OutputImage(montageImage);
} }
} }
@@ -70,8 +104,8 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
finally finally
{ {
// Dispose of any Images // Dispose of any Images
if (Images != null) if (images != null)
foreach (Image i in Images) foreach (Image i in images)
i.Dispose(); i.Dispose();
} }
} }
@@ -175,5 +209,7 @@ namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
return new Tuple<int, int, double>(bestColumnCount, bestRowCount, bestItemRatio); return new Tuple<int, int, double>(bestColumnCount, bestRowCount, bestItemRatio);
} }
} }
} }
@@ -4,7 +4,7 @@ namespace Disco.Services.Expressions
{ {
private string _Source; private string _Source;
bool IExpressionPart.ErrorsAllowed public bool ErrorsAllowed
{ {
get get
{ {
@@ -15,7 +15,7 @@ namespace Disco.Services.Expressions
return; return;
} }
} }
string IExpressionPart.Source public string Source
{ {
get get
{ {
@@ -26,7 +26,7 @@ namespace Disco.Services.Expressions
return; return;
} }
} }
string IExpressionPart.RawSource public string RawSource
{ {
get get
{ {
@@ -37,7 +37,7 @@ namespace Disco.Services.Expressions
return; return;
} }
} }
bool IExpressionPart.IsDynamic public bool IsDynamic
{ {
get get
{ {