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

Feature/copy drawings #1472

Merged
merged 18 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Copy slicers & fixing copy issues.
  • Loading branch information
AdrianEPPlus committed May 20, 2024
commit ca5b923890a28b0c187533e51b882b4f75b7c217
19 changes: 10 additions & 9 deletions src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,8 @@ internal static void CopyControl(ExcelPackage pck, ExcelWorksheet target, ExcelC

var prevRelID = ctrl._control.RelationshipId;
var rel = target.Part.CreateRelationship(UriHelper.GetRelativeUri(target.WorksheetUri, UriCtrl), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/ctrlProp");
XmlAttribute relAtt = target.WorksheetXml.SelectSingleNode(string.Format("//d:control/@r:id[.='{0}']", prevRelID), target.NameSpaceManager) as XmlAttribute;
var relAtts = target.WorksheetXml.SelectNodes(string.Format("//d:control/@r:id[.='{0}']", prevRelID), target.NameSpaceManager);
XmlAttribute relAtt = relAtts.Item(relAtts.Count-1) as XmlAttribute; //target.WorksheetXml.SelectSingleNode(string.Format("//d:control/@r:id[.='{0}']", prevRelID), target.NameSpaceManager) as XmlAttribute;
relAtt.Value = rel.Id;
}

Expand Down Expand Up @@ -1150,21 +1151,21 @@ private static void CopyText(ExcelHeaderFooterText from, ExcelHeaderFooterText t
to.RightAlignedText = from.RightAlignedText;
}

private static void CopySlicers(ExcelWorksheet copy, ExcelWorksheet added)
private static void CopySlicers(ExcelWorksheet source, ExcelWorksheet target)
{
foreach (var source in copy.SlicerXmlSources._list)
foreach (var slicer in source.SlicerXmlSources._list)
{
var id = added.SheetId;
var uri = XmlHelper.GetNewUri(added.Part.Package, "/xl/slicers/slicer{0}.xml", ref id);
var part = added.Part.Package.CreatePart(uri, "application/vnd.ms-excel.slicer+xml", added.Part.Package.Compression);
var rel = added.Part.CreateRelationship(uri, Packaging.TargetMode.Internal, ExcelPackage.schemaRelationshipsSlicer);
var id = target.SheetId;
var uri = XmlHelper.GetNewUri(target.Part.Package, "/xl/slicers/slicer{0}.xml", ref id);
var part = target.Part.Package.CreatePart(uri, "application/vnd.ms-excel.slicer+xml", target.Part.Package.Compression);
var rel = target.Part.CreateRelationship(uri, Packaging.TargetMode.Internal, ExcelPackage.schemaRelationshipsSlicer);
var xml = new XmlDocument();
xml.LoadXml(source.XmlDocument.OuterXml);
xml.LoadXml(slicer.XmlDocument.OuterXml);
var stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write));
xml.Save(stream);

//Now create the new relationship between the worksheet and the slicer.
var relNode = (XmlElement)(added.WorksheetXml.DocumentElement.SelectSingleNode($"d:extLst/d:ext/x14:slicerList/x14:slicer[@r:id='{source.Rel.Id}']", added.NameSpaceManager));
var relNode = (XmlElement)(target.WorksheetXml.DocumentElement.SelectSingleNode($"d:extLst/d:ext/x14:slicerList/x14:slicer[@r:id='{slicer.Rel.Id}']", target.NameSpaceManager));
relNode.Attributes["r:id"].Value = rel.Id;
}
}
Expand Down
94 changes: 83 additions & 11 deletions src/EPPlus/Drawing/ExcelDrawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
using OfficeOpenXml.Core.Worksheet;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Drawing.Chart.ChartEx;
using OfficeOpenXml.Drawing.Chart.Style;
using OfficeOpenXml.Drawing.Controls;
using OfficeOpenXml.Drawing.Interfaces;
using OfficeOpenXml.Drawing.Slicer;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance;
using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using OfficeOpenXml.Packaging;
Expand Down Expand Up @@ -1350,6 +1352,7 @@ public void Copy(ExcelWorksheet worksheet, int row, int col, int rowOffset = 0,
CopyChart(worksheet, drawNode, row, col, rowOffset, colOffset);
return;
case eDrawingType.Slicer:
CopySlicer(worksheet, drawNode, row, col, rowOffset, colOffset);
break;
case eDrawingType.Control:
CopyControl(worksheet, drawNode, row, col, rowOffset, colOffset);
Expand Down Expand Up @@ -1383,39 +1386,109 @@ public void Copy(ExcelWorksheet worksheet, int row, int col, int rowOffset = 0,
}
}

