Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add table HeaderRowCount attribute #26

Open
wants to merge 22 commits into
base: vNext
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
233edb1
add table HeaderRowCount attribute
chen1tian Jul 16, 2020
6ea34d8
发布包到本地nuget
chen1tian Jul 17, 2020
a8b6601
增加组合图表的测试代码
chen1tian Oct 30, 2020
cd07a05
优化MoveSecondChartSeries后修改旧series的代码
chen1tian Oct 30, 2020
691d2f2
增加打包脚本
chen1tian Oct 30, 2020
c993302
修正第二坐标轴为折线图时,不展示线条的问题
chen1tian Nov 17, 2020
f5b3ede
修复当次级坐标轴为折线图时,线条不显示的问题
chen1tian Nov 17, 2020
f219e4c
remove build script for local nuget server
chen1tian Aug 16, 2021
220bf32
Merge branch 'EricWhiteDev:vNext' into vNext
chen1tian Aug 16, 2021
1594363
修复次轴图表展示不争取的问题。
chen1tian Apr 19, 2023
efa7bfb
更新版本
chen1tian Apr 19, 2023
3216baa
nuget包版本增加
chen1tian Apr 19, 2023
6c4d029
Merge branch 'vNext' of https://github.com/chen1tian/Open-Xml-PowerTo…
chen1tian Apr 19, 2023
31b7524
如果没有找到次轴序列则抛出异常
chen1tian Apr 19, 2023
5b4cf01
增加图片替换功能
chen1tian Apr 24, 2023
9bbb759
重构图片更新器,增加byte[]参数的输入
chen1tian Apr 24, 2023
a7822a4
增加图片替换
chen1tian Apr 24, 2023
1dfc59f
修正替换图片时候,将其他图片全部替换的问题
chen1tian Apr 25, 2023
e7926ce
图片替换后,去掉包围图片的自定义控件
chen1tian May 17, 2023
7fab0b1
修改图片时候,更新宽度和高度
chen1tian May 17, 2023
30b04c6
图片替换时,可以设置宽高
chen1tian May 18, 2023
c42056c
更新版本号
chen1tian May 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[*.cs]

# SA1626: Single-line comments should not use documentation style slashes
dotnet_diagnostic.SA1626.severity = suggestion
246 changes: 3 additions & 243 deletions OpenXmlPowerTools.sln

Large diffs are not rendered by default.

61 changes: 56 additions & 5 deletions OpenXmlPowerTools/ChartUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public class ChartData
public string[] CategoryNames;

public double[][] Values;

/// <summary>
/// 组合图表中,次轴图表的序列名称映射
/// 映射规则为:chatData中的序列名对应模板中的序列名。
/// </summary>
public Dictionary<string, string> SecondChartSeriesNames { get; set; } = new Dictionary<string, string>();
}

public class ChartUpdater
Expand Down Expand Up @@ -94,7 +100,14 @@ public static void UpdateChart(ChartPart chartPart, ChartData chartData)
throw new ArgumentException("Invalid chart data");
}

UpdateEmbeddedWorkbook(chartPart, chartData);

UpdateSeries(chartPart, chartData);
//如果设置了次轴,更新次轴
if (chartData.SecondChartSeriesNames.Count > 0)
{
UpdateSeries(chartPart, chartData, true);
}
}

private static Dictionary<int, string> FormatCodes = new Dictionary<int, string>()
Expand Down Expand Up @@ -129,13 +142,44 @@ public static void UpdateChart(ChartPart chartPart, ChartData chartData)
{ 49, "@" },
};

