Skip to content

Commit 5358567

Browse files
authored
Fixed part already exists and rId errors when copying pictures between workbooks (#1846)
* fix for i1841 * In-depth tests + extras * Fixed issues with rId when copying back * Moved tests to copy drawing file
1 parent e1289f7 commit 5358567

File tree

4 files changed

+285
-22
lines changed

4 files changed

+285
-22
lines changed

src/EPPlus/Drawing/ExcelDrawing.cs

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,73 +1686,100 @@ private XmlNode CopyChart(ExcelWorksheet worksheet, bool isGroupShape = false, X
16861686
return drawNode;
16871687
}
16881688

1689-
private XmlNode CopyPicture(ExcelWorksheet worksheet, bool isGroupShape = false, XmlNode groupDrawNode = null)
1689+
private XmlNode CopyPicture(ExcelWorksheet targetWorksheet, bool isGroupShape = false, XmlNode groupDrawNode = null)
16901690
{
16911691
XmlNode drawNode = null;
1692+
1693+
var targetWorkbook = targetWorksheet.Workbook;
1694+
var targetPackage = targetWorkbook._package;
1695+
16921696
if (isGroupShape && groupDrawNode != null)
16931697
{
16941698
drawNode = groupDrawNode;
1695-
groupDrawNode.SelectSingleNode("xdr:nvPicPr/xdr:cNvPr", worksheet._drawings.NameSpaceManager).Attributes["id"].Value = (++worksheet.Workbook._nextDrawingId).ToString();
1699+
groupDrawNode.SelectSingleNode("xdr:nvPicPr/xdr:cNvPr", targetWorksheet._drawings.NameSpaceManager).Attributes["id"].Value = (++targetWorkbook._nextDrawingId).ToString();
16961700
}
16971701
else
16981702
{
16991703
//Create node in drawing.xml
1700-
drawNode = worksheet.Drawings.CreateDocumentAndTopNode(CellAnchor, false);
1704+
drawNode = targetWorksheet.Drawings.CreateDocumentAndTopNode(CellAnchor, false);
17011705
drawNode.InnerXml = TopNode.InnerXml;
17021706
}
17031707
//If same drawings object, we are done.
1704-
if (worksheet._drawings != _drawings)
1708+
if (targetWorksheet._drawings != _drawings)
17051709
{
17061710
//Get the relation node
17071711
var relNode = drawNode.SelectSingleNode("xdr:pic/xdr:blipFill/a:blip/@r:embed", NameSpaceManager);
17081712
if(relNode == null)
17091713
{
17101714
relNode = drawNode.SelectSingleNode("xdr:blipFill/a:blip/@r:embed", NameSpaceManager);
17111715
}
1716+
17121717
if (relNode != null && _drawings.Part.RelationshipExists(relNode.Value))
17131718
{
1714-
var rel = _drawings.Part.GetRelationship(relNode.Value);
1719+
var srcsRel = _drawings.Part.GetRelationship(relNode.Value);
1720+
ZipPackageRelationship newRel = null;
1721+
bool imageExists = false;
1722+
17151723
//Copy image file to new workbook if target worksheet is in a different workbook.
1716-
if (worksheet.Workbook != _drawings.Worksheet.Workbook)
1724+
if (targetWorkbook != _drawings.Worksheet.Workbook)
17171725
{
1718-
var uri = UriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
1726+
var uri = UriHelper.ResolvePartUri(srcsRel.SourceUri, srcsRel.TargetUri);
17191727
var imagePart = _drawings.Worksheet.Workbook._package.ZipPackage.GetPart(uri);
1728+
17201729
var imageStream = (MemoryStream)imagePart.GetStream(FileMode.Open, FileAccess.Read);
17211730
var image = new byte[imageStream.Length];
1731+
17221732
imageStream.Seek(0, SeekOrigin.Begin);
17231733
imageStream.Read(image, 0, (int)imageStream.Length);
1724-
var imageInfo = worksheet.Workbook._package.PictureStore.GetImageInfo(image);
1734+
1735+
var imageInfo = targetPackage.PictureStore.GetImageInfo(image);
1736+
17251737
if (imageInfo == null)
17261738
{
1727-
var copyPart = worksheet.Workbook._package.ZipPackage.CreatePart(uri, imagePart.ContentType);
1739+
var info = new FileInfo(uri.OriginalString);
1740+
Uri absUri = GetNewUri(targetPackage.ZipPackage, "/xl/media/image{0}" + info.Extension);
1741+
1742+
newRel = targetWorksheet._drawings.Part.CreateRelationshipFromCopy(srcsRel);
1743+
1744+
var relativeUri = UriHelper.GetRelativeUri(newRel.SourceUri, absUri);
1745+
newRel.TargetUri = relativeUri;
1746+
1747+
var copyPart = targetPackage.ZipPackage.CreatePart(absUri, imagePart.ContentType);
17281748
var copyStream = (MemoryStream)copyPart.GetStream(FileMode.Create, FileAccess.Write);
17291749
copyStream.Write(image, 0, image.Length);
1750+
1751+
relNode.Value = newRel.Id;
17301752
}
17311753
else
17321754
{
1733-
rel.TargetUri = imageInfo.Uri;
1755+
var relativeUri = UriHelper.GetRelativeUri(srcsRel.SourceUri, imageInfo.Uri);
1756+
var exisistingRel = targetWorksheet._drawings.Part.GetRelationshipsByType(srcsRel.RelationshipType).Where(x => x.TargetUri == relativeUri).FirstOrDefault();
1757+
relNode.Value = exisistingRel.Id;
17341758
}
17351759
}
1736-
//Check if relationship exists.
1737-
var exisistingRel = worksheet._drawings.Part.GetRelationshipsByType(rel.RelationshipType).Where(x => x.Target == rel.Target).FirstOrDefault();
1738-
//Create new relation id if no relation exsist or if it's a different worksheet. Otherwise asign the exsisting relationship Id
1739-
if (exisistingRel == null || worksheet != _drawings.Worksheet)
1740-
{
1741-
var newRel = worksheet._drawings.Part.CreateRelationshipFromCopy(rel);
1742-
relNode.Value = newRel.Id;
1743-
}
17441760
else
17451761
{
1746-
relNode.Value = exisistingRel.Id;
1762+
//Check if relationship exists.
1763+
var exisistingRel = targetWorksheet._drawings.Part.GetRelationshipsByType(srcsRel.RelationshipType).Where(x => x.TargetUri == srcsRel.TargetUri).FirstOrDefault();
1764+
//Create new relation id if no relation exsist or if it's a different worksheet. Otherwise asign the existing relationship Id
1765+
if (exisistingRel == null || targetWorksheet != _drawings.Worksheet)
1766+
{
1767+
newRel = targetWorksheet._drawings.Part.CreateRelationshipFromCopy(srcsRel);
1768+
relNode.Value = newRel.Id;
1769+
}
1770+
else
1771+
{
1772+
relNode.Value = exisistingRel.Id;
1773+
}
17471774
}
17481775
}
17491776
}
17501777
if (!isGroupShape)
17511778
{
17521779
//Set New id on copied picture.
1753-
var pic = GetDrawing(worksheet._drawings, drawNode) as ExcelPicture;
1754-
pic.SetNewId(++worksheet.Workbook._nextDrawingId);
1755-
pic.Name = worksheet._drawings.GetUniqueDrawingName(this.Name);
1780+
var pic = GetDrawing(targetWorksheet._drawings, drawNode) as ExcelPicture;
1781+
pic.SetNewId(++targetWorkbook._nextDrawingId);
1782+
pic.Name = targetWorksheet._drawings.GetUniqueDrawingName(this.Name);
17561783
}
17571784
return drawNode;
17581785
}

src/EPPlus/Drawing/ExcelPicture.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Date Author Change
2727

2828
#if NETFULL
2929
using System.Drawing.Imaging;
30+
using System.Xml.Linq;
31+
3032
#endif
3133
#if !NET35 && !NET40
3234
using System.Threading.Tasks;
@@ -133,6 +135,13 @@ private void Init()
133135
_verticalFlipPath = string.Format(_verticalFlipPath, _topPath);
134136
}
135137