private void CopySlicer(ExcelWorksheet worksheet, XmlElement drawNode, int row, int col, int rowOffset, int colOffset)
{
var slicer = this as ExcelTableSlicer;
//Copy Worksheet Node
((XmlElement)worksheet.TopNode).SetAttribute("xmlns:x14", ExcelPackage.schemaMainX14); //Make sure the namespace exists
var slicerNode = worksheet.CreateNode("d:extLst");
var ogWSNode = _drawings.Worksheet.TopNode.SelectSingleNode("d:extLst", _drawings.Worksheet.NameSpaceManager);
slicerNode.InnerXml = ogWSNode.InnerXml;

////Set Name in drawingXML <xdr:cNvPr id="4" name="Name 1"> <sle:slicer name = "Name 1" />
var drawNameNode = drawNode.SelectSingleNode("mc:AlternateContent/mc:Choice/xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr", worksheet._drawings.NameSpaceManager);
var drawNameNodeVal = drawNameNode.Attributes["name"].Value + " " + worksheet.Workbook._nextDrawingId++;
drawNameNode.Attributes["name"].Value = drawNameNodeVal;
var drawNameNode2 = drawNode.SelectSingleNode("mc:AlternateContent/mc:Choice/xdr:graphicFrame/a:graphic/a:graphicData/sle:slicer", worksheet._drawings.NameSpaceManager);
drawNameNode2.Attributes["name"].Value = drawNameNodeVal;

//Copy Slicer
var sl = _drawings.Worksheet.SlicerXmlSources._list.Find(x => x == slicer._xmlSource);
var id = worksheet.SheetId;
var uri = XmlHelper.GetNewUri(worksheet.Part.Package, "/xl/slicers/slicer{0}.xml", ref id);
var part = worksheet.Part.Package.CreatePart(uri, "application/vnd.ms-excel.slicer+xml", worksheet.Part.Package.Compression);
var rel = worksheet.Part.CreateRelationship(uri, Packaging.TargetMode.Internal, ExcelPackage.schemaRelationshipsSlicer);
var xml = new XmlDocument();
xml.LoadXml(sl.XmlDocument.OuterXml);
//Set name in SlicerXML <slicer name="Name 1"
var slicerNodes = xml.LastChild.ChildNodes;
XmlNode keeper = null;
foreach (XmlNode node in slicerNodes)
{
if (node.Attributes["name"].Value == slicer.Name)
{
node.Attributes["name"].Value = drawNameNodeVal;
keeper = node;
}
}
xml.LastChild.RemoveAll();
xml.LastChild.AppendChild(keeper);
var stream = new StreamWriter(part.GetStream(FileMode.Create, FileAccess.Write));
xml.Save(stream);

//Now create the new relationship between the worksheet and the slicer.
var relNode = (XmlElement)(worksheet.WorksheetXml.DocumentElement.SelectSingleNode($"d:extLst/d:ext/x14:slicerList/x14:slicer[@r:id='{sl.Rel.Id}']", worksheet.NameSpaceManager));
relNode.Attributes["r:id"].Value = rel.Id;
}

