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
Next Next commit
copyDrawings progress
  • Loading branch information
AdrianEPPlus committed May 10, 2024
commit eb66df50c1341b8edcb856eb4ec23593439eba25
115 changes: 62 additions & 53 deletions src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,61 +373,11 @@ private static void CopyDrawingRels(ExcelDrawing copyDraw, ExcelPackage pck, Exc
var uriDraw = partDraw.Uri;
if (copyDraw is ExcelChart chart)
{
var xml = chart.ChartXml.InnerXml;
Uri uriChart;
ZipPackagePart chartPart;
if (chart._isChartEx)
{
uriChart = XmlHelper.GetNewUri(pck.ZipPackage, "/xl/charts/chartEx{0}.xml");
chartPart = pck.ZipPackage.CreatePart(uriChart, ContentTypes.contentTypeChartEx, pck.Compression);
}
else
{
uriChart = XmlHelper.GetNewUri(pck.ZipPackage, "/xl/charts/chart{0}.xml");
chartPart = pck.ZipPackage.CreatePart(uriChart, ContentTypes.contentTypeChart, pck.Compression);
}
StreamWriter streamChart = new StreamWriter(chartPart.GetStream(FileMode.Create, FileAccess.Write));
streamChart.Write(xml);
streamChart.Flush();
//Now create the new relationship to the copied chart xml
XmlNode relNode;
if (chart._isChartEx)
{
relNode = copyDraw.TopNode.SelectSingleNode("mc:AlternateContent/mc:Choice[@Requires='cx1' or @Requires='cx2']/xdr:graphicFrame/a:graphic/a:graphicData/cx:chart/@r:id", copy.Drawings.NameSpaceManager);
string prevRelID = relNode?.Value;
var rel = partDraw.CreateRelationship(UriHelper.GetRelativeUri(uriDraw, uriChart), Packaging.TargetMode.Internal, ExcelPackage.schemaChartExRelationships);
XmlAttribute relAtt = drawXml.SelectSingleNode(string.Format("//cx:chart/@r:id[.='{0}']", prevRelID), copy.Drawings.NameSpaceManager) as XmlAttribute;
relAtt.Value = rel.Id;
}
else
{
relNode = copyDraw.TopNode.SelectSingleNode("xdr:graphicFrame/a:graphic/a:graphicData/c:chart/@r:id", copy.Drawings.NameSpaceManager);
string prevRelID = relNode?.Value;
var rel = partDraw.CreateRelationship(UriHelper.GetRelativeUri(uriDraw, uriChart), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/chart");
XmlAttribute relAtt = drawXml.SelectSingleNode(string.Format("//c:chart/@r:id[.='{0}']", prevRelID), copy.Drawings.NameSpaceManager) as XmlAttribute;
relAtt.Value = rel.Id;
}

CopyChartRelations(copy, added, chart, chartPart);
CopyChartRelations(copyDraw, pck, added, partDraw, drawXml, copy, uriDraw, chart);
}
else if (copyDraw is ExcelPicture pic)
{
IPictureContainer container = pic;
var uri = container.UriPic;
var ii = added.Workbook._package.PictureStore.AddImage(pic.Image.ImageBytes, null, pic.Image.Type);

var rel = partDraw.CreateRelationship(UriHelper.GetRelativeUri(added.WorksheetUri, ii.Uri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/image");
//Fixes problem with invalid image when the same image is used more than once.
XmlNode relAtt =
drawXml.SelectSingleNode(
string.Format(
"//xdr:pic/xdr:nvPicPr/xdr:cNvPr/@name[.='{0}']/../../../xdr:blipFill/a:blip/@r:embed",
pic.Name), copy.Drawings.NameSpaceManager);

if (relAtt != null)
{
relAtt.Value = rel.Id;
}
CopyPicture(added, partDraw, drawXml, copy, pic);
}
else if (copyDraw is ExcelControl ctrl)
{
Expand Down Expand Up @@ -478,7 +428,66 @@ private static void CopyDrawingRels(ExcelDrawing copyDraw, ExcelPackage pck, Exc
}
}

private static void CopyChartRelations(ExcelWorksheet copy, ExcelWorksheet added, ExcelChart chart, ZipPackagePart chartPart)
internal static void CopyChartRelations(ExcelDrawing copyDraw, ExcelPackage pck, ExcelWorksheet added, ZipPackagePart partDraw, XmlDocument drawXml, ExcelWorksheet copy, Uri uriDraw, ExcelChart chart)
{
var xml = chart.ChartXml.InnerXml;
Uri uriChart;
ZipPackagePart chartPart;
if (chart._isChartEx)
{
uriChart = XmlHelper.GetNewUri(pck.ZipPackage, "/xl/charts/chartEx{0}.xml");
chartPart = pck.ZipPackage.CreatePart(uriChart, ContentTypes.contentTypeChartEx, pck.Compression);
}
else
{
uriChart = XmlHelper.GetNewUri(pck.ZipPackage, "/xl/charts/chart{0}.xml");
chartPart = pck.ZipPackage.CreatePart(uriChart, ContentTypes.contentTypeChart, pck.Compression);
}
StreamWriter streamChart = new StreamWriter(chartPart.GetStream(FileMode.Create, FileAccess.Write));
streamChart.Write(xml);
streamChart.Flush();
//Now create the new relationship to the copied chart xml
XmlNode relNode;
if (chart._isChartEx)
{
relNode = copyDraw.TopNode.SelectSingleNode("mc:AlternateContent/mc:Choice[@Requires='cx1' or @Requires='cx2']/xdr:graphicFrame/a:graphic/a:graphicData/cx:chart/@r:id", copy.Drawings.NameSpaceManager);
string prevRelID = relNode?.Value;
var rel = partDraw.CreateRelationship(UriHelper.GetRelativeUri(uriDraw, uriChart), Packaging.TargetMode.Internal, ExcelPackage.schemaChartExRelationships);
XmlAttribute relAtt = drawXml.SelectSingleNode(string.Format("//cx:chart/@r:id[.='{0}']", prevRelID), copy.Drawings.NameSpaceManager) as XmlAttribute;
relAtt.Value = rel.Id;
}
else
{
relNode = copyDraw.TopNode.SelectSingleNode("xdr:graphicFrame/a:graphic/a:graphicData/c:chart/@r:id", copy.Drawings.NameSpaceManager);
string prevRelID = relNode?.Value;
var rel = partDraw.CreateRelationship(UriHelper.GetRelativeUri(uriDraw, uriChart), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/chart");
XmlAttribute relAtt = drawXml.SelectSingleNode(string.Format("//c:chart/@r:id[.='{0}']", prevRelID), copy.Drawings.NameSpaceManager) as XmlAttribute;
relAtt.Value = rel.Id;
}
CopyChartRelations(copy, added, chart, chartPart);
}

internal static void CopyPicture(ExcelWorksheet added, ZipPackagePart partDraw, XmlDocument drawXml, ExcelWorksheet copy, ExcelPicture pic)
{
IPictureContainer container = pic;
var uri = container.UriPic;
var ii = added.Workbook._package.PictureStore.AddImage(pic.Image.ImageBytes, null, pic.Image.Type);

var rel = partDraw.CreateRelationship(UriHelper.GetRelativeUri(added.WorksheetUri, ii.Uri), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/image");
//Fixes problem with invalid image when the same image is used more than once.
XmlNode relAtt =
drawXml.SelectSingleNode(
string.Format(
"//xdr:pic/xdr:nvPicPr/xdr:cNvPr/@name[.='{0}']/../../../xdr:blipFill/a:blip/@r:embed",
pic.Name), copy.Drawings.NameSpaceManager);

if (relAtt != null)
{
relAtt.Value = rel.Id;
}
}

internal static void CopyChartRelations(ExcelWorksheet copy, ExcelWorksheet added, ExcelChart chart, ZipPackagePart chartPart)
{
foreach (var relCopy in chart.Part.GetRelationships())
{
Expand Down
130 changes: 130 additions & 0 deletions src/EPPlus/Drawing/ExcelDrawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using OfficeOpenXml.Core.CellStore;
using OfficeOpenXml.Core.Worksheet;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Drawing.Chart.ChartEx;
using OfficeOpenXml.Drawing.Controls;
using OfficeOpenXml.Drawing.Interfaces;
using OfficeOpenXml.Drawing.Slicer;
using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;
using OfficeOpenXml.Packaging;
using OfficeOpenXml.Packaging.Ionic;
using OfficeOpenXml.Style.XmlAccess;
Expand Down Expand Up @@ -1324,5 +1328,131 @@ internal XmlElement CreateClientData()
TopNode.AppendChild(clientDataNode);
return clientDataNode;
}


public void Copy(ExcelWorksheet worksheet, int row, int col, int rowOffset = 0, int colOffset = 0)
{
var drawNode = worksheet.Drawings.CreateDocumentAndTopNode(CellAnchor, false);
drawNode.InnerXml = TopNode.InnerXml;
//create relation
if(worksheet._drawings!=_drawings)
{
switch (DrawingType)
{
case eDrawingType.Shape:
//Check blip and stuff
break;
case eDrawingType.Picture:
CopyRelations(worksheet, drawNode);
break;
case eDrawingType.Chart:
CopyChart(worksheet, drawNode, row, col, rowOffset, colOffset);
return;
}
}
var copy = GetDrawing(worksheet._drawings, drawNode);
worksheet.Drawings.AddDrawingInternal(copy);
var width = GetPixelWidth();
var height = GetPixelHeight();
copy.SetPosition(row, rowOffset, col, colOffset);
copy.SetPixelWidth(width);
copy.SetPixelHeight(height);
copy.GetPositionSize();
switch (DrawingType)
{
case eDrawingType.Shape:
copy._id = worksheet.Workbook._nextDrawingId++;
break;
case eDrawingType.Picture:
var pic = copy as ExcelPicture;
pic.SetNewId(worksheet.Workbook._nextDrawingId++);
break;
case eDrawingType.Chart:

break;
}
}

private void CopyChart(ExcelWorksheet worksheet, XmlElement drawNode, int row, int col, int rowOffset, int colOffset)
{
var relNode = drawNode.SelectSingleNode("xdr:graphicFrame/a:graphic/a:graphicData/c:chart/@r:id", NameSpaceManager);
var origialChart = this as ExcelChart;

if (relNode != null && _drawings.Part.RelationshipExists(relNode.Value))
{

var rel = _drawings.Part.GetRelationship(relNode.Value);
var uri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
//if (worksheet.Workbook != _drawings.Worksheet.Workbook)
//{
var chartPart = _drawings.Worksheet.Workbook._package.ZipPackage.GetPart(uri);
//CopyChartRelations(ExcelDrawing copyDraw, ExcelPackage pck, ExcelWorksheet added, ZipPackagePart partDraw, XmlDocument drawXml, ExcelWorksheet copy, Uri uriDraw, ExcelChart chart
WorksheetCopyHelper.CopyChartRelations(this, worksheet._package, _drawings.Worksheet, chartPart, drawNode.OwnerDocument, worksheet, uri, origialChart);
//var chartStream = (MemoryStream)chartPart.GetStream(FileMode.Open, FileAccess.Read);

//var chartBytes = new byte[chartStream.Length];
//chartStream.Seek(0, SeekOrigin.Begin);
//chartStream.Read(chartBytes, 0, (int)chartStream.Length);
//var copyPart = worksheet.Workbook._package.ZipPackage.CreatePart(uri, chartPart.ContentType);
//var copyStream = (MemoryStream)copyPart.GetStream(FileMode.Create, FileAccess.Write);
//copyStream.Write(chartBytes, 0, chartBytes.Length);
//var newRel = worksheet._drawings.Part.CreateRelationshipFromCopy(rel);
//relNode.Value = newRel.Id;
////}
var uri2 = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
var part = _drawings.Worksheet.Workbook._package.ZipPackage.GetPart(uri2);
var chart = ExcelChart.CreateChartFromXml(worksheet.Drawings, drawNode, uri, worksheet._package.ZipPackage.GetPart(uri), drawNode.OwnerDocument);
chart.Name = "THIS IS HCART";
chart.SetPosition(row, rowOffset, col, colOffset);
worksheet.Drawings._drawingsList.Add(chart);
worksheet.Drawings._drawingNames.Add(chart.Name, worksheet.Drawings._drawingsList.Count - 1);
}
}

private void CopyRelations(ExcelWorksheet worksheet, XmlElement drawNode)
{
var relNode = drawNode.SelectSingleNode("xdr:pic/xdr:blipFill/a:blip/@r:embed", NameSpaceManager);
if(relNode != null && _drawings.Part.RelationshipExists(relNode.Value))
{
var rel = _drawings.Part.GetRelationship(relNode.Value);

if (worksheet.Workbook != _drawings.Worksheet.Workbook)
{
var uri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
var imagePart = _drawings.Worksheet.Workbook._package.ZipPackage.GetPart(uri);
var imageStream = (MemoryStream)imagePart.GetStream(FileMode.Open, FileAccess.Read);
var image = new byte[imageStream.Length];
imageStream.Seek(0, SeekOrigin.Begin);
imageStream.Read(image, 0, (int)imageStream.Length);
var imageInfo = worksheet.Workbook._package.PictureStore.GetImageInfo(image);
if (imageInfo == null)
{
var copyPart = worksheet.Workbook._package.ZipPackage.CreatePart(uri, imagePart.ContentType);
var copyStream = (MemoryStream)copyPart.GetStream(FileMode.Create, FileAccess.Write);
copyStream.Write(image, 0, image.Length);
}
else
{
rel.TargetUri = imageInfo.Uri;
}
}

var exisistingRel = worksheet._drawings.Part.GetRelationshipsByType(rel.RelationshipType).Where(x=> x.Target == rel.Target).FirstOrDefault();
if(exisistingRel == null)
{
var newRel = worksheet._drawings.Part.CreateRelationshipFromCopy(rel);
relNode.Value = newRel.Id;
}
else
{
relNode.Value = exisistingRel.Id;
}
}
}

public void Copy(ExcelRangeBase range, int rowOffset = 0, int colOffset = 0)
{

}
}
}
Loading