Skip to content

Commit 19898ef

Browse files
authored
#91 imagetag (#206)
* appveyor. Fix zip name for signed artifacts * #91 How to insert Image? * Tests fix * Tests fix 2
1 parent 8a957ba commit 19898ef

34 files changed

+479
-111
lines changed

ClosedXML.Report/ClosedXML.Report.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;net40;net46</TargetFrameworks>
5-
<LangVersion>7.1</LangVersion>
5+
<LangVersion>Latest</LangVersion>
66
<AssemblyName>ClosedXML.Report</AssemblyName>
77
<PackageId>ClosedXML.Report</PackageId>
88
<Configurations>Debug;Release;Release.Signed</Configurations>

ClosedXML.Report/Excel/Subtotal.cs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class Subtotal : IDisposable
1111
{
1212
private readonly IXLRange _range;
1313
private readonly bool _summaryAbove;
14+
private readonly GroupTag[] _groupTags;
15+
private readonly FormulaEvaluator _evaluator;
1416
private bool _pageBreaks;
1517
private Func<string, string> _getGroupLabel;
1618
private IXLWorksheet Sheet => _range.Worksheet;
@@ -23,10 +25,12 @@ public Subtotal(IXLRange range) : this(range, false)
2325
{
2426
}
2527

26-
public Subtotal(IXLRange range, bool summaryAbove)
28+
public Subtotal(IXLRange range, bool summaryAbove, GroupTag[] groupTags = null, FormulaEvaluator evaluator = null)
2729
{
2830
_range = range;
2931
_summaryAbove = summaryAbove;
32+
_groupTags = groupTags;
33+
_evaluator = evaluator;
3034
Sheet.Outline.SummaryVLocation = _summaryAbove ? XLOutlineSummaryVLocation.Top : XLOutlineSummaryVLocation.Bottom;
3135
var workbook = Sheet.Workbook;
3236
const string tempsheet = "__tempsheet";
@@ -40,7 +44,7 @@ public Subtotal(IXLRange range, bool summaryAbove)
4044

4145
public SubtotalGroup[] Groups => _groups.ToArray();
4246

43-
public SubtotalGroup AddGrandTotal(GroupTag[] groups, SummaryFuncTag[] summaries)
47+
public SubtotalGroup AddGrandTotal(SummaryFuncTag[] summaries)
4448
{
4549
if (Sheet.Row(_range.Row(2).RowNumber()).OutlineLevel == 0)
4650
{
@@ -49,13 +53,13 @@ public SubtotalGroup AddGrandTotal(GroupTag[] groups, SummaryFuncTag[] summaries
4953
{
5054
var rangeAddress = _range.ShiftRows(1).RangeAddress;
5155
_range.InsertRowsAbove(1, true);
52-
gr = CreateGroup(Sheet.Range(rangeAddress), 1, 1, GrandLabel, groups, summaries, false,true);
56+
gr = CreateGroup(Sheet.Range(rangeAddress), 1, 1, GrandLabel, summaries, false,true);
5357
}
5458
else
5559
{
5660
var rangeAddress = _range.RangeAddress;
5761
_range.InsertRowsBelow(1, true);
58-
gr = CreateGroup(Sheet.Range(rangeAddress), 1, 1, GrandLabel, groups, summaries, false,true);
62+
gr = CreateGroup(Sheet.Range(rangeAddress), 1, 1, GrandLabel, summaries, false,true);
5963
}
6064
gr.Column = 0;
6165
_groups.Add(gr);
@@ -64,7 +68,7 @@ public SubtotalGroup AddGrandTotal(GroupTag[] groups, SummaryFuncTag[] summaries
6468
else return null;
6569
}
6670

67-
public void GroupBy(int groupBy, GroupTag[] groups, SummaryFuncTag[] summaries, bool pageBreaks = false, Func<string, string> getGroupLabel = null)
71+
public void GroupBy(int groupBy, SummaryFuncTag[] summaries, bool pageBreaks = false, Func<string, string> getGroupLabel = null)
6872
{
6973
_pageBreaks = pageBreaks;
7074
_getGroupLabel = getGroupLabel;
@@ -87,7 +91,7 @@ public void GroupBy(int groupBy, GroupTag[] groups, SummaryFuncTag[] summaries,
8791
foreach (var moveData in grRanges)
8892
{
8993
if (moveData.Type == RangeType.DataRange)
90-
_groups.Add(CreateGroup(Sheet.Range(moveData.TargetAddress), groupBy, level, moveData.GroupTitle, groups, summaries, _pageBreaks));
94+
_groups.Add(CreateGroup(Sheet.Range(moveData.TargetAddress), groupBy, level, moveData.GroupTitle, summaries, _pageBreaks));
9195
}
9296
ArrangePageBreaks(Groups, grRanges);
9397
}
@@ -252,7 +256,7 @@ private void MoveRange(MoveData moveData)
252256
Sheet.Cell(moveData.TargetAddress.FirstAddress).Value = _tempSheet.Range(1, 1, srcRng.RowCount(), srcRng.ColumnCount());
253257
}
254258

255-
private SubtotalGroup CreateGroup(IXLRange groupRng, int groupClmn, int level, string title, GroupTag[] groups, SummaryFuncTag[] summaries, bool pageBreaks,bool isGrandTotal=false)
259+
private SubtotalGroup CreateGroup(IXLRange groupRng, int groupClmn, int level, string title, SummaryFuncTag[] summaries, bool pageBreaks,bool isGrandTotal=false)
256260
{
257261
var firstRow = groupRng.RangeAddress.FirstAddress.RowNumber;
258262
var lastRow = groupRng.RangeAddress.LastAddress.RowNumber;
@@ -274,24 +278,12 @@ private SubtotalGroup CreateGroup(IXLRange groupRng, int groupClmn, int level, s
274278
summRow.Clear(XLClearOptions.Contents | XLClearOptions.DataType); //TODO Check if the issue persists (ClosedXML issue 844)
275279
//TODO: Remove the extra space if we can change the existing gauge files
276280
//summRow.Cell(groupClmn).Value = _getGroupLabel != null ? _getGroupLabel(title) : title + (isGrandTotal?null: (string.IsNullOrWhiteSpace(groups?.Where(x => x.Column == groupClmn).Select(x => x.TotalLabel).FirstOrDefault())?null:" ") + (groups?.Where(x => x.Column == groupClmn).Select(x=>x.TotalLabel).FirstOrDefault()??TotalLabel));
277-
summRow.Cell(groupClmn).Value = _getGroupLabel != null ? _getGroupLabel(title) : title + (isGrandTotal ? null : " " + (groups?.Where(x => x.Column == groupClmn).Select(x => x.TotalLabel).FirstOrDefault() ?? TotalLabel));
281+
summRow.Cell(groupClmn).Value = _getGroupLabel != null ? _getGroupLabel(title) : title + (isGrandTotal ? null : " " + (_groupTags?.Where(x => x.Column == groupClmn).Select(x => x.TotalLabel).FirstOrDefault() ?? TotalLabel));
278282
Sheet.Row(summRow.RowNumber()).OutlineLevel = level - 1;
279-
foreach (var item in summaries)
283+
foreach (var summ in summaries)
280284
{
281-
var summ = item.GetFunc();
282-
/*if (summ.FuncNum == 0)
283-
{
284-
summRow.Cell(summ.Column).Value = summ.Calculate(groupRng);
285-
}
286-
else */if (summ.FuncNum > 0)
287-
{
288-
var funcRngAddr = groupRng.Column(summ.Column).RangeAddress;
289-
summRow.Cell(summ.Column).FormulaA1 =(string.IsNullOrWhiteSpace(item.Cell.Formula) ?string.Empty: $"{item.Cell.Formula} & ")+$"Subtotal({summ.FuncNum},{funcRngAddr.ToStringRelative()})";
290-
}
291-
else
292-
{
293-
throw new NotSupportedException($"Aggregate function {summ.FuncName} not supported.");
294-
}
285+
var rngDataSource = summ.DataSource; // TODO Get data source from mapping row-item
286+
summ.Execute(new SummaryProcessingContext(groupRng, summRow, rngDataSource, _evaluator));
295287
}
296288

297289
var rows = Sheet.Rows(firstRow, lastRow);

ClosedXML.Report/Excel/SubtotalSummaryFunc.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ public virtual int FuncNum
5252
{
5353
if (_func == null)
5454
_func = GetFunc(FuncName);
55-
return GetExpression != null ? 0 : _func.FuncNum;
55+
return GetCalculateDelegate != null ? 0 : _func.FuncNum;
5656
}
5757
}
5858

59-
public Func<Type, LambdaExpression> GetExpression;
59+
public Func<Type, Delegate> GetCalculateDelegate;
6060

6161
internal object Calculate(IDataSource dataSource)
6262
{
@@ -69,8 +69,8 @@ internal object Calculate(IDataSource dataSource)
6969

7070
var agg = _func.CreateAggregator();
7171

72-
var lambda = GetExpression(items[0].GetType());
73-
var dlg = lambda.Compile();
72+
var dlg = GetCalculateDelegate(items[0].GetType());
73+
//var dlg = lambda.Compile();
7474
foreach (var item in items)
7575
{
7676
try

ClosedXML.Report/Excel/TempSheetBuffer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,15 @@ public IXLRange CopyTo(IXLRange range)
141141

142142
var tgtSheet = range.Worksheet;
143143
var tgtStartRow = range.RangeAddress.FirstAddress.RowNumber;
144+
145+
foreach (var picture in _sheet.Pictures)
146+
{
147+
var tgtPic = picture.CopyTo(tgtSheet);
148+
//var relAddress = picture.TopLeftCell.Relative(range.RangeAddress.FirstAddress);
149+
var tgtCell = range.RangeAddress.FirstAddress.Offset(picture.TopLeftCell.Address);
150+
tgtPic.MoveTo(tgtCell);
151+
}
152+
144153
var srcRows = _sheet.Rows(tempRng.RangeAddress.FirstAddress.RowNumber, tempRng.RangeAddress.LastAddress.RowNumber);
145154
foreach (var row in srcRows)
146155
{

ClosedXML.Report/Excel/XlExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ public static void Subtotal(this IXLRange range, int groupBy, string function, i
159159
if (replace)
160160
subtotal.Unsubtotal();
161161
var summaries = totalList.Select(x => new SummaryFuncTag {Name=function.ToLower(), Cell = new TemplateCell { Column = x } }).ToArray();
162-
subtotal.AddGrandTotal(null, summaries);
163-
subtotal.GroupBy(groupBy, null, summaries, pageBreaks);
162+
subtotal.AddGrandTotal(summaries);
163+
subtotal.GroupBy(groupBy, summaries, pageBreaks);
164164
}
165165
}
166166

ClosedXML.Report/FormulaEvaluator.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
namespace ClosedXML.Report
1111
{
12-
internal class FormulaEvaluator
12+
public class FormulaEvaluator
1313
{
1414
private static readonly Regex ExprMatch = new Regex(@"\{\{.+?\}\}");
15-
// private readonly Interpreter _interpreter; !!! переделать на DynamicLinq
15+
1616
private readonly Dictionary<string, Delegate> _lambdaCache = new Dictionary<string, Delegate>();
1717
private readonly Dictionary<string, object> _variables = new Dictionary<string, object>();
1818

@@ -21,7 +21,7 @@ public object Evaluate(string formula, params Parameter[] pars)
2121
var expressions = GetExpressions(formula);
2222
foreach (var expr in expressions)
2323
{
24-
var val = Eval(expr.Substring(2, expr.Length - 4), pars);
24+
var val = Eval(Trim(expr), pars);
2525
if (expr == formula)
2626
return val;
2727

@@ -46,7 +46,7 @@ public bool TryEvaluate(string formula, out object result, params Parameter[] pa
4646

4747
public void AddVariable(string name, object value)
4848
{
49-
_variables[name]=value;
49+
_variables[name] = value;
5050
}
5151

5252
private string ObjToString(object val)
@@ -63,31 +63,46 @@ private string ObjToString(object val)
6363
private IEnumerable<string> GetExpressions(string cellValue)
6464
{
6565
var matches = ExprMatch.Matches(cellValue);
66+
if (matches.Count == 0)
67+
return new[] { cellValue };
6668
return from Match match in matches select match.Value;
6769
}
6870

69-
private object Eval(string expression, Parameter[] pars)
71+
private string Trim(string formula)
72+
{
73+
if (formula.StartsWith("{{"))
74+
return formula.Substring(2, formula.Length - 4);
75+
else
76+
return formula;
77+
}
78+
79+
internal Delegate ParseExpression(string formula, ParameterExpression[] parameters)
7080
{
71-
if (!_lambdaCache.TryGetValue(expression, out var lambda))
81+
if (!_lambdaCache.TryGetValue(formula, out var lambda))
7282
{
73-
var parameters = pars.Select(p=>p.ParameterExpression).ToArray();
7483
try
7584
{
76-
lambda = XLDynamicExpressionParser.ParseLambda(parameters, typeof(object), expression, _variables).Compile();
85+
lambda = XLDynamicExpressionParser.ParseLambda(parameters, typeof(object), formula, _variables).Compile();
7786
}
7887
catch (ArgumentException)
7988
{
8089
return null;
8190
}
8291

83-
_lambdaCache.Add(expression, lambda);
92+
_lambdaCache.Add(formula, lambda);
8493
}
94+
return lambda;
95+
}
8596

86-
return lambda.DynamicInvoke(pars.Select(p => p.Value).ToArray());
97+
private object Eval(string expression, Parameter[] pars)
98+
{
99+
var parameters = pars.Select(p => p.ParameterExpression).ToArray();
100+
var lambda = ParseExpression(expression, parameters);
101+
return lambda?.DynamicInvoke(pars.Select(p => p.Value).ToArray());
87102
}
88103
}
89104

90-
internal class Parameter
105+
public class Parameter
91106
{
92107
public Parameter(string name, object value)
93108
{

ClosedXML.Report/Options/GroupTag.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public override void Execute(ProcessingContext context)
9191
// sort range
9292
base.Execute(context);
9393

94-
Process(context.Range, fields, summaryAbove, funcs, DisableGrandTotal);
94+
Process(context, fields, summaryAbove, funcs, DisableGrandTotal);
9595

9696
foreach (var tag in fields.Cast<OptionTag>().Union(funcs))
9797
{
@@ -101,9 +101,9 @@ public override void Execute(ProcessingContext context)
101101

102102
private bool DisableGrandTotal => List.HasTag("disablegrandtotal") || !List.GetAll<SummaryFuncTag>().Any();
103103

104-
private void Process(IXLRange root, GroupTag[] groups, bool summaryAbove,SummaryFuncTag[] summaries , bool disableGrandTotal)
104+
private void Process(ProcessingContext context, GroupTag[] groups, bool summaryAbove, SummaryFuncTag[] summaries , bool disableGrandTotal)
105105
{
106-
SubtotalSummaryFunc[] funcs= summaries.Select(x => x.GetFunc()).ToArray();
106+
var root = context.Range;
107107
var groupRow = root.LastRow();
108108
// DoGroups
109109

@@ -115,13 +115,13 @@ private void Process(IXLRange root, GroupTag[] groups, bool summaryAbove,Summary
115115

116116
var r = root.Offset(0, 0, rows, columns);
117117

118-
using (var subtotal = new Subtotal(r, summaryAbove))
118+
using (var subtotal = new Subtotal(r, summaryAbove, groups))
119119
{
120120
if (TotalLabel != null) subtotal.TotalLabel = TotalLabel;
121121
if (GrandLabel != null) subtotal.GrandLabel = GrandLabel;
122122
if (!disableGrandTotal)
123123
{
124-
var total = subtotal.AddGrandTotal(groups, summaries);
124+
var total = subtotal.AddGrandTotal(summaries);
125125
total.SummaryRow.Cell(2).Value = total.SummaryRow.Cell(1).Value;
126126
total.SummaryRow.Cell(1).Value = null;
127127
level++;
@@ -133,10 +133,10 @@ private void Process(IXLRange root, GroupTag[] groups, bool summaryAbove,Summary
133133
if (!string.IsNullOrEmpty(g.LabelFormat))
134134
labFormat = title => string.Format(LabelFormat, title);
135135

136-
if (g.MergeLabels == MergeMode.Merge2 && funcs.Length == 0)
136+
if (g.MergeLabels == MergeMode.Merge2 && summaries.Length == 0)
137137
subtotal.ScanForGroups(g.Column);
138138
else
139-
subtotal.GroupBy(g.Column, groups, g.DisableSubtotals ? new SummaryFuncTag[0] : summaries, g.PageBreaks, labFormat);
139+
subtotal.GroupBy(g.Column, g.DisableSubtotals ? new SummaryFuncTag[0] : summaries, g.PageBreaks, labFormat);
140140

141141
g.Level = ++level;
142142
}

ClosedXML.Report/Options/ImageTag.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Drawing;
2+
using System.IO;
3+
using ClosedXML.Excel.Drawings;
4+
using ClosedXML.Report.Utils;
5+
6+
namespace ClosedXML.Report.Options
7+
{
8+
public class ImageTag: OptionTag
9+
{
10+
public string Value => Parameters.ContainsKey("value") ? Parameters["value"] : null;
11+
public string ImageName => Parameters.ContainsKey("imagename") ? Parameters["imagename"] : null;
12+
public double Scale => Parameters.ContainsKey("scale") ? Parameters["scale"].AsDouble() : 0;
13+
public int Width => Parameters.ContainsKey("width") ? Parameters["width"].AsInt() : 0;
14+
public int Height => Parameters.ContainsKey("height") ? Parameters["height"].AsInt() : 0;
15+
16+
/*
17+
IXLPicture AddPicture(Stream stream, XLPictureFormat format);
18+
IXLPicture AddPicture(Stream stream, XLPictureFormat format, string name);
19+
*/
20+
public override void Execute(ProcessingContext context)
21+
{
22+
var xlCell = Cell.GetXlCell(context.Range);
23+
if (!string.IsNullOrEmpty(Value))
24+
{
25+
IXLPicture picture;
26+
var imgValue = context.Evaluator.Evaluate(Value, new Parameter("item", context.Value));
27+
28+
switch (imgValue)
29+
{
30+
case Stream stream: picture = xlCell.Worksheet.AddPicture(stream); break;
31+
case string path: picture = xlCell.Worksheet.AddPicture(path); break;
32+
case Bitmap image: picture = xlCell.Worksheet.AddPicture(image); break;
33+
default: throw new TemplateParseException("Unsupported image type.", xlCell.AsRange());
34+
};
35+
picture.MoveTo(xlCell);
36+
if (!string.IsNullOrEmpty(ImageName)) picture.Name = ImageName;
37+
if (Scale > 0) picture.Scale(Scale);
38+
if (Width > 0) picture.Width = Width;
39+
if (Height > 0) picture.Height = Height;
40+
}
41+
}
42+
}
43+
}

ClosedXML.Report/Options/PivotTag.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ private IXLPivotTable CreatePivot(PivotTag pivot, ProcessingContext context, XLP
191191
{
192192
var rowOffset = context.Range.RangeAddress.FirstAddress.RowNumber > 1 ? -1 : 0;
193193
IXLRange srcRange = context.Range.Offset(rowOffset, 1, context.Range.RowCount(), context.Range.ColumnCount() - 1);
194-
var pt = destination.TargetWorksheet.PivotTables.AddNew(destination.TableName, destination.TargetCell, srcRange);
194+
var pt = destination.TargetWorksheet.PivotTables.Add(destination.TableName, destination.TargetCell, srcRange);
195195
pt.MergeAndCenterWithLabels = pivot.HasParameter("MergeLabels");
196196
pt.ShowExpandCollapseButtons = pivot.HasParameter("ShowButtons");
197197
pt.ClassicPivotTableLayout = !pivot.HasParameter("TreeLayout");

0 commit comments

Comments
 (0)