private void CopyControl(ExcelWorksheet worksheet, XmlNode drawNode, int row, int col, int rowOffset, int colOffset)
{
var control = this as ExcelControl; //<- allt finns h�r typ i _control
var control = this as ExcelControl;
var controlId = worksheet._nextControlId++.ToString();

//Update DrawNode Id
var drawIdNode = drawNode.SelectSingleNode("xdr:sp/xdr:nvSpPr/xdr:cNvPr", worksheet.NameSpaceManager);
drawIdNode.Attributes["id"].Value = controlId;
var drawSpIdNode = drawIdNode.SelectSingleNode("a:extLst/a:ext/a14:compatExt", worksheet.NameSpaceManager);
drawSpIdNode.Attributes["spid"].Value = "_x0000_s" + controlId;
var drawSpIdNode = drawIdNode.SelectSingleNode("a:extLst/a:ext/a14:compatExt", _drawings.NameSpaceManager);
var spid = drawSpIdNode.Attributes["spid"].Value = "_x0000_s" + controlId;

//Create worksheet node
XmlNode ctrlNode = worksheet.CreateControlContainerNode();
((XmlElement)worksheet.TopNode).SetAttribute("xmlns:xdr", ExcelPackage.schemaSheetDrawings); //Make sure the namespace exists
((XmlElement)worksheet.TopNode).SetAttribute("xmlns:x14", ExcelPackage.schemaMainX14); //Make sure the namespace exists
((XmlElement)worksheet.TopNode).SetAttribute("xmlns:mc", ExcelPackage.schemaMarkupCompatibility); //Make sure the namespace exists
ctrlNode.InnerXml = control._control.TopNode.ParentNode.InnerXml; //control.GetControlStartWorksheetXml(control._control.RelationshipId);
ctrlNode.InnerXml = control._control.TopNode.ParentNode.InnerXml;
ctrlNode.FirstChild.Attributes["shapeId"].Value = controlId;
WorksheetCopyHelper.CopyControl(worksheet._package, worksheet, control);

//Create vml
//Create spid
worksheet.VmlDrawings.AddControl(control, control.LegacySpId);
worksheet.VmlDrawings.AddControl(control, spid);
var vmlId = worksheet.VmlDrawings._drawings[worksheet.VmlDrawings._drawings.Count - 1].TopNode;
vmlId.Attributes["spid"].Value = "_x0000_s" + controlId;
vmlId.Attributes["spid"].Value = spid;

//Create the copy
var copy = GetDrawing(worksheet._drawings, drawNode);
worksheet._drawings.AddDrawingInternal(copy);
copy.EditAs = ExcelControl.GetControlEditAs(control.ControlType);
var width = GetPixelWidth();
var height = GetPixelHeight();
copy.SetPixelWidth(width);
copy.SetPixelHeight(height);
copy.SetPosition(row, rowOffset, col, colOffset);
worksheet._drawings.AddDrawingInternal(copy);

//Update position in worksheet xml
var fromCol = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:from/xdr:col", worksheet.NameSpaceManager);
var fromColOff = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:from/xdr:colOff", worksheet.NameSpaceManager);
var fromRow = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:from/xdr:row", worksheet.NameSpaceManager);
var fromRowOff = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:from/xdr:rowOff", worksheet.NameSpaceManager);
fromCol.InnerText = col.ToString();
fromColOff.InnerText = colOffset.ToString();
fromRow.InnerText = row.ToString();
fromRowOff.InnerText = rowOffset.ToString();
var toCol = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:to/xdr:col", worksheet.NameSpaceManager);
var toColOff = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:to/xdr:colOff", worksheet.NameSpaceManager);
var toRow = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:to/xdr:row", worksheet.NameSpaceManager);
var toRowOff = ctrlNode.SelectSingleNode("d:control/d:controlPr/d:anchor/d:to/xdr:rowOff", worksheet.NameSpaceManager);
toCol.InnerText = copy.To.Column.ToString();
toColOff.InnerText = copy.To.ColumnOff.ToString();
toRow.InnerText = copy.To.Row.ToString();
toRowOff.InnerText = copy.To.RowOff.ToString();

//Update position in drawing vml
var vmlPosition = vmlId.SelectSingleNode("x:ClientData/x:Anchor", worksheet._vmlDrawings.NameSpaceManager);
vmlPosition.InnerXml = copy.From.Column + ", " + copy.From.ColumnOff + ", " + copy.From.Row + ", " + copy.From.RowOff + ", " +
copy.To.Column + ", " + copy.To.ColumnOff + ", " + copy.To.Row + ", " + copy.To.RowOff;

}

private void CopyChart(ExcelWorksheet worksheet, XmlNode drawNode, int row, int col, int rowOffset, int colOffset)
Expand Down Expand Up @@ -1460,9 +1533,8 @@ private void CopyRelations(ExcelWorksheet worksheet, XmlElement drawNode)
rel.TargetUri = imageInfo.Uri;
}
}

var exisistingRel = worksheet._drawings.Part.GetRelationshipsByType(rel.RelationshipType).Where(x=> x.Target == rel.Target).FirstOrDefault();
if(exisistingRel == null)
var exisistingRel = worksheet._drawings.Part.GetRelationshipsByType(rel.RelationshipType).Where(x => x.Target == rel.Target).FirstOrDefault();
if (exisistingRel == null || worksheet != _drawings.Worksheet)
{
var newRel = worksheet._drawings.Part.CreateRelationshipFromCopy(rel);
relNode.Value = newRel.Id;
Expand Down
117 changes: 106 additions & 11 deletions src/EPPlusTest/Drawing/CopyDrawingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,34 @@ namespace EPPlusTest.Drawing
[TestClass]
public class CopyDrawingTests : TestBase
{
//Copy Shape Tests
[TestMethod]
public void CopyShapeTest()
public void CopyShapeSameWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws0 = p.Workbook.Worksheets[0];
ws0.Drawings[0].Copy(ws0, 25, 1);
SaveAndCleanup(p);
}
[TestMethod]
public void CopyShapeOtherWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws0 = p.Workbook.Worksheets[0];
var ws1 = p.Workbook.Worksheets[1];
ws0.Drawings[0].Copy(ws1, 10, 10);
SaveAndCleanup(p);
}