private static void UpdateSeries(ChartPart chartPart, ChartData chartData)
/// <summary>
///
/// </summary>
/// <param name="chartPart"></param>
/// <param name="chartData"></param>
/// <param name="setSecond">设置次级坐标,默认为false</param>
/// <exception cref="OpenXmlPowerToolsException"></exception>
private static void UpdateSeries(ChartPart chartPart, ChartData chartData, bool setSecond = false)
{
UpdateEmbeddedWorkbook(chartPart, chartData);

// update series
XDocument cpXDoc = chartPart.GetXDocument();
XElement root = cpXDoc.Root;
var firstSeries = root.Descendants(C.ser).FirstOrDefault();

// 序列名
var seriesNames = chartData.SeriesNames.Except(chartData.SecondChartSeriesNames.Keys);
if (setSecond)
{
seriesNames = chartData.SecondChartSeriesNames.Keys;
}

var series = root.Descendants(C.ser).ToList();
// 如果不处理次轴
if (!setSecond)
{
series = series.Where(x => x.Descendants(C.tx).SelectMany(tx => tx.Descendants(C.v)).Any(v => !chartData.SecondChartSeriesNames.Values.Contains(v.Value))).ToList();

if (series == null || series.Count == 0)
throw new OpenXmlPowerToolsException("未找到图表主序列");
}
else // 处理次轴
{
series = series.Where(x => x.Descendants(C.tx).SelectMany(tx => tx.Descendants(C.v)).Any(v => chartData.SecondChartSeriesNames.Values.Contains(v.Value))).ToList();

if (series == null || series.Count == 0)
throw new OpenXmlPowerToolsException("未找到次轴序列,请检查次轴映射字典");
}

var firstSeries = series.FirstOrDefault();
var numRef = firstSeries.Elements(C.val).Elements(C.numRef).FirstOrDefault();
string sheetName = null;
var f = (string)firstSeries.Descendants(C.f).FirstOrDefault();
Expand All @@ -151,6 +195,9 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData)
{
XElement cat = null;

if (!seriesNames.Contains(sn))
return null;

var oldCat = firstSeries.Elements(C.cat).FirstOrDefault();
if (oldCat == null)
throw new OpenXmlPowerToolsException("Invalid chart markup");
Expand Down Expand Up @@ -379,6 +426,10 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData)
newSer = (XElement)UpdateAccentTransform(newSer, accentNumber);
return newSer;
});

// 删除newSetOfSeries中null的项目
newSetOfSeries = newSetOfSeries.Where(x => x != null).ToList();