138+
internal string GetRelId()
139+
{
140+
XmlElement blip = (XmlElement)TopNode.SelectSingleNode($"{_topPath}xdr:blipFill/a:blip", NameSpaceManager);
141+
142+
return blip.GetAttribute("embed", ExcelPackage.schemaRelationships);
143+
}
144+
136145
internal void SetRelId(XmlNode node, ePictureType type, string relID, string attribute = "embed")
137146
{
138147
XmlElement blip = (XmlElement)node.SelectSingleNode($"{_topPath}xdr:blipFill/a:blip", NameSpaceManager);

src/EPPlusTest/Drawing/CopyDrawingTests.cs

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,5 +443,229 @@ public void ReadAndCopyTwoAnchorImage()
443443
SaveAndCleanup(package);
444444
}
445445
}
446+
447+
[TestMethod]
448+
public void CopyImagesBetweenTwo_Unsaved_Workbooks()
449+
{
450+
string sheetName = "AWs";
451+
string wbName = "CopyPicture_PictureObject";
452+
453+
using (var src = OpenPackage($"{wbName}1.xlsx", true))
454+
{
455+
var wbFirst = src.Workbook;
456+
var wsFirst = wbFirst.Worksheets.Add(sheetName);
457+
458+
var pic1 = wsFirst.Drawings.AddPicture("epplusPicture", GetResourceFile("EPPlus.png"));
459+
460+
using (var target = OpenPackage($"{wbName}2.xlsx", true))
461+
{
462+
var wbSecond = target.Workbook;
463+
var wsSecond = wbSecond.Worksheets.Add(sheetName);
464+
465+
var pic2 = wsSecond.Drawings.AddPicture("screenshotPicture", GetResourceFile("screenshot.PNG"));
466+
pic1.Copy(wsSecond, 1, 1, 0, 0);
467+
pic2.Copy(wsFirst, 1, 1, 0, 0);
468+
469+
Assert.AreEqual(2, wsSecond.Drawings.Count);
470+
Assert.AreEqual(2, wsFirst.Drawings.Count);
471+
472+
Assert.AreEqual("epplusPicture", wsSecond.Drawings[1].Name);
473+
Assert.AreEqual("screenshotPicture", wsFirst.Drawings[1].Name);
474+
475+
//Check that we don't create new rels as image already exists in target
476+
Assert.AreEqual(2, wsFirst.Drawings.Part._rels.Count);
477+
Assert.AreEqual(2, wsSecond.Drawings.Part._rels.Count);
478+
479+
SaveAndCleanup(target);
480+
}
481+
SaveAndCleanup(src);
482+
}
483+
}
484+
485+
[TestMethod]
486+
public void CopyImagesBetweenTwo_UnsavedWorkbooks_NamedRanges()
487+
{
488+
string sheetName = "AWs";
489+
string rangeName = "SomeName";
490+
string rangeAddress = "A1:G9";
491+
string wbName = "CopyPicture_NamedRanges";
492+
493+
using (var src = OpenPackage($"{wbName}1.xlsx", true))
494+
{
495+
var wbFirst = src.Workbook;
496+
var wsFirst = wbFirst.Worksheets.Add(sheetName);
497+
498+
var pic1 = wsFirst.Drawings.AddPicture("epplusPicture", GetResourceFile("EPPlus.png"));
499+
500+
wsFirst.Names.AddName(rangeName, wsFirst.Cells[rangeAddress]);
501+
502+
using (var target = OpenPackage($"{wbName}2.xlsx", true))
503+
{
504+
var wbSecond = target.Workbook;
505+
var wsSecond = wbSecond.Worksheets.Add(sheetName);
506+
507+
var pic2 = wsSecond.Drawings.AddPicture("screenshotPicture", GetResourceFile("screenshot.PNG"));
508+
wsSecond.Names.AddName(rangeName, wsSecond.Cells[rangeAddress]);
509+
510+
CopyImagesBetweenWorkbooks(src, target, sheetName, rangeName);
511+
512+
//ensure "normal" copying works
513+
Assert.AreEqual(2, wsSecond.Drawings.Count);
514+
Assert.AreEqual("screenshotPicture", wsSecond.Drawings[0].Name);
515+
Assert.AreEqual("epplusPicture", wsSecond.Drawings[1].Name);
516+
517+
CopyImagesBetweenWorkbooks(target, src, sheetName, rangeName);
518+
519+
//Ensure nothing's changed in the workbook we are copying FROM when copying back
520+
Assert.AreEqual(2, wsSecond.Drawings.Count);
521+
Assert.AreEqual("screenshotPicture", wsSecond.Drawings[0].Name);
522+
Assert.AreEqual("epplusPicture", wsSecond.Drawings[1].Name);
523+
524+
//Copies the copy therefore 3
525+
Assert.AreEqual(3, wsFirst.Drawings.Count);
526+
Assert.AreEqual("epplusPicture", wsFirst.Drawings[0].Name);
527+
Assert.AreEqual("screenshotPicture", wsFirst.Drawings[1].Name);
528+
Assert.AreEqual("epplusPicture1", wsFirst.Drawings[2].Name);
529+
530+
//Check that we don't create new rels as image already exists in target
531+
Assert.AreEqual(2, wsFirst.Drawings.Part._rels.Count);
532+
Assert.AreEqual(2, wsSecond.Drawings.Part._rels.Count);
533+
534+
//Ensure no missmatch of ids
535+
var relIdOriginal = wsFirst.Drawings[0].As.Picture.GetRelId();
536+
var relIdCopiedBack = wsFirst.Drawings[2].As.Picture.GetRelId();
537+
538+
Assert.AreEqual(relIdOriginal, relIdCopiedBack);
539+
Assert.AreEqual("rId1", relIdOriginal);
540+
Assert.AreEqual("rId2", wsFirst.Drawings[1].As.Picture.GetRelId());
541+
542+
SaveAndCleanup(target);
543+
}
544+
SaveAndCleanup(src);
545+
}
546+
}
547+
548+
[TestMethod]
549+
public void CopyImagesBetweenTwo_Saved_Workbooks_NamedRanges()
550+
{
551+
string sheetName = "AWs";
552+
string rangeName = "SomeName";
553+
string rangeAddress = "A1:G9";
554+
string wbName = "CopyPictureRead_NamedRanges";
555+
556+
//Create the workbooks
557+
using (var src = OpenPackage($"{wbName}1.xlsx", true))
558+
{
559+
var wbFirst = src.Workbook;
560+
var wsFirst = wbFirst.Worksheets.Add(sheetName);
561+
562+
var pic1 = wsFirst.Drawings.AddPicture("epplusPicture", GetResourceFile("EPPlus.png"));
563+
564+
wsFirst.Names.AddName(rangeName, wsFirst.Cells[rangeAddress]);
565+
566+
SaveAndCleanup(src);
567+
}
568+
using (var target = OpenPackage($"{wbName}2.xlsx", true))
569+
{
570+
var wbSecond = target.Workbook;
571+
var wsSecond = wbSecond.Worksheets.Add(sheetName);
572+
573+
var pic2 = wsSecond.Drawings.AddPicture("screenshotPicture", GetResourceFile("screenshot.PNG"));
574+
wsSecond.Names.AddName(rangeName, wsSecond.Cells[rangeAddress]);
575+
576+
SaveAndCleanup(target);
577+
}
578+
579+
//Read and copy images between the workbooks
580+
using (var src = OpenPackage($"{wbName}1.xlsx"))
581+
{
582+
var wbFirst = src.Workbook;
583+
var wsFirst = wbFirst.Worksheets.First();
584+
585+
using (var target = OpenPackage($"{wbName}2.xlsx"))
586+
{
587+
var wbSecond = target.Workbook;
588+
var wsSecond = wbSecond.Worksheets.First();
589+
590+
Assert.AreEqual(1, wsSecond.Drawings.Count);
591+
Assert.AreEqual(1, wsFirst.Drawings.Count);
592+
593+
CopyImagesBetweenWorkbooks(src, target, sheetName, rangeName);
594+
CopyImagesBetweenWorkbooks(target, src, sheetName, rangeName);
595+
596+
Assert.AreEqual(2, wsSecond.Drawings.Count);
597+
//Copies the copy therefore 3
598+
Assert.AreEqual(3, wsFirst.Drawings.Count);
599+
600+
Assert.AreEqual("epplusPicture", wsSecond.Drawings[1].Name);
601+
Assert.AreEqual("screenshotPicture", wsFirst.Drawings[1].Name);
602+
603+
//Assert.AreEqual("1", wsFirst.Drawings[2].As.Picture.Part._rels["0"].Id);
604+
605+
var outputName = GetOutputFile("", "copy_" + target.File.Name).FullName;
606+
target.SaveAs(outputName);
607+
}
608+
var outputName2 = GetOutputFile("", "copy_" + src.File.Name).FullName;
609+
src.SaveAs(outputName2);
610+
}
611+
}
612+
613+
[TestMethod]
614+
public void CopyImages_Read_EnsureWorkaround()
615+
{
616+
using (var src = OpenTemplatePackage("ImageInRange1.xlsx"))
617+
{
618+
var wb = src.Workbook;
619+
using (var target = OpenTemplatePackage("ImageInRange2.xlsx"))
620+
{
621+
var wb2 = target.Workbook;
622+
623+
CopyNamedRangeToTargetSheetWB(src, target, "sheet1", "SomeName");
624+
625+
var outputName = GetOutputFile("", "alt_" + target.File.Name).FullName;
626+
target.SaveAs(outputName);
627+
}
628+
var outputName2 = GetOutputFile("", "alt_" + src.File.Name).FullName;
629+
src.SaveAs(outputName2);
630+
}
631+
}
632+
633+
[TestMethod]
634+
public void CopyImages_Read()
635+
{
636+
using (var src = OpenTemplatePackage("ImageInRange1.xlsx"))
637+
{
638+
var wb = src.Workbook;
639+
using (var target = OpenTemplatePackage("ImageInRange2.xlsx"))
640+
{
641+
var wb2 = target.Workbook;
642+
643+
CopyImagesBetweenWorkbooks(src, target, "sheet1", "SomeName");
644+
645+
SaveAndCleanup(target);
646+
}
647+
SaveAndCleanup(src);
648+
}
649+
}
650+
651+
//From i1841 / s814
652+
public void CopyImagesBetweenWorkbooks(ExcelPackage src, ExcelPackage target, string sheetName, string namedRange)
653+
{
654+
var range = src.Workbook.Worksheets[sheetName].Names[namedRange];
655+
var targetRange = target.Workbook.Worksheets[sheetName].Names[namedRange];
656+
range.Copy(targetRange);
657+
}
658+
659+
public void CopyNamedRangeToTargetSheetWB(ExcelPackage src, ExcelPackage target, string sheetName, string namedRange)
660+
{
661+
var srcWs = src.Workbook.Worksheets[sheetName];
662+
var tmpWs = target.Workbook.Worksheets.Add("tmpCopy", srcWs);
663+
664+
var range = tmpWs.Names[namedRange];
665+
var targetRange = target.Workbook.Worksheets[sheetName].Names[namedRange];
666+
range.Copy(targetRange);
667+
668+
target.Workbook.Worksheets.Delete(tmpWs);
669+
}
446670
}
447671
}

src/EPPlusTest/Drawing/DrawingTest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ Date Author Change
3939
using System.Diagnostics;
4040
using System.Reflection;
4141
using OfficeOpenXml.Drawing.Theme;
42+
using System.Linq;
43+
using System.Collections.Generic;
44+
using System.Collections;
4245

4346
namespace EPPlusTest
4447
{

0 commit comments

Comments
 (0)