[TestMethod]
public void CopyShapeOtherWorkbookTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws0 = p.Workbook.Worksheets[0];
using var p2 = OpenPackage("Target.xlsx", true);
var ws1 = p2.Workbook.Worksheets.Add("Sheet1");
ws0.Drawings[0].Copy(ws1, 10, 10);
SaveAndCleanup(p2);
}
[TestMethod]
public void CopyShapeBlipFillTest()
{
Expand All @@ -29,42 +47,117 @@ public void CopyShapeBlipFillTest()
SaveAndCleanup(p);
}

//Copy Picture Tests
[TestMethod]
public void CopyPictureSameWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws1 = p.Workbook.Worksheets[1];
ws1.Drawings[0].Copy(ws1, 0, 15);
SaveAndCleanup(p);
}
[TestMethod]
public void CopyPictureTest()
public void CopyPictureOtherWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws0 = p.Workbook.Worksheets[0];
var ws1 = p.Workbook.Worksheets[1];
ws1.Drawings[0].Copy(ws0, 20, 1);
SaveAndCleanup(p);
}

[TestMethod]
public void CopyPictureTestExternal()
public void CopyPictureOtherWorkbookTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws0 = p.Workbook.Worksheets[1];
using var p2 = OpenPackage("Target.xlsx", true);
var ws1 = p2.Workbook.Worksheets.Add("Sheet1");
ws0.Drawings[0].Copy(ws1, 20, 1);
ws0.Drawings[0].Copy(ws1, 20, 10);
ws0.Drawings[0].Copy(ws1, 1, 1);
SaveAndCleanup(p2);
}

//Copy Control Tests
[TestMethod]
public void CopyControlTest()
public void CopyControlSameWorksheetTest() //Fungerar ej. Flyttar orginalet
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws1 = p.Workbook.Worksheets[1];
ws1.Drawings[1].Copy(ws1, 25, 20);
SaveAndCleanup(p);
}
[TestMethod]
public void CopyControlOtherWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws2 = p.Workbook.Worksheets[2];
var ws1 = p.Workbook.Worksheets[1];
ws1.Drawings[1].Copy(ws2, 20, 1);
//ws1.Drawings[2].Copy(ws2, 40, 1);
//ws1.Drawings[1].Copy(ws2, 50, 1);
ws1.Drawings[2].Copy(ws2, 40, 1);
ws1.Drawings[1].Copy(ws2, 50, 1);
SaveAndCleanup(p);
}
[TestMethod]
public void CopyControlOtherWorkbookTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws1 = p.Workbook.Worksheets[1];
using var p2 = OpenPackage("Target.xlsx", true);
var ws2 = p2.Workbook.Worksheets.Add("Sheet1");
ws1.Drawings[1].Copy(ws2, 20, 1);
ws1.Drawings[2].Copy(ws2, 40, 1);
ws1.Drawings[1].Copy(ws2, 50, 1);
SaveAndCleanup(p2);
}

//Copy Slicer Tests
[TestMethod]
public void CopySlicerSameWorksheetTest() //Fungerar ej
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws1 = p.Workbook.Worksheets[0];
ws1.Drawings[2].Copy(ws1, 1, 25, 0, 0);
SaveAndCleanup(p);
}
[TestMethod]
public void CopySlicerOtherWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws1 = p.Workbook.Worksheets[0];
var ws3 = p.Workbook.Worksheets[2];
ws1.Drawings[2].Copy(ws3, 1, 15, 0, 0);
SaveAndCleanup(p);
}
[TestMethod]
public void CopySlicerOtherWorkbookTest() //Fungerar ej
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var ws1 = p.Workbook.Worksheets[0];
using var p2 = OpenPackage("Target.xlsx", true);
var ws2 = p2.Workbook.Worksheets.Add("Sheet1");
ws1.Drawings[2].Copy(ws2, 1, 15, 0, 0);
SaveAndCleanup(p2);
}

//Copy Chart Tests
[TestMethod]
public void CopyChartSameWorksheetTest() //Fungerar ej
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var sourceWs = p.Workbook.Worksheets[2];
sourceWs.Drawings[0].Copy(sourceWs, 20, 1);
SaveAndCleanup(p);
}
[TestMethod]
public void CopyChartTestExternal()
public void CopyChartOtherWorksheetTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var sourceWs = p.Workbook.Worksheets[2];
var ws1 = p.Workbook.Worksheets[1];
sourceWs.Drawings[0].Copy(ws1, 20, 20);
SaveAndCleanup(p);
}
[TestMethod]
public void CopyChartOtherWorkbookTest()
{
using var p = OpenTemplatePackage("CopyDrawings.xlsx");
var sourceWs = p.Workbook.Worksheets[2];
Expand All @@ -73,5 +166,7 @@ public void CopyChartTestExternal()
sourceWs.Drawings[0].Copy(targetWs, 20, 1);
SaveAndCleanup(p2);
}

//Copy Group Shape Tests
}
}