firstSeries.ReplaceWith(newSetOfSeries);
chartPart.PutXDocument();
}
Expand Down Expand Up @@ -427,7 +478,7 @@ private static void UpdateEmbeddedWorkbook(ChartPart chartPart, ChartData chartD
var firstRow = new XElement(S.row,
new XAttribute("r", "1"),
new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)),
new [] { new XElement(S.c,
new[] { new XElement(S.c,
new XAttribute("r", "A1"),
new XAttribute("t", "str"),
new XElement(S.v,
Expand Down
49 changes: 31 additions & 18 deletions OpenXmlPowerTools/DocumentAssembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ private static string ValidatePerSchema(XElement element)
<xs:element name='Table'>
<xs:complexType>
<xs:attribute name='Select' type='xs:string' use='required' />
<xs:attribute name='HeaderRowCount' type='xs:int' use='optional' />
</xs:complexType>
</xs:element>
</xs:schema>",
Expand Down Expand Up @@ -575,6 +576,8 @@ private class PA
public static XName Match = "Match";
public static XName NotMatch = "NotMatch";
public static XName Depth = "Depth";

public static XName HeaderRowCount = "HeaderRowCount";
}

private class PASchemaSet
Expand All @@ -600,8 +603,8 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr
XElement para = element.Descendants(W.p).FirstOrDefault();
XElement run = element.Descendants(W.r).FirstOrDefault();

var xPath = (string) element.Attribute(PA.Select);
var optionalString = (string) element.Attribute(PA.Optional);
var xPath = (string)element.Attribute(PA.Select);
var optionalString = (string)element.Attribute(PA.Optional);
bool optional = (optionalString != null && optionalString.ToLower() == "true");

string newValue;
Expand All @@ -618,7 +621,7 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr
{

XElement p = new XElement(W.p, para.Elements(W.pPr));
foreach(string line in newValue.Split('\n'))
foreach (string line in newValue.Split('\n'))
{
p.Add(new XElement(W.r,
para.Elements(W.r).Elements(W.rPr).FirstOrDefault(),
Expand All @@ -630,7 +633,7 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr
else
{
List<XElement> list = new List<XElement>();
foreach(string line in newValue.Split('\n'))
foreach (string line in newValue.Split('\n'))
{
list.Add(new XElement(W.r,
run.Elements().Where(e => e.Name != W.t),
Expand Down Expand Up @@ -692,11 +695,21 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr
}
if (tableData.Count() == 0)
return CreateContextErrorMessage(element, "Table Select returned no data", templateError);



int? headerRowCountAttr = (int?)element.Attribute(PA.HeaderRowCount) ?? 1;
if (headerRowCountAttr.Value < 1)
{
headerRowCountAttr = 1;
}
var headerRowCount = headerRowCountAttr.Value;

XElement table = element.Element(W.tbl);
XElement protoRow = table.Elements(W.tr).Skip(1).FirstOrDefault();
XElement protoRow = table.Elements(W.tr).Skip(headerRowCount).FirstOrDefault();
var footerRowsBeforeTransform = table
.Elements(W.tr)
.Skip(2)
.Skip(headerRowCount + 1)
.ToList();
var footerRows = footerRowsBeforeTransform
.Select(x => ContentReplacementTransform(x, data, templateError))
Expand All @@ -707,7 +720,7 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr
protoRow.Descendants(W.bookmarkEnd).Remove();
XElement newTable = new XElement(W.tbl,
table.Elements().Where(e => e.Name != W.tr),
table.Elements(W.tr).FirstOrDefault(),
table.Elements(W.tr).Take(headerRowCount),
tableData.Select(d =>
new XElement(W.tr,
protoRow.Elements().Where(r => r.Name != W.tc),
Expand Down Expand Up @@ -756,17 +769,17 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr
if (match != null && notMatch != null)
return CreateContextErrorMessage(element, "Conditional: Cannot specify both Match and NotMatch", templateError);

string testValue = null;
string testValue = null;

try
{
testValue = EvaluateXPathToString(data, xPath, false);
}
catch (XPathException e)
catch (XPathException e)
{
return CreateContextErrorMessage(element, e.Message, templateError);
}

if ((match != null && testValue == match) || (notMatch != null && testValue != notMatch))
{
var content = element.Elements().Select(e => ContentReplacementTransform(e, data, templateError));
Expand Down Expand Up @@ -815,14 +828,14 @@ private static XElement CreateParaErrorMessage(string errorMessage, TemplateErro
return errorPara;
}

private static string EvaluateXPathToString(XElement element, string xPath, bool optional )
private static string EvaluateXPathToString(XElement element, string xPath, bool optional)
{
object xPathSelectResult;
try
{
//support some cells in the table may not have an xpath expression.
if (String.IsNullOrWhiteSpace(xPath)) return String.Empty;

xPathSelectResult = element.XPathEvaluate(xPath);
}
catch (XPathException e)
Expand All @@ -832,7 +845,7 @@ private static string EvaluateXPathToString(XElement element, string xPath, bool

if ((xPathSelectResult is IEnumerable) && !(xPathSelectResult is string))
{
var selectedData = ((IEnumerable) xPathSelectResult).Cast<XObject>();
var selectedData = ((IEnumerable)xPathSelectResult).Cast<XObject>();
if (!selectedData.Any())
{
if (optional) return string.Empty;
Expand All @@ -843,11 +856,11 @@ private static string EvaluateXPathToString(XElement element, string xPath, bool
throw new XPathException(string.Format("XPath expression ({0}) returned more than one node", xPath));
}

XObject selectedDatum = selectedData.First();
if (selectedDatum is XElement) return ((XElement) selectedDatum).Value;
XObject selectedDatum = selectedData.First();

if (selectedDatum is XElement) return ((XElement)selectedDatum).Value;

if (selectedDatum is XAttribute) return ((XAttribute) selectedDatum).Value;
if (selectedDatum is XAttribute) return ((XAttribute)selectedDatum).Value;
}

return xPathSelectResult.ToString();
Expand Down
20 changes: 20 additions & 0 deletions OpenXmlPowerTools/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1004:Documentation lines should begin with single space", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1612:Element parameter documentation should match element parameters", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1616:Element return value documentation should have text", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1617:Void return value should not be documented", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1614:Element parameter documentation should have text", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1627:Documentation text should not be empty", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1606:Element documentation should have summary text", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:Documentation text should end with a period", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation should match accessors", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1514:Element documentation header should be preceded by blank line", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1626:Single-line comments should not use documentation style slashes", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1636:File header copyright text should match", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:Constructor summary documentation should begin with standard text", Justification = "<挂起>")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "<挂起>")]
49 changes: 49 additions & 0 deletions OpenXmlPowerTools/Helpers/ImageHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;

namespace OpenXmlPowerTools.Helpers
{
public class ImageHelper
{
public static (int width, int height) GetImageMetrics(Stream imageData, ImageFormat imageFormat)
{
var width = 0;
var height = 0;

if (imageFormat == ImageFormat.Emf)
{
using (var metafile = new Metafile(imageData))
{
width = metafile.Width; // 获取宽度
height = metafile.Height; // 获取高度
}
}
else
{
using (var image = Image.FromStream(imageData))
{
width = image.Width; // 获取宽度
height = image.Height; // 获取高度
}
}

return (width, height);
}

/// <summary>
/// 获取图片的宽和高
/// </summary>
/// <param name="newImagePath"></param>
/// <returns></returns>
public static (int width, int height) GetImageMetrics(string newImagePath, ImageFormat imageFormat)
{
var ext = Path.GetExtension(newImagePath).ToLower();
var stream = File.OpenRead(newImagePath);
return GetImageMetrics(stream, imageFormat);
}
}
}
